865 lines
43 KiB
HTML
865 lines
43 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta
|
||
name="viewport"
|
||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||
<meta
|
||
http-equiv="X-UA-Compatible"
|
||
content="ie=edge">
|
||
<meta
|
||
name="theme-color"
|
||
content="#fff"
|
||
id="theme-color">
|
||
<meta
|
||
name="description"
|
||
content="AKLabs">
|
||
<link
|
||
rel="icon"
|
||
href="/">
|
||
<title>Devlog Entry</title>
|
||
|
||
|
||
<meta
|
||
property="og:title"
|
||
content="Devlog Entry">
|
||
|
||
|
||
<meta
|
||
property="og:url"
|
||
content="https://aklabs.net/2026/05/15/Devlog-20260518/index.html">
|
||
|
||
|
||
<meta
|
||
property="og:img"
|
||
content="/images/akesterson.webp">
|
||
|
||
|
||
|
||
<meta
|
||
property="og:type"
|
||
content="article">
|
||
<meta
|
||
property="og:article:published_time"
|
||
content="2026-05-15">
|
||
<meta
|
||
property="og:article:modified_time"
|
||
content="2026-05-21">
|
||
<meta
|
||
property="og:article:author"
|
||
content="Andrew Kesterson">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="preload" href="//at.alicdn.com/t/font_1946621_i1kgafibvw.css" as="style" >
|
||
<link rel="preload" href="//at.alicdn.com/t/font_1952792_89b4ac4k4up.css" as="style" >
|
||
<link rel="preload" href="/css/main.css" as="style" >
|
||
|
||
<link rel="modulepreload" href="//instant.page/5.1.0">
|
||
|
||
<link rel="stylesheet" href="/css/main.css">
|
||
|
||
<link rel="stylesheet" href="//at.alicdn.com/t/font_1946621_i1kgafibvw.css">
|
||
|
||
<link rel="stylesheet" href="//at.alicdn.com/t/font_1952792_89b4ac4k4up.css">
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="/js/lib/lightbox/baguetteBox.min.css">
|
||
|
||
<script>
|
||
function loadScript(url, cb) {
|
||
var script = document.createElement('script');
|
||
script.src = url;
|
||
if (cb) script.onload = cb;
|
||
script.async = true;
|
||
document.body.appendChild(script);
|
||
}
|
||
function loadCSS(href, data, attr) {
|
||
var sheet = document.createElement('link');
|
||
sheet.ref = 'stylesheet';
|
||
sheet.href = href;
|
||
sheet.dataset[data] = attr;
|
||
document.head.appendChild(sheet);
|
||
}
|
||
function changeCSS(cssFile, data, attr) {
|
||
var oldlink = document.querySelector(data);
|
||
var newlink = document.createElement("link");
|
||
newlink.setAttribute("rel", "stylesheet");
|
||
newlink.setAttribute("href", cssFile);
|
||
newlink.dataset.prism = attr;
|
||
document.head.replaceChild(newlink, oldlink);
|
||
}
|
||
</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script>
|
||
function prismThemeChange() {
|
||
if(document.getElementById('theme-color').dataset.mode === 'dark') {
|
||
if(document.querySelector('[data-prism]')) {
|
||
changeCSS('/js/lib/prism/prism-tomorrow.min.css', '[data-prism]', 'prism-tomorrow');
|
||
} else {
|
||
loadCSS('/js/lib/prism/prism-tomorrow.min.css', 'prism', 'prism-tomorrow');
|
||
}
|
||
} else {
|
||
if(document.querySelector('[data-prism]')) {
|
||
changeCSS('/js/lib/prism/prism-defauult.min.css', '[data-prism]', 'prism-defauult');
|
||
} else {
|
||
loadCSS('/js/lib/prism/prism-defauult.min.css', 'prism', 'prism-defauult');
|
||
}
|
||
}
|
||
}
|
||
prismThemeChange()
|
||
</script>
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="/js/lib/prism/prism-line-numbers.min.css">
|
||
|
||
|
||
|
||
<script>
|
||
// control reverse button
|
||
var reverseDarkList = {
|
||
dark: 'light',
|
||
light: 'dark'
|
||
};
|
||
var themeColor = {
|
||
dark: '#1c1c1e',
|
||
light: '#fff'
|
||
}
|
||
// get the data of css prefers-color-scheme
|
||
var getCssMediaQuery = function() {
|
||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||
};
|
||
// reverse current darkmode setting function
|
||
var reverseDarkModeSetting = function() {
|
||
var setting = localStorage.getItem('user-color-scheme');
|
||
if(reverseDarkList[setting]) {
|
||
setting = reverseDarkList[setting];
|
||
} else if(setting === null) {
|
||
setting = reverseDarkList[getCssMediaQuery()];
|
||
} else {
|
||
return;
|
||
}
|
||
localStorage.setItem('user-color-scheme', setting);
|
||
return setting;
|
||
};
|
||
// apply current darkmode setting
|
||
</script>
|
||
|
||
<script>
|
||
var setDarkmode = function(mode) {
|
||
var setting = mode || localStorage.getItem('user-color-scheme');
|
||
if(setting === getCssMediaQuery()) {
|
||
document.documentElement.removeAttribute('data-user-color-scheme');
|
||
localStorage.removeItem('user-color-scheme');
|
||
document.getElementById('theme-color').content = themeColor[setting];
|
||
document.getElementById('theme-color').dataset.mode = setting;
|
||
prismThemeChange();
|
||
} else if(reverseDarkList[setting]) {
|
||
document.documentElement.setAttribute('data-user-color-scheme', setting);
|
||
document.getElementById('theme-color').content = themeColor[setting];
|
||
document.getElementById('theme-color').dataset.mode = setting;
|
||
prismThemeChange();
|
||
} else {
|
||
document.documentElement.removeAttribute('data-user-color-scheme');
|
||
localStorage.removeItem('user-color-scheme');
|
||
document.getElementById('theme-color').content = themeColor[getCssMediaQuery()];
|
||
document.getElementById('theme-color').dataset.mode = getCssMediaQuery();
|
||
prismThemeChange();
|
||
}
|
||
};
|
||
setDarkmode();
|
||
</script>
|
||
|
||
|
||
|
||
<link rel="preload" href="/js/lib/lightbox/baguetteBox.min.js" as="script">
|
||
<link rel="preload" href="/js/lib/lightbox/baguetteBox.min.css" as="style" >
|
||
|
||
|
||
<link rel="preload" href="/js/lib/lozad.min.js" as="script">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<meta name="generator" content="Hexo 6.0.0"><link rel="alternate" href="/atom.xml" title="AKLabs" type="application/atom+xml">
|
||
</head>
|
||
|
||
<body>
|
||
<div class="wrapper">
|
||
|
||
<nav class="navbar">
|
||
<div class="navbar-logo">
|
||
<a class="navbar-logo-main" href="/">
|
||
|
||
<span class="navbar-logo-dsc">AKLabs</span>
|
||
</a>
|
||
</div>
|
||
<div class="navbar-menu">
|
||
|
||
<a
|
||
href="/now"
|
||
class="navbar-menu-item">
|
||
|
||
~/.plan
|
||
|
||
</a>
|
||
|
||
<a
|
||
href="/archives"
|
||
class="navbar-menu-item">
|
||
|
||
Archive
|
||
|
||
</a>
|
||
|
||
<a
|
||
href="/categories"
|
||
class="navbar-menu-item">
|
||
|
||
Categories
|
||
|
||
</a>
|
||
|
||
<a
|
||
href="/about"
|
||
class="navbar-menu-item">
|
||
|
||
About
|
||
|
||
</a>
|
||
|
||
<a
|
||
href="/contact"
|
||
class="navbar-menu-item">
|
||
|
||
Contact
|
||
|
||
</a>
|
||
|
||
<button
|
||
class="navbar-menu-item darknavbar navbar-menu-btn"
|
||
aria-label="Toggle dark mode"
|
||
id="dark">
|
||
<i class="iconfont icon-weather"></i>
|
||
</button>
|
||
<button
|
||
class="navbar-menu-item searchnavbar navbar-menu-btn"
|
||
aria-label="Toggle search"
|
||
id="search">
|
||
<!-- <i
|
||
class="iconfont icon-search"
|
||
style="font-size: 1.2rem; font-weight: 400;">
|
||
</i> -->
|
||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img"
|
||
class="iconify iconify--ion" width="28" height="28" preserveAspectRatio="xMidYMid meet" viewBox="0 0 512 512">
|
||
<path fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="28"
|
||
d="M256 80a176 176 0 1 0 176 176A176 176 0 0 0 256 80Z"></path>
|
||
<path fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="28"
|
||
d="M232 160a72 72 0 1 0 72 72a72 72 0 0 0-72-72Z"></path>
|
||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="28"
|
||
d="M283.64 283.64L336 336"></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</nav>
|
||
|
||
<div
|
||
id="local-search"
|
||
style="display: none">
|
||
<input
|
||
class="navbar-menu-item"
|
||
id="search-input"
|
||
placeholder="请输入搜索内容..." />
|
||
<div id="search-content"></div>
|
||
</div>
|
||
|
||
<div class="section-wrap">
|
||
<div class="container">
|
||
<div class="columns">
|
||
<aside class="left-column">
|
||
|
||
<div class="card card-author">
|
||
|
||
<img
|
||
src="/images/akesterson.webp"
|
||
class="author-img"
|
||
width="88"
|
||
height="88"
|
||
alt="author avatar">
|
||
|
||
<p class="author-name">Andrew Kesterson</p>
|
||
<p class="author-description"><center><i>"Love God. Live Righteously. Die Well."</i> <br/> <br/> <a target="_blank" rel="noopener" href="https://source.starfort.tech/andrew">Source Code</a> || <a target="_blank" rel="noopener" href="https://www.linkedin.com/in/andrewkesterson/">LinkedIn</a> <br/> </center></p>
|
||
<div class="author-message">
|
||
<a
|
||
class="author-posts-count"
|
||
href="/archives">
|
||
<span>29</span>
|
||
<span>Posts</span>
|
||
</a>
|
||
<a
|
||
class="author-categories-count"
|
||
href="/categories">
|
||
<span>9</span>
|
||
<span>Categories</span>
|
||
</a>
|
||
<a
|
||
class="author-tags-count"
|
||
href="/tags">
|
||
<span>0</span>
|
||
<span>Tags</span>
|
||
</a>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="sticky-tablet">
|
||
|
||
|
||
<article class="display-when-two-columns spacer">
|
||
<div class="card card-content toc-card">
|
||
<div class="toc-header">
|
||
<i
|
||
class="iconfont icon-menu"
|
||
style="padding-right: 2px;">
|
||
</i>TOC
|
||
</div>
|
||
<ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Goodbye-GitHub"><span class="toc-text">Goodbye GitHub</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#libakerror-improvements"><span class="toc-text">libakerror improvements</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#libakstdlib"><span class="toc-text">libakstdlib</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#libakgl"><span class="toc-text">libakgl</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#akgltest"><span class="toc-text">akgltest</span></a></li></ol>
|
||
</div>
|
||
</article>
|
||
|
||
|
||
<article class="card card-content categories-widget">
|
||
<div class="categories-card">
|
||
<div class="categories-header">
|
||
<i
|
||
class="iconfont icon-fenlei"
|
||
style="padding-right: 2px;">
|
||
</i>Categories
|
||
</div>
|
||
<div class="categories-list">
|
||
|
||
<a href="/categories/Books/">
|
||
<div class="categories-list-item">
|
||
Books
|
||
<span class="categories-list-item-badge">14</span>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/categories/Faith/">
|
||
<div class="categories-list-item">
|
||
Faith
|
||
<span class="categories-list-item-badge">6</span>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/categories/Outdoors/">
|
||
<div class="categories-list-item">
|
||
Outdoors
|
||
<span class="categories-list-item-badge">1</span>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/categories/Philosophy/">
|
||
<div class="categories-list-item">
|
||
Philosophy
|
||
<span class="categories-list-item-badge">5</span>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/categories/Leadership/">
|
||
<div class="categories-list-item">
|
||
Leadership
|
||
<span class="categories-list-item-badge">8</span>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/categories/History/">
|
||
<div class="categories-list-item">
|
||
History
|
||
<span class="categories-list-item-badge">1</span>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/categories/Liberal-Education/">
|
||
<div class="categories-list-item">
|
||
Liberal-Education
|
||
<span class="categories-list-item-badge">1</span>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/categories/Technology/">
|
||
<div class="categories-list-item">
|
||
Technology
|
||
<span class="categories-list-item-badge">11</span>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/categories/Current-Events/">
|
||
<div class="categories-list-item">
|
||
Current-Events
|
||
<span class="categories-list-item-badge">5</span>
|
||
</div>
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="card card-content tags-widget">
|
||
<div class="tags-card">
|
||
<div class="tags-header">
|
||
<i
|
||
class="iconfont icon-biaoqian"
|
||
style="padding-right: 2px;">
|
||
</i>hot tags
|
||
</div>
|
||
<div class="tags-list">
|
||
|
||
</div>
|
||
</div>
|
||
</article>
|
||
|
||
|
||
</div>
|
||
</aside>
|
||
<main class="main-column">
|
||
|
||
<article class="card card-content">
|
||
<header>
|
||
<h1 class="post-title">
|
||
Devlog Entry
|
||
</h1>
|
||
</header>
|
||
<div class="post-meta post-show-meta">
|
||
<time datetime="2026-05-16T02:04:50.000Z">
|
||
<i
|
||
class="iconfont icon-calendar"
|
||
style="margin-right: 2px;">
|
||
</i>
|
||
<span>2026-05-15</span>
|
||
</time>
|
||
|
||
<span class="dot"></span>
|
||
|
||
<a
|
||
href="/categories/Technology/"
|
||
class="post-meta-link">
|
||
Technology
|
||
</a>
|
||
|
||
|
||
|
||
<span class="dot"></span>
|
||
<span>2k words</span>
|
||
|
||
</div>
|
||
|
||
</header>
|
||
<div
|
||
id="section"
|
||
class="post-content">
|
||
<p>I just wanted to drop some notes on things I’ve been hacking on recently.</p>
|
||
<h1 id="Goodbye-GitHub"><a href="#Goodbye-GitHub" class="headerlink" title="Goodbye GitHub"></a>Goodbye GitHub</h1><p>I’ve become increasingly unhappy with Github for a variety of reasons. For that reason, I’ve moved all of my source code over to <a target="_blank" rel="noopener" href="https://source.starfort.tech/">the Starfort Source Vault</a>. My GitHub will now only host stuff that I do for my corporate job. I’ve also moved my personal website off of GitHub Pages and back in to Starfort’s local infrastructure. This is a control issue: I want to retain full control over my intellectual property and the way my online presence is presented. I’m also not interested in Microsoft scraping all of my source code to train their AI models, so I’m taking my marbles and going home.</p>
|
||
<h1 id="libakerror-improvements"><a href="#libakerror-improvements" class="headerlink" title="libakerror improvements"></a>libakerror improvements</h1><p><a target="_blank" rel="noopener" href="https://source.starfort.tech/andrew/libakerror">libakerror</a> is a safe exception handling library for C. I’m baking it into all of my new projects that I’m writing in C, so it’s getting worked out quite a bit.</p>
|
||
<ul>
|
||
<li><p>Began namespacing the library into <code>akerr_</code>. All of the functions, types and global variables are in this namespace, but the macros are not. I’m not sure I like them not being namespaced, but I also don’t want to make them any longer to type. I may wind up leaving the macros.</p>
|
||
</li>
|
||
<li><p>I found myself using large <code>ATTEMPT ... FINISH</code> blocks that would <code>CATCH</code> something but never actually do anything with the thing it caught. This was a lot of unnecessary typing. I added a new macro, <code>PASS</code>, that simply passes an error back up through the stack without actually doing anything to it. Saves a lot of typing, and is what I actually want to do probably >80% of the time (in my current projects anyway).</p>
|
||
</li>
|
||
</ul>
|
||
<pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token function">PREPARE_ERROR</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
<span class="token function">PASS</span><span class="token punctuation">(</span>e<span class="token punctuation">,</span> <span class="token function">some_method_that_returns_errorcontext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
<span class="token function">SUCCEED_RETURN</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>
|
||
|
||
<ul>
|
||
<li>Properly handling functions that don’t SUCCEED properly. Previously there was nothing stopping you from doing this:</li>
|
||
</ul>
|
||
<pre class="line-numbers language-c" data-language="c"><code class="language-c">akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">func</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span>
|
||
<span class="token punctuation">{</span>
|
||
<span class="token function">PREPARE_ERROR</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span>
|
||
<span class="token comment">// do something </span>
|
||
<span class="token comment">// don't return (e), OR, return(e) without calling SUCCEED</span>
|
||
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
|
||
|
||
<p>This results in your function having an undefined (and invalid) pointer return value. When the DETECT() macro (invoked by CATCH(), FAIL_*(), or PASS()) inspected the value of your return function, it would see a non-NULL value, and then go about inspecting the datastructure. Unfortunately, in these careless cases, the pointer was invalid, so BAM segfault.</p>
|
||
<p>I introduced a new macro, VALID(), that first checks to see if the return value from the function is a valid exception - that’s an easy check, since all exceptions come from the global AKERR_ARRAY_ERROR which contains a known quantity of error objects. By checking to see if the akerr_ErrorContext pointer address is within the range of memory occupied by the array, we can be reasonably certain it is (or is not) a valid error. Invalid error return values get an <code>AKERR_BEHAVIOR</code> error thrown by the library.</p>
|
||
<ul>
|
||
<li>Documentation update to reflect the above changes</li>
|
||
</ul>
|
||
<h1 id="libakstdlib"><a href="#libakstdlib" class="headerlink" title="libakstdlib"></a>libakstdlib</h1><p><a target="_blank" rel="noopener" href="https://source.starfort.tech/andrew/libakstdlib">libakstdlib</a> is a libc wrapper that implements <code>libakerror</code> error handling around those functions.</p>
|
||
<p>I find myself wrapping various libc/stdlib calls in <code>libakerror</code> macros frequently, so I’ve started a libc wrapper that does that for me. It doesn’t have much in it yet, just the functions I use the most:</p>
|
||
<pre class="line-numbers language-c" data-language="c"><code class="language-c">akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_fopen</span><span class="token punctuation">(</span><span class="token keyword">char</span> <span class="token operator">*</span>pathname<span class="token punctuation">,</span> <span class="token keyword">char</span> <span class="token operator">*</span>mode<span class="token punctuation">,</span> FILE <span class="token operator">*</span><span class="token operator">*</span>fp<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_fread</span><span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span>ptr<span class="token punctuation">,</span> <span class="token class-name">size_t</span> size<span class="token punctuation">,</span> <span class="token class-name">size_t</span> nmemb<span class="token punctuation">,</span> FILE <span class="token operator">*</span>stream<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_fwrite</span><span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span>ptr<span class="token punctuation">,</span> <span class="token class-name">size_t</span> size<span class="token punctuation">,</span> <span class="token class-name">size_t</span> nmemb<span class="token punctuation">,</span> FILE <span class="token operator">*</span>fp<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_fclose</span><span class="token punctuation">(</span>FILE <span class="token operator">*</span>stream<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_malloc</span><span class="token punctuation">(</span><span class="token class-name">size_t</span> size<span class="token punctuation">,</span> <span class="token keyword">void</span> <span class="token operator">*</span><span class="token operator">*</span>dst<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_memset</span><span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span>s<span class="token punctuation">,</span> <span class="token keyword">int</span> c<span class="token punctuation">,</span> <span class="token class-name">size_t</span> n<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_memcpy</span><span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span>d<span class="token punctuation">,</span> <span class="token keyword">void</span> <span class="token operator">*</span>s<span class="token punctuation">,</span> <span class="token class-name">size_t</span> n<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_free</span><span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span>ptr<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_printf</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">*</span>count<span class="token punctuation">,</span> <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>restrict format<span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_fprintf</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">*</span>count<span class="token punctuation">,</span> FILE <span class="token operator">*</span>restrict stream<span class="token punctuation">,</span> <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>restrict format<span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_sprintf</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">*</span>count<span class="token punctuation">,</span> <span class="token keyword">char</span> <span class="token operator">*</span>restrict str<span class="token punctuation">,</span> <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>restrict format<span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_atoi</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>nptr<span class="token punctuation">,</span> <span class="token keyword">int</span> <span class="token operator">*</span>dest<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_atol</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>nptr<span class="token punctuation">,</span> <span class="token keyword">long</span> <span class="token operator">*</span>dest<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
akerr_ErrorContext AKERR_NOIGNORE <span class="token operator">*</span><span class="token function">aksl_atoll</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>nptr<span class="token punctuation">,</span> <span class="token keyword">long</span> <span class="token keyword">long</span> <span class="token operator">*</span>dest<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
|
||
|
||
<p>Most libc functions set <code>errno</code> when they fail (but not all), so most failure operations from these wrappers will throw the value of <code>errno</code> as their exception. I need to explicitly document where these functions throw something else. The functions are pretty easy drop-in replacements for their libc counterparts, with the exception that they no longer return values, they return error state. So you have to pass additional pointer arguments for them to place their outputs.</p>
|
||
<pre class="line-numbers language-none"><code class="language-none">FILE *fp = NULL;
|
||
char membuf[512];
|
||
PREPARE_ERROR(e);
|
||
PASS(e, fopen("somefile.txt", "rb", &fp);
|
||
PASS(e, fread((void *)&membuf, 1, 512, fp));
|
||
SUCCEED_RETURN(e);<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
|
||
|
||
<h1 id="libakgl"><a href="#libakgl" class="headerlink" title="libakgl"></a>libakgl</h1><p><a target="_blank" rel="noopener" href="https://source.starfort.tech/andrew/libakgl">libakgl</a> is an SDL3 game programming library.</p>
|
||
<p>With the release of SDL version 3 last year, I’ve started working on a new (?) C game library (not an engine) built around SDL3 as a fun side project.</p>
|
||
<p>I say “new” in quotes because it is very much a spiritual successor to <a target="_blank" rel="noopener" href="https://source.starfort.tech/andrew/libsdlgame">a defunct SDL2 game library I wrote over a decade ago</a>. That engine was largely an ode to (and a grieving process for) the Adobe Flash player as a game platform. The terminology I used there was heavily influenced by Flash, and that terminology lives on in the new <code>libakgl</code>. However <code>libakgl</code> is C++ and used a lot of standard template library types and operations, very closely mimicking the code flow from ActionScript; <code>libakgl</code> is pure C and therefore looks and flows a bit different. Regardless, my work on this library really represents a rebellion against complexity and vendor lock-in.</p>
|
||
<p>First, in my day job, I spend a ton of time working with infinite layers of complexity in the DevSecOps space without ever actually touching much real code at all. There’s barely even a computer involved - it’s more like I work with several dozen <em>abstractions</em> of a computer. </p>
|
||
<p>Second, in the game space, everything has moved towards complex game engines using higher level languages to accomplish anything. Tools like Unity or Unreal Engine, even open source tools like Godot or Phaser, I personally find that they <em>feel</em> too complex. Given that my fondest game dev memories are from the Flash/ActionScript days, I do wonder a little bit about this feeling - in terms of user experience, tools like GameMaker or Phaser are much closer to Flash than <code>libakgl</code>. But they all require me to learn their interface, their quirks, and to confine whatever idea I have into their box. Maybe complexity is the wrong word, but when I look at the majority of modern engines, I wind up thinking, “I don’t want to deal with all that complexity”.</p>
|
||
<p>Third, this library is 100% pure C (I’ve <em>tried</em> to keep it C99, but I make no promises, and there are some GNU-isms in there that may not work right outside of GCC). Writing the old <code>libsdlgame</code> in C++ was fun, the STL felt like it was making things faster, but it eventually became a total shitshow. I’m sure some of that was my own fault, but some of it was because the STL is notoriously slow. Then there’s the fact that I was doing tons and tons of dynamic memory management in the old version - there are all kinds of dangers associated with that. There were also many pitfalls in the extensive usage of C++’s object oriented model. Here’s a great example from the old codebase that probably says more about my skill as a programmer at that time than it does about the pitfalls of C++’s object oriented model :</p>
|
||
<pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">// THE PROJECTILE/CHARACTER SUBCLASSING IS FUCKED. THE ORDER OF THE 'damage' ELEMENT AND OTHER </span>
|
||
<span class="token comment">// ITEMS CAUSES UNEXPECTED RESULTS AS ITEMS SUCH AS PROJECTILES ARE TYPECAST AS A (Character *)</span>
|
||
<span class="token comment">// AND PASSED AROUND THRU COLLISION FUNCTIONS. CHANGE THE ORDER THAT THE 'damage' ELEMENT IS DEFINED</span>
|
||
<span class="token comment">// IN THE CHARACTER CLASS AND YOU'LL SEE THE LASERS START DOING WIERD SHIT AS A RESULT.</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
|
||
|
||
<p>… so in this version of the library, we are abandoning as much complexity as possible. Specifically:</p>
|
||
<ol>
|
||
<li>No dynamic memory allocation. Everything comes from static arrays of objects defined at compile time. SDL still performs some dynamic memory allocation, but that will be converted to use a custom arena memory allocator.</li>
|
||
<li>Everything is a fatty struct (in the style of the old Quake games, and with a nod to <a target="_blank" rel="noopener" href="https://computerenhance.com/">Casey Muratori</a>)</li>
|
||
<li>Entity behavior can be controlled through setting function pointers on the fatty structs, but there is no inheritance, RTTI or dynamic dispatch</li>
|
||
</ol>
|
||
<p>Last, I’m building this library because I don’t like being locked in to any particular engine. Godot, Unreal, Unity, GameMaker, RPG Maker, they’re all fine tools. But they all have their own ecosystems, their own expectations, and their own hard limits. Sometimes I might need to change engines to execute one idea versus another one. By just rolling my own library, I gain lots of flexibility. And it’s all my code, so I have nobody to blame but myself when something breaks - and I also own 100% of the opportunity to make it better.</p>
|
||
<p>Last, I’m building this project with cmake, as opposed to the Makefiles I’m so used to. I find that cmake is actually making it really easy to string projects together as dependencies - for example I have a test program for this library which is able to use git submodules to keep all its dependencies together in a monorepo, and cmake does a great job of building and linking everything, without me having to keep track of versions installed on my system.</p>
|
||
<p>Right now the library can:</p>
|
||
<ul>
|
||
<li>Load and animate sprites from JSON files describing a spritesheet reference and how to extract frames from the spritesheet</li>
|
||
<li>Load characters from JSON files that describe a collection of sprites that map to a given set of state flags (for example “use sprite ‘walking left’ when actor has state ‘ACTOR_STATE_MOVELEFT’)</li>
|
||
<li>Create actors on the stage, assign them a character, and ensure the correct sprite is shown animating for the correct state</li>
|
||
<li>Load and display orthogonal tilemaps (as exported by <a target="_blank" rel="noopener" href="https://www.mapeditor.org/">Tiled</a>). This includes:<ul>
|
||
<li>Image layers</li>
|
||
<li>Object layers<ul>
|
||
<li>Object layers can spawn actors at a given point on the map, each being displayed with a given character and starting with a given state</li>
|
||
</ul>
|
||
</li>
|
||
<li>Tile layers<ul>
|
||
<li>Actors have a layer ID on the screen, so actors can be drawn between tile layers for depth</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>Manage input from the keyboard or gamepads<ul>
|
||
<li>Control maps connect a given Actor object to a given C function via a given SDL input event</li>
|
||
<li>Multiple control maps can be defined, so that you can easily swap out control schemes (for example use different control schemes for your menu screen, your inventory screen, and your main gameplay screen)</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>Some of the library has unit tests, which is nice, but it’s hard to conceptualize how to test some of what it does. That might just be me being lazy.</p>
|
||
<p>There’s a lot more to be done (see <a target="_blank" rel="noopener" href="https://source.starfort.tech/andrew/libakgl/src/branch/main/TODO.txt">the TODO file</a> for my current shortlist of deliverables), but it’s a fun project. I’ll probably start making real things with it soon.</p>
|
||
<h1 id="akgltest"><a href="#akgltest" class="headerlink" title="akgltest"></a>akgltest</h1><p><a target="_blank" rel="noopener" href="https://source.starfort.tech/andrew/akgl-test">akgl-test</a> is just a testbed for libakgl. It illustrates:</p>
|
||
<ul>
|
||
<li>Loading assets<ul>
|
||
<li>Sprites from JSON</li>
|
||
<li>Characters from JSON</li>
|
||
<li>Tilemaps</li>
|
||
</ul>
|
||
</li>
|
||
<li>Setting up control maps<ul>
|
||
<li>ESC sends you back to the main menu</li>
|
||
<li>M toggles music</li>
|
||
<li>UP moves a character up the screen with a walking animation</li>
|
||
<li>DOWN moves a character down the screen with a walking animation</li>
|
||
<li>LEFT moves a character to the left with a walking animation</li>
|
||
<li>RIGHT moves a character to the right with a walking animation</li>
|
||
</ul>
|
||
</li>
|
||
<li>Changing through various game states (a loading screen, menu screen, and gameplay)</li>
|
||
<li>Loading and changing tilemaps<ul>
|
||
<li>Creating actors on the screen based off the tilemap</li>
|
||
<li>Using forced perspective on an image map to create the illusion of depth when using 2D sprites on a prerendered 3D background image</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>Once I get collision detection working in <code>libakgl</code> I’ll start producing small games with it. I think I’ll start with a breakout clone.</p>
|
||
|
||
</div>
|
||
<div>
|
||
|
||
</div>
|
||
</article>
|
||
<div class="nav">
|
||
|
||
|
||
<div class="nav-item-next">
|
||
<a
|
||
href="/2026/02/23/Why-Firefighting/"
|
||
class="nav-link">
|
||
<div>
|
||
<div class="nav-label">Next</div>
|
||
|
||
<div class="nav-title">Why Firefighting </div>
|
||
|
||
</div>
|
||
<i class="iconfont icon-right nav-next-icon"></i>
|
||
</a>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div
|
||
class="card card-content toc-card"
|
||
id="mobiletoc">
|
||
<div class="toc-header">
|
||
<i
|
||
class="iconfont icon-menu"
|
||
style="padding-right: 2px;">
|
||
</i>TOC
|
||
</div>
|
||
<ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Goodbye-GitHub"><span class="toc-text">Goodbye GitHub</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#libakerror-improvements"><span class="toc-text">libakerror improvements</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#libakstdlib"><span class="toc-text">libakstdlib</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#libakgl"><span class="toc-text">libakgl</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#akgltest"><span class="toc-text">akgltest</span></a></li></ol>
|
||
</div>
|
||
|
||
</main>
|
||
<aside class="right-column">
|
||
<div class="sticky-widescreen">
|
||
|
||
|
||
<article class="card card-content toc-card">
|
||
<div class="toc-header">
|
||
<i
|
||
class="iconfont icon-menu"
|
||
style="padding-right: 2px;">
|
||
</i>TOC
|
||
</div>
|
||
<ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Goodbye-GitHub"><span class="toc-text">Goodbye GitHub</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#libakerror-improvements"><span class="toc-text">libakerror improvements</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#libakstdlib"><span class="toc-text">libakstdlib</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#libakgl"><span class="toc-text">libakgl</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#akgltest"><span class="toc-text">akgltest</span></a></li></ol>
|
||
</article>
|
||
|
||
|
||
<article class="card card-content">
|
||
<div class="recent-posts-card">
|
||
<div class="recent-posts-header">
|
||
<i
|
||
class="iconfont icon-wenzhang_huaban"
|
||
style="padding-right: 2px;">
|
||
</i>Recent Posts
|
||
</div>
|
||
<div class="recent-posts-list">
|
||
|
||
<div class="recent-posts-item">
|
||
<div class="recent-posts-item-title">2026-05-15</div>
|
||
<a href="/2026/05/15/Devlog-20260518/"><div class="recent-posts-item-content">Devlog Entry</div></a>
|
||
</div>
|
||
|
||
<div class="recent-posts-item">
|
||
<div class="recent-posts-item-title">2026-02-23</div>
|
||
<a href="/2026/02/23/Why-Firefighting/"><div class="recent-posts-item-content">Why Firefighting</div></a>
|
||
</div>
|
||
|
||
<div class="recent-posts-item">
|
||
<div class="recent-posts-item-title">2026-02-09</div>
|
||
<a href="/2026/02/09/News-2026-Week-5/"><div class="recent-posts-item-content">News 2026 Week 5</div></a>
|
||
</div>
|
||
|
||
<div class="recent-posts-item">
|
||
<div class="recent-posts-item-title">2026-01-30</div>
|
||
<a href="/2026/01/30/News-2026-Week-4/"><div class="recent-posts-item-content">News - 2026 - Week 4</div></a>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</article>
|
||
|
||
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<footer class="footer">
|
||
<div class="footer-container">
|
||
<div>
|
||
<div class="footer-dsc">
|
||
<span>
|
||
Copyright ©
|
||
|
||
|
||
-
|
||
2026
|
||
|
||
</span>
|
||
|
||
<a
|
||
href="mailto:andrew@aklabs.net"
|
||
class="footer-link">
|
||
Andrew Kesterson
|
||
</a>
|
||
<br/>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="footer-dsc">
|
||
|
||
Powered by
|
||
<a
|
||
href="https://hexo.io/"
|
||
class="footer-link"
|
||
target="_blank"
|
||
rel="nofollow noopener noreferrer">
|
||
Hexo
|
||
</a>
|
||
|
||
|
||
<span> | </span>
|
||
|
||
|
||
Theme -
|
||
<a
|
||
href="https://github.com/theme-kaze"
|
||
class="footer-link"
|
||
target="_blank"
|
||
rel="nofollow noopener noreferrer">
|
||
Kaze
|
||
</a>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
</footer>
|
||
|
||
|
||
<a
|
||
role="button"
|
||
id="scrollbutton"
|
||
class="basebutton"
|
||
aria-label="回到顶部">
|
||
<i class="iconfont icon-arrowleft button-icon"></i>
|
||
</a>
|
||
|
||
<a
|
||
role="button"
|
||
id="menubutton"
|
||
aria-label="menu button"
|
||
class="basebutton">
|
||
<i class="iconfont icon-menu button-icon"></i>
|
||
</a>
|
||
<a
|
||
role="button"
|
||
id="popbutton"
|
||
class="basebutton"
|
||
aria-label="控制中心">
|
||
<i class="iconfont icon-expand button-icon"></i>
|
||
</a>
|
||
<a
|
||
role="button"
|
||
id="darkbutton"
|
||
class="basebutton darkwidget"
|
||
aria-label="夜色模式">
|
||
<i class="iconfont icon-weather button-icon"></i>
|
||
</a>
|
||
<a
|
||
role="button"
|
||
id="searchbutton"
|
||
class="basebutton searchwidget"
|
||
aria-label="搜索">
|
||
<i class="iconfont icon-search button-icon"></i>
|
||
</a>
|
||
|
||
|
||
|
||
<script>
|
||
var addImgLayout = function () {
|
||
var img = document.querySelectorAll('.post-content img')
|
||
var i
|
||
for (i = 0; i < img.length; i++) {
|
||
var wrapper = document.createElement('a')
|
||
wrapper.setAttribute('href', img[i].getAttribute('data-src'))
|
||
wrapper.setAttribute('aria-label', 'illustration')
|
||
wrapper.style.cssText =
|
||
'width: 100%; display: flex; justify-content: center;'
|
||
if (img[i].alt) wrapper.dataset.caption = img[i].alt
|
||
wrapper.dataset.nolink = true
|
||
img[i].before(wrapper)
|
||
wrapper.append(img[i])
|
||
var divWrap = document.createElement('div')
|
||
divWrap.classList.add('gallery')
|
||
wrapper.before(divWrap)
|
||
divWrap.append(wrapper)
|
||
}
|
||
baguetteBox.run('.gallery')
|
||
}
|
||
</script>
|
||
<script>
|
||
loadScript(
|
||
"/js/lib/lightbox/baguetteBox.min.js",
|
||
addImgLayout
|
||
)
|
||
</script>
|
||
|
||
|
||
|
||
<script src="/js/main.js"></script>
|
||
|
||
|
||
<script>
|
||
var addLazyload = function () {
|
||
var observer = lozad('.lozad', {
|
||
load: function (el) {
|
||
el.srcset = el.getAttribute('data-src')
|
||
},
|
||
loaded: function (el) {
|
||
el.classList.add('loaded')
|
||
},
|
||
})
|
||
observer.observe()
|
||
}
|
||
</script>
|
||
<script>
|
||
loadScript('/js/lib/lozad.min.js', addLazyload)
|
||
</script>
|
||
|
||
<script src="//instant.page/5.1.0" type="module"
|
||
integrity="sha384-by67kQnR+pyfy8yWP4kPO12fHKRLHZPfEsiSXR8u2IKcTdxD805MGUXBzVPnkLHw"></script>
|
||
|
||
<script>
|
||
var googleAnalytics = function () {
|
||
window.dataLayer = window.dataLayer || []
|
||
function gtag() {
|
||
dataLayer.push(arguments)
|
||
}
|
||
gtag('js', new Date())
|
||
gtag('config', 'G-S3YLF516N6')
|
||
}
|
||
</script>
|
||
<script>
|
||
loadScript(
|
||
'https://www.googletagmanager.com/gtag/js?id=' +
|
||
'G-S3YLF516N6',
|
||
googleAnalytics
|
||
)
|
||
</script>
|
||
|
||
|
||
</body>
|
||
</html>
|