Take libakgl 0.5.0 and rename every symbol it namespaced

0.5.0 gives every exported symbol the akgl_ prefix, which is the first libakgl
release to break this project's source rather than only its ABI. The soname goes
to libakgl.so.0.5 and include/akbasic/akgl.h asserts the floor.

What moved here: akgl_render_bind2d is akgl_render_2d_bind,
akgl_sprite_sheet_coords_for_frame is akgl_spritesheet_coords_for_frame, the
renderer, camera and window globals carry the prefix, and _akgl_renderer and
_akgl_camera are akgl_default_renderer and akgl_default_camera.

The renames were applied by site rather than by pattern, because renderer is also
a parameter name in src/sprite_akgl.c and a struct member throughout
src/frontend_akgl.c -- a substitution would have rewritten both without a word.
That is the same trap upstream describes hitting, and it is worth knowing that
the defect behind the rename was not cosmetic: an exported global called renderer
collided with a test's own variable, the executable's definition preempted the
library's, and every texture load in that suite failed while the suite passed.

0.5.0 also fixes libakgl defect 26, which was one of the two reasons
src/sprite_akgl.c installs its own renderfunc: akgl_actor_render took its
destination height from the sprite's width, drawing a 24x21 Commodore sprite as a
24x24 square. The renderfunc stays, because the other reason has not moved -- an
actor carries one scalar scale and SPRITE has separate x- and y-expand bits --
but the comments and TODO.md deviation 40 no longer claim a defect that is fixed.

Every documentation figure re-renders byte-identical under the new library, which
is what says the sprite and drawing paths did not move underneath them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-01 08:01:43 -04:00
parent 8077806598
commit 3415c9aec6
10 changed files with 128 additions and 96 deletions

View File

@@ -30,7 +30,8 @@
#include <akgl/controller.h>
#include <akgl/error.h>
/*
* game.h purely for the `renderer` global and its `_akgl_renderer` storage.
* game.h purely for the `akgl_renderer` global and its `akgl_default_renderer`
* storage.
* akgl_text_rendertextat() takes no renderer argument and reads that global, so
* a host that never calls akgl_game_init() -- which is every host embedding this
* interpreter, since owning the game loop is exactly what goal 3 forbids -- has
@@ -72,13 +73,13 @@ akerr_ErrorContext *akbasic_frontend_akgl_init(akbasic_AkglFrontend *obj, const
FAIL_ZERO_RETURN(errctx, TTF_Init(), AKGL_ERR_SDL,
"Couldn't initialize SDL_ttf: %s", SDL_GetError());
obj->renderer = &_akgl_renderer;
renderer = obj->renderer;
obj->renderer = &akgl_default_renderer;
akgl_renderer = obj->renderer;
FAIL_ZERO_RETURN(errctx,
SDL_CreateWindowAndRenderer(title, w, h, 0,
&obj->window, &obj->renderer->sdl_renderer),
AKGL_ERR_SDL, "Couldn't create the window: %s", SDL_GetError());
window = obj->window;
akgl_window = obj->window;
/*
* Without this SDL sends no SDL_EVENT_TEXT_INPUT at all, every keystroke
@@ -95,14 +96,14 @@ akerr_ErrorContext *akbasic_frontend_akgl_init(akbasic_AkglFrontend *obj, const
SDL_GetError());
}
/*
* Bind the 2D methods onto the renderer we just made. akgl_render_init2d()
* Bind the 2D methods onto the renderer we just made. akgl_render_2d_init()
* would do this too, but it creates its own window from the game properties
* first, which is the akgl_game_init() path a host with its own window is
* not on. libakgl 0.3.0 split the vtable half out for exactly this caller --
* it was API-gap item 7, and until it landed these six pointers were
* assigned by hand here and in tests/akgl_backends.c.
*/
PASS(errctx, akgl_render_bind2d(obj->renderer));
PASS(errctx, akgl_render_2d_bind(obj->renderer));
/*
* The object pools and the name registries. Every akgl_*_initialize ends in
@@ -133,11 +134,11 @@ akerr_ErrorContext *akbasic_frontend_akgl_init(akbasic_AkglFrontend *obj, const
* anybody could act on. One screen's worth at the origin: BASIC has no verb
* that scrolls a view, so the world and the screen are the same thing here.
*/
camera = &_akgl_camera;
camera->x = 0.0f;
camera->y = 0.0f;
camera->w = (float)w;
camera->h = (float)h;
akgl_camera = &akgl_default_camera;
akgl_camera->x = 0.0f;
akgl_camera->y = 0.0f;
akgl_camera->w = (float)w;
akgl_camera->h = (float)h;
obj->font = TTF_OpenFont(fontpath, (float)fontsize);
FAIL_ZERO_RETURN(errctx, (obj->font != NULL), AKGL_ERR_SDL,
@@ -331,7 +332,7 @@ void akbasic_frontend_akgl_shutdown(akbasic_AkglFrontend *obj)
SDL_StopTextInput(obj->window);
SDL_DestroyWindow(obj->window);
obj->window = NULL;
window = NULL;
akgl_window = NULL;
}
TTF_Quit();
if ( obj->ownssdl ) {

View File

@@ -17,12 +17,20 @@
* loads through akgl_spritesheet_initialize(), the same two calls
* akgl_sprite_load_json_spritesheet() makes.
*
* Every actor gets a renderfunc of its own rather than akgl_actor_render(). Two
* reasons, both defects filed upstream: the default computes its destination
* height from the sprite's *width*, which draws a 24x21 Commodore sprite as a
* 24x24 square; and an actor carries one scalar `scale`, which cannot express
* SPRITE's separate x- and y-expand bits. Installing a function pointer is
* libakgl's own extension point for exactly this, so nothing is forked.
* Every actor gets a renderfunc of its own rather than akgl_actor_render(). It
* was two reasons, both filed upstream, and **libakgl 0.5.0 fixed one of them**:
* the default used to compute its destination height from the sprite's *width*,
* drawing a 24x21 Commodore sprite as a 24x24 square, and it now uses the
* height. That was libakgl defect 26.
*
* The remaining reason is the one that still requires this file: an akgl_Actor
* carries a single scalar `scale` applied to both axes, which cannot express
* SPRITE's separate x- and y-expand bits. libakgl records that as an open item
* and names this interpreter as the caller that needs it; the shape it takes --
* scale_x/scale_y beside scale, or replacing scale outright -- is a design
* decision over there rather than a patch. Until it lands, installing a function
* pointer is libakgl's own extension point for exactly this, so nothing is
* forked.
*/
#include <errno.h>
@@ -82,9 +90,11 @@ static akerr_ErrorContext *slot_of(akbasic_SpriteBackend *self, int n, akbasic_A
/**
* @brief Draw one BASIC sprite, in place of akgl_actor_render().
*
* Differs from the default in exactly two ways, both noted at the top of this
* file: the destination height comes from the sprite's height rather than its
* width, and the two expansion bits scale the axes independently.
* Differs from the default in exactly one way now, noted at the top of this file:
* the two expansion bits scale the axes independently, where an actor's single
* `scale` cannot. Taking the destination height from the sprite's height used to
* be the second difference and is the default's behaviour as of libakgl 0.5.0 --
* the line below is no longer a correction, only agreement.
*
* Like the default, it skips rather than reports: an actor with no image, one
* that is hidden, one whose slot has been reused. A frame is not the place to
@@ -121,9 +131,9 @@ static akerr_ErrorContext *spr_render_actor(akgl_Actor *obj)
SUCCEED_RETURN(errctx);
}
PASS(errctx, akgl_sprite_sheet_coords_for_frame(sprite, &src, 0));
dest.x = obj->x - (camera != NULL ? camera->x : 0.0f);
dest.y = obj->y - (camera != NULL ? camera->y : 0.0f);
PASS(errctx, akgl_spritesheet_coords_for_frame(sprite, &src, 0));
dest.x = obj->x - (akgl_camera != NULL ? akgl_camera->x : 0.0f);
dest.y = obj->y - (akgl_camera != NULL ? akgl_camera->y : 0.0f);
dest.w = (float32_t)sprite->width * (state->xexpand[found] ? 2.0f : 1.0f);
dest.h = (float32_t)sprite->height * (state->yexpand[found] ? 2.0f : 1.0f);