Files
www.aklabs.net/2026/05/15/Devlog-20260518/index.html

879 lines
43 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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/Technology/">
<div class="categories-list-item">
Technology
<span class="categories-list-item-badge">11</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/Leadership/">
<div class="categories-list-item">
Leadership
<span class="categories-list-item-badge">8</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>
<a href="/categories/Philosophy/">
<div class="categories-list-item">
Philosophy
<span class="categories-list-item-badge">5</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>
</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-15T22: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 Ive been hacking on recently.</p>
<h1 id="Goodbye-GitHub"><a href="#Goodbye-GitHub" class="headerlink" title="Goodbye GitHub"></a>Goodbye GitHub</h1><p>Ive become increasingly unhappy with Github for a variety of reasons. For that reason, Ive 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. Ive also moved my personal website off of GitHub Pages and back in to Starforts 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. Im also not interested in Microsoft scraping all of my source code to train their AI models, so Im 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. Im baking it into all of my new projects that Im writing in C, so its 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. Im not sure I like them not being namespaced, but I also dont 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 &gt;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 dont 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">&#123;</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">&#125;</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 - thats 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&#x2F;stdlib calls in <code>libakerror</code> macros frequently, so Ive started a libc wrapper that does that for me. It doesnt 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 &#x3D; NULL;
char membuf[512];
PREPARE_ERROR(e);
PASS(e, fopen(&quot;somefile.txt&quot;, &quot;rb&quot;, &amp;fp);
PASS(e, fread((void *)&amp;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, Ive 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. Theres barely even a computer involved - its 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&#x2F;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 dont want to deal with all that complexity”.</p>
<p>Third, this library is 100% pure C (Ive <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. Im sure some of that was my own fault, but some of it was because the STL is notoriously slow. Then theres 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. Heres 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, Im building this library because I dont like being locked in to any particular engine. Godot, Unreal, Unity, GameMaker, RPG Maker, theyre 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 its 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, Im building this project with cmake, as opposed to the Makefiles Im 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 its hard to conceptualize how to test some of what it does. That might just be me being lazy.</p>
<p>Theres 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 its a fun project. Ill 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> Ill start producing small games with it. I think Ill start with a breakout clone.</p>
</div>
<div>
</div>
</article>
<div class="nav">
<div class="nav-item-prev">
<a
href="/2026/05/21/News-2026-Week-2/"
class="nav-link">
<i class="iconfont icon-left nav-prev-icon"></i>
<div>
<div class="nav-label">Prev</div>
<div class="nav-title">News-2026-Week-2 </div>
</div>
</a>
</div>
<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-21</div>
<a href="/2026/05/21/News-2026-Week-2/"><div class="recent-posts-item-content">News-2026-Week-2</div></a>
</div>
<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>
</div>
</article>
</div>
</aside>
</div>
</div>
</div>
</div>
<footer class="footer">
<div class="footer-container">
<div>
<div class="footer-dsc">
<span>
Copyright ©
-
2026
</span>
&nbsp;
<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">
&nbsp;Hexo
</a>
<span>&nbsp;|&nbsp;</span>
Theme -
<a
href="https://github.com/theme-kaze"
class="footer-link"
target="_blank"
rel="nofollow noopener noreferrer">
&nbsp;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>