From 3415c9aec6f586e3aec305cf30f6c55ece337eb9 Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Sat, 1 Aug 2026 08:01:43 -0400 Subject: [PATCH] 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) --- MAINTENANCE.md | 2 +- README.md | 2 +- TODO.md | 24 +++++++++++++----- deps/libakgl | 2 +- include/akbasic/akgl.h | 38 +++++++++++++++++----------- src/frontend_akgl.c | 25 +++++++++--------- src/sprite_akgl.c | 34 ++++++++++++++++--------- tests/akgl_backends.c | 57 +++++++++++++++++++++--------------------- tests/akgl_frontend.c | 8 +++--- tools/screenshot.c | 32 ++++++++++++------------ 10 files changed, 128 insertions(+), 96 deletions(-) diff --git a/MAINTENANCE.md b/MAINTENANCE.md index bb54538..222c7f8 100644 --- a/MAINTENANCE.md +++ b/MAINTENANCE.md @@ -166,7 +166,7 @@ a mixed build leaks pool slots or frees one twice rather than failing to link. |---|---|---|---|---| | `deps/libakerror` | 2.0.1 | `libakerror.so.2` | major only | **none** — no version macro; `include/akbasic/error.h` feature-tests `AKERR_THREAD_SAFE` and `AKERR_EXIT_STATUS_UNREPRESENTABLE` instead | | `deps/libakstdlib` | 0.2.0 | `libakstdlib.so.0.2` | **`MAJOR.MINOR` while major is 0** | `AKSL_VERSION_*`, `aksl_version()`, `AKSL_VERSION_CHECK()` | -| `deps/libakgl` | 0.4.0 | `libakgl.so.0.4` | **`MAJOR.MINOR` while major is 0** | `AKGL_VERSION*`, `akgl_version()`, `AKGL_VERSION_AT_LEAST()` | +| `deps/libakgl` | 0.5.0 | `libakgl.so.0.5` | **`MAJOR.MINOR` while major is 0** | `AKGL_VERSION*`, `akgl_version()`, `AKGL_VERSION_AT_LEAST()` | For both 0.x libraries the soname carries `MAJOR.MINOR` deliberately: 0.1 and 0.2 are *different* ABIs, and both become major-only at 1.0. Do not read `0.1 → 0.2` as a compatible diff --git a/README.md b/README.md index 48eb40c..b0f9944 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ nothing to install first. * [libakstdlib](https://source.starfort.tech/andrew/libakstdlib) 0.2.0 — libc wrappers that report through `libakerror`. String-to-number conversion goes straight to it, which is why `VAL("garbage")` is an error rather than a silent `0`. -* [libakgl](https://source.starfort.tech/andrew/libakgl) 0.4.0 — **optional**, only for +* [libakgl](https://source.starfort.tech/andrew/libakgl) 0.5.0 — **optional**, only for `-DAKBASIC_WITH_AKGL=ON`. Pulls in SDL3. Its soname carries `MAJOR.MINOR` while the major is 0, so rebuild rather than relink. * [basicinterpret](https://source.starfort.tech/andrew/basicinterpret) — the Go original. diff --git a/TODO.md b/TODO.md index a2556bd..59eb1b7 100644 --- a/TODO.md +++ b/TODO.md @@ -1291,11 +1291,23 @@ deviations from the reference's *program*: `main.go` and the SDL half of 40. **A sprite is a real `libakgl` actor, and it is drawn by a renderfunc of ours.** Each of the eight becomes a `SpriteSheet` + `Sprite` + `Character` + `Actor` registered in `AKGL_REGISTRY_ACTOR`, so an embedding game sees BASIC's sprites alongside its own (goal 3). - But `akgl_actor_render()` is not what draws them: it computes its destination *height* from - the sprite's **width**, which would draw a 24×21 Commodore sprite as a 24×24 square, and an - actor carries a single scalar `scale` that cannot express `SPRITE`'s separate x- and - y-expand bits. `src/sprite_akgl.c` installs its own `renderfunc`, which is libakgl's own - extension point for exactly this. Both defects are filed upstream. + But `akgl_actor_render()` is not what draws them. There were two reasons and **libakgl + 0.5.0 fixed one**: the default used to compute its destination *height* from the sprite's + **width**, drawing a 24×21 Commodore sprite as a 24×24 square, and it now uses the height + — libakgl defect 26, filed from here and closed there. + + **The reason that remains is the one that keeps this file.** An actor carries a single + scalar `scale` applied to both axes, which cannot express `SPRITE`'s separate x- and + y-expand bits, so `MOVSPR`'s twice-as-wide-same-height is unrepresentable. libakgl records + it as open and names this interpreter as the caller that needs it; the fix is a design + choice over there — `scale_x`/`scale_y` beside `scale`, or replacing `scale` and taking + the ABI break while the major is still 0 — rather than a patch. Until then + `src/sprite_akgl.c` installs its own `renderfunc`, which is libakgl's own extension point + for exactly this. + + Worth keeping as a pattern: both defects were found by *using* the library for something + it had not been used for, and both were filed rather than worked around silently. One came + back fixed. --- @@ -1708,7 +1720,7 @@ Dependency baseline: |---|---|---| | `deps/libakerror` | 2.0.1 | Private ownership-enforced status registry. akbasic reserves 512–767 in `akbasic_error_register()`. Does not namespace its `coverage` target when embedded — worked around in our `CMakeLists.txt`. **2.0.0 is thread safe and an ABI break** (`libakerror.so.2`): `__akerr_last_ignored` is thread-local and `akerr_next_error()` returns an owned reference, neither of which fails to link when mismatched. **2.0.1 fixes an exit status that mattered more to this band than to any other** — see below. | | `deps/libakstdlib` | 0.2.0 | soname `libakstdlib.so.0.2`. `AKSL_VERSION_CHECK()` asserted in `tests/version_check.c`. This release fixed all six confirmed defects the port was working around — see §1.9, where the bans are now lifted. | -| `deps/libakgl` | 0.4.0 | soname `libakgl.so.0.4`. Owns status codes 256–260. Linked and tested under `-DAKBASIC_WITH_AKGL=ON`, which still defaults OFF so the core library and its whole suite build on a machine with no SDL. **0.3.0 closed every API gap this port had filed** — see §7 — so the four workarounds §3 used to list are gone. 0.4.0 is a leak-and-overread release that changed no public struct, but the soname carries `MAJOR.MINOR` while the major is 0, so rebuild rather than relink. | +| `deps/libakgl` | 0.5.0 | soname `libakgl.so.0.5`. Owns status codes 256–260. Linked and tested under `-DAKBASIC_WITH_AKGL=ON`, which still defaults OFF so the core library and its whole suite build on a machine with no SDL. **0.3.0 closed every API gap this port had filed** — see §7 — so the four workarounds §3 used to list are gone. 0.4.0 was a leak-and-overread release that changed no public struct. **0.5.0 is the first that broke our source as well as our ABI**: it namespaced every exported symbol, so `akgl_render_bind2d` is `akgl_render_2d_bind`, `akgl_sprite_sheet_coords_for_frame` is `akgl_spritesheet_coords_for_frame`, the `renderer`/`camera`/`window` globals carry the prefix, and `_akgl_renderer`/`_akgl_camera` are `akgl_default_renderer`/`akgl_default_camera`. `include/akbasic/akgl.h` asserts the 0.5.0 floor. | **An unhandled error in this band used to exit zero, and 512 is the worst possible base for that.** `libakerror`'s default unhandled-error handler ended in `exit(errctx->status)`, and a diff --git a/deps/libakgl b/deps/libakgl index 93e8e4a..b899f09 160000 --- a/deps/libakgl +++ b/deps/libakgl @@ -1 +1 @@ -Subproject commit 93e8e4afa4fff5778606d50f92107141f4ada180 +Subproject commit b899f09fc8b54802c3f64ba1473c657bd3feaaeb diff --git a/include/akbasic/akgl.h b/include/akbasic/akgl.h index 15e8079..6a56963 100644 --- a/include/akbasic/akgl.h +++ b/include/akbasic/akgl.h @@ -36,26 +36,34 @@ #include /* - * The API floor is 0.3.0: akgl_render_bind2d(), akgl_audio_sweep(), and - * akgl_Keystroke with akgl_controller_poll_keystroke() all arrived there, and - * nothing in this target uses anything newer. The floor asserted below is - * 0.4.0 anyway, and the difference is ABI rather than API. + * **0.5.0 is a hard floor, API as well as ABI**, which is new: every release + * before it moved this line for ABI reasons alone. 0.5.0 namespaced every + * exported symbol, so the spellings this target compiles against do not exist in + * 0.4.0 and the ones 0.4.0 had do not exist here. `akgl_render_bind2d` is + * `akgl_render_2d_bind`, `akgl_sprite_sheet_coords_for_frame` is + * `akgl_spritesheet_coords_for_frame`, and the `renderer`, `camera` and `window` + * globals carry the prefix -- with `_akgl_renderer` and `_akgl_camera` now + * `akgl_default_renderer` and `akgl_default_camera`. * - * The soname carries MAJOR.MINOR while the major is 0, so 0.3 and 0.4 are - * different ABIs *by declaration* -- libakgl's versioning policy says a 0.x - * minor bump may break, and the soname is built to match. 0.4.0 in fact changed - * no public struct: it is leak and overread fixes inside src/. The floor moves - * anyway, because the alternative is deciding for ourselves which of libakgl's - * minor releases were really compatible, and that is exactly the judgement the - * soname exists to take away from us. + * That rename was not cosmetic and libakgl's TODO.md says why at length: an + * exported global called `renderer` collided with a test's own variable of the + * same name, the executable's definition preempted the library's, and every + * texture load in that suite failed while the suite reported success. * - * What it catches is the case the soname cannot: a build against 0.3 headers - * that happens to find a 0.4 library, or the reverse. Refused here rather than + * The soname carries MAJOR.MINOR while the major is 0, so 0.4 and 0.5 are + * different ABIs *by declaration* -- libakgl's versioning policy says a 0.x minor + * bump may break, and the soname is built to match. The floor moves with every + * one of them, because the alternative is deciding for ourselves which of + * libakgl's minor releases were really compatible, and that is exactly the + * judgement the soname exists to take away from us. + * + * What it catches is the case the soname cannot: a build against 0.4 headers + * that happens to find a 0.5 library, or the reverse. Refused here rather than * at link time, because a missing symbol names a function and this names the * release. */ -#if !AKGL_VERSION_AT_LEAST(0, 4, 0) -#error "akbasic's libakgl adaptors require libakgl 0.4.0 or later" +#if !AKGL_VERSION_AT_LEAST(0, 5, 0) +#error "akbasic's libakgl adaptors require libakgl 0.5.0 or later" #endif #include diff --git a/src/frontend_akgl.c b/src/frontend_akgl.c index 762cb54..16a1ed7 100644 --- a/src/frontend_akgl.c +++ b/src/frontend_akgl.c @@ -30,7 +30,8 @@ #include #include /* - * 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 ) { diff --git a/src/sprite_akgl.c b/src/sprite_akgl.c index 207b506..caeac4f 100644 --- a/src/sprite_akgl.c +++ b/src/sprite_akgl.c @@ -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 @@ -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); diff --git a/tests/akgl_backends.c b/tests/akgl_backends.c index cb1df0b..6ca0bcf 100644 --- a/tests/akgl_backends.c +++ b/tests/akgl_backends.c @@ -25,7 +25,7 @@ #include #include /* - * game.h purely for the `renderer` global and its `_akgl_renderer` storage. The + * game.h purely for the `akgl_renderer` global and its `akgl_default_renderer` storage. The * interpreter never calls akgl_game_init() -- it drives subsystems directly, and * owning the game loop is exactly what goal 3 forbids -- but the draw calls read * that global, so a test standing in for a host has to populate it the same way @@ -85,9 +85,9 @@ static akerr_ErrorContext AKERR_NOIGNORE *clear_target(void) { PREPARE_ERROR(errctx); - FAIL_ZERO_RETURN(errctx, SDL_SetRenderDrawColor(renderer->sdl_renderer, 0, 0, 0, 0xff), + FAIL_ZERO_RETURN(errctx, SDL_SetRenderDrawColor(akgl_renderer->sdl_renderer, 0, 0, 0, 0xff), AKGL_ERR_SDL, "%s", SDL_GetError()); - FAIL_ZERO_RETURN(errctx, SDL_RenderClear(renderer->sdl_renderer), + FAIL_ZERO_RETURN(errctx, SDL_RenderClear(akgl_renderer->sdl_renderer), AKGL_ERR_SDL, "%s", SDL_GetError()); SUCCEED_RETURN(errctx); } @@ -104,8 +104,8 @@ static akerr_ErrorContext AKERR_NOIGNORE *start_runtime(const char *source) PASS(errctx, akbasic_sink_init_stdio(&SINK, &SINKSTATE, OUT, NULL)); PASS(errctx, akbasic_runtime_init(&RUNTIME, &SINK)); - PASS(errctx, akbasic_graphics_init_akgl(&GRAPHICS, &GRAPHICSSTATE, renderer)); - PASS(errctx, akbasic_sprite_init_akgl(&SPRITES, &SPRITESSTATE, renderer, &GRAPHICSSTATE)); + PASS(errctx, akbasic_graphics_init_akgl(&GRAPHICS, &GRAPHICSSTATE, akgl_renderer)); + PASS(errctx, akbasic_sprite_init_akgl(&SPRITES, &SPRITESSTATE, akgl_renderer, &GRAPHICSSTATE)); PASS(errctx, akbasic_runtime_set_devices(&RUNTIME, &GRAPHICS, NULL, NULL, &SPRITES)); PASS(errctx, akbasic_runtime_load(&RUNTIME, source)); PASS(errctx, akbasic_runtime_start(&RUNTIME, AKBASIC_MODE_RUN)); @@ -136,7 +136,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_draw_reaches_pixels(void) PASS(errctx, start_runtime("10 COLOR 1, 3\n20 DRAW 1, 10, 20\n")); TEST_REQUIRE_STR(OUTPUT, ""); - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); TEST_REQUIRE(shot != NULL, "could not read the render target back"); /* Palette index 3 is red: 0x88, 0x39, 0x32 in src/graphics_tables.c. */ TEST_REQUIRE(pixel_is(shot, 10, 20, 0x88, 0x39, 0x32), @@ -171,7 +171,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_size_is_the_renderer(void) "20 DRAW 1, 1000, 1000\n")); TEST_REQUIRE_STR(OUTPUT, ""); - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); TEST_REQUIRE(shot != NULL, "could not read the render target back"); /* * The far corner of the user space lands on the far corner of the target. @@ -195,7 +195,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_box_outlines(void) PASS(errctx, start_runtime("10 BOX 1, 10, 10, 40, 40\n")); TEST_REQUIRE_STR(OUTPUT, ""); - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); TEST_REQUIRE(shot != NULL, "could not read the render target back"); TEST_REQUIRE(pixel_is(shot, 10, 10, 0xff, 0xff, 0xff), "the box's corner should be lit"); TEST_REQUIRE(pixel_is(shot, 25, 10, 0xff, 0xff, 0xff), "the box's top edge should be lit"); @@ -223,7 +223,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_paint_fills_region(void) "30 PAINT 2, 25, 25\n")); TEST_REQUIRE_STR(OUTPUT, ""); - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); TEST_REQUIRE(shot != NULL, "could not read the render target back"); /* Palette index 6 is green: 0x55, 0xa0, 0x49. */ TEST_REQUIRE(pixel_is(shot, 25, 25, 0x55, 0xa0, 0x49), @@ -247,7 +247,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_shape_roundtrip(void) "30 GSHAPE A$, 64, 64\n")); TEST_REQUIRE_STR(OUTPUT, ""); - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); TEST_REQUIRE(shot != NULL, "could not read the render target back"); /* The saved corner was at (4,4); pasted at (64,64) it lands at (68,68). */ TEST_REQUIRE(pixel_is(shot, 68, 68, 0xff, 0xff, 0xff), @@ -273,7 +273,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_sink_grid_and_wrap(void) PASS(errctx, akgl_text_measure(font, "A", &cellw, &cellh)); TEST_REQUIRE(cellw > 0 && cellh > 0, "the fixture font should measure a real cell"); - PASS(errctx, akbasic_sink_init_akgl(&AKGLSINK, &AKGLSINKSTATE, renderer, font, + PASS(errctx, akbasic_sink_init_akgl(&AKGLSINK, &AKGLSINKSTATE, akgl_renderer, font, TARGET_SIZE, TARGET_SIZE)); TEST_REQUIRE_INT(AKGLSINKSTATE.cellw, cellw); TEST_REQUIRE_INT(AKGLSINKSTATE.columns, TARGET_SIZE / cellw); @@ -297,7 +297,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_sink_grid_and_wrap(void) * producing a zero-column grid, which would divide by zero on the first * wrap. */ - TEST_REQUIRE_STATUS(akbasic_sink_init_akgl(&AKGLSINK, &AKGLSINKSTATE, renderer, font, 1, 1), + TEST_REQUIRE_STATUS(akbasic_sink_init_akgl(&AKGLSINK, &AKGLSINKSTATE, akgl_renderer, font, 1, 1), AKBASIC_ERR_BOUNDS); TEST_REQUIRE_STATUS(akbasic_sink_init_akgl(&AKGLSINK, &AKGLSINKSTATE, NULL, font, TARGET_SIZE, TARGET_SIZE), @@ -315,7 +315,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_sink_renders(void) bool lit = false; PASS(errctx, clear_target()); - PASS(errctx, akbasic_sink_init_akgl(&AKGLSINK, &AKGLSINKSTATE, renderer, font, + PASS(errctx, akbasic_sink_init_akgl(&AKGLSINK, &AKGLSINKSTATE, akgl_renderer, font, TARGET_SIZE, TARGET_SIZE)); PASS(errctx, AKGLSINK.write(&AKGLSINK, "X")); PASS(errctx, akbasic_sink_akgl_render(&AKGLSINK)); @@ -326,7 +326,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_sink_renders(void) * is free to hint them differently. The seam being tested is that the text * reached the renderer at the cursor's cell at all. */ - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); TEST_REQUIRE(shot != NULL, "could not read the render target back"); for ( y = 0; y < AKGLSINKSTATE.cellh && !lit; y++ ) { for ( x = 0; x < AKGLSINKSTATE.cellw && !lit; x++ ) { @@ -402,7 +402,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_sprite_from_file(void) TEST_REQUIRE_STR(OUTPUT, ""); PASS(errctx, akbasic_sprite_akgl_render(&SPRITES)); - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); TEST_REQUIRE(shot != NULL, "could not read the render target back"); /* * Magenta, not white: SPRITE's colour argument modulates the texture and @@ -448,7 +448,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_sprite_from_pattern(void) TEST_REQUIRE_STR(OUTPUT, ""); PASS(errctx, akbasic_sprite_akgl_render(&SPRITES)); - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); TEST_REQUIRE(shot != NULL, "could not read the render target back"); /* Palette index 6 is green: 0x55, 0xa0, 0x49. */ TEST_REQUIRE(pixel_is(shot, 20, 20, 0x55, 0xa0, 0x49), @@ -485,7 +485,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_sprite_from_shape(void) TEST_REQUIRE_STR(OUTPUT, ""); PASS(errctx, akbasic_sprite_akgl_render(&SPRITES)); - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); TEST_REQUIRE(shot != NULL, "could not read the render target back"); /* * Palette index 3 is red, which is what the BOX was outlined in. The corner @@ -512,7 +512,7 @@ int main(void) ATTEMPT { CATCH(errctx, akgl_error_init()); - renderer = &_akgl_renderer; + akgl_renderer = &akgl_default_renderer; FAIL_ZERO_BREAK(errctx, SDL_Init(SDL_INIT_VIDEO), AKGL_ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError()); @@ -521,17 +521,18 @@ int main(void) FAIL_ZERO_BREAK(errctx, SDL_CreateWindowAndRenderer("net/aklabs/akbasic/test_akgl_backends", TARGET_SIZE, TARGET_SIZE, 0, - &window, &renderer->sdl_renderer), + &akgl_window, &akgl_renderer->sdl_renderer), AKGL_ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError()); /* * Bind the 2D methods. akgl_text_rendertextat() reaches through - * renderer->draw_texture and an SDL_Renderer alone does not fill that in; + * akgl_renderer->draw_texture and an SDL_Renderer alone does not fill that in; * deps/libakgl/tests/draw.c gets away without it because the draw * primitives take the SDL_Renderer directly. These six pointers were - * assigned by hand here until libakgl 0.3.0 split akgl_render_bind2d out - * of akgl_render_init2d -- API-gap item 7, filed from this file. + * assigned by hand here until libakgl 0.3.0 split the bind half out of the + * init half -- API-gap item 7, filed from this file. Both were renamed in + * 0.5.0, from akgl_render_bind2d and akgl_render_init2d. */ - CATCH(errctx, akgl_render_bind2d(renderer)); + CATCH(errctx, akgl_render_2d_bind(akgl_renderer)); /* * The three things akgl_game_init() would have done and this file, being @@ -541,11 +542,11 @@ int main(void) */ CATCH(errctx, akgl_heap_init()); CATCH(errctx, akgl_registry_init()); - camera = &_akgl_camera; - camera->x = 0.0f; - camera->y = 0.0f; - camera->w = (float)TARGET_SIZE; - camera->h = (float)TARGET_SIZE; + akgl_camera = &akgl_default_camera; + akgl_camera->x = 0.0f; + akgl_camera->y = 0.0f; + akgl_camera->w = (float)TARGET_SIZE; + akgl_camera->h = (float)TARGET_SIZE; /* * libakgl's own monospaced fixture, borrowed rather than copied. Being diff --git a/tests/akgl_frontend.c b/tests/akgl_frontend.c index 01cbc70..75551c1 100644 --- a/tests/akgl_frontend.c +++ b/tests/akgl_frontend.c @@ -33,7 +33,7 @@ #include #include #include -/* game.h for the `camera` global, which akgl_actor_render() reads. */ +/* game.h for the `akgl_camera` global, which akgl_actor_render() reads. */ #include #include #include @@ -875,9 +875,9 @@ static akerr_ErrorContext AKERR_NOIGNORE *test_host_can_draw_an_actor(void) PASS(errctx, start_frontend(NULL)); - TEST_REQUIRE(camera != NULL, "the frontend should have pointed the camera somewhere"); - TEST_REQUIRE_INT((int)camera->w, WINDOW_W); - TEST_REQUIRE_INT((int)camera->h, WINDOW_H); + TEST_REQUIRE(akgl_camera != NULL, "the frontend should have pointed the camera somewhere"); + TEST_REQUIRE_INT((int)akgl_camera->w, WINDOW_W); + TEST_REQUIRE_INT((int)akgl_camera->h, WINDOW_H); TEST_REQUIRE(AKGL_REGISTRY_ACTOR != 0, "the frontend should have created the registries"); memset(sheetname, 0, sizeof(sheetname)); diff --git a/tools/screenshot.c b/tools/screenshot.c index 6fd2451..3b81136 100644 --- a/tools/screenshot.c +++ b/tools/screenshot.c @@ -104,22 +104,22 @@ static akerr_ErrorContext AKERR_NOIGNORE *draw_program(const char *outpath, int SDL_Surface *shot = NULL; PASS(errctx, akgl_error_init()); - renderer = &_akgl_renderer; + akgl_renderer = &akgl_default_renderer; FAIL_ZERO_RETURN(errctx, SDL_Init(SDL_INIT_VIDEO), AKGL_ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError()); FAIL_ZERO_RETURN(errctx, SDL_CreateWindowAndRenderer("net/aklabs/akbasic/screenshot", w, h, 0, - &window, &renderer->sdl_renderer), + &akgl_window, &akgl_renderer->sdl_renderer), AKGL_ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError()); - PASS(errctx, akgl_render_bind2d(renderer)); + PASS(errctx, akgl_render_2d_bind(akgl_renderer)); PASS(errctx, akgl_heap_init()); PASS(errctx, akgl_registry_init()); - camera = &_akgl_camera; - camera->x = 0.0f; - camera->y = 0.0f; - camera->w = (float32_t)w; - camera->h = (float32_t)h; + akgl_camera = &akgl_default_camera; + akgl_camera->x = 0.0f; + akgl_camera->y = 0.0f; + akgl_camera->w = (float32_t)w; + akgl_camera->h = (float32_t)h; /* * Opaque black, so a figure has a defined background rather than whatever @@ -127,17 +127,17 @@ static akerr_ErrorContext AKERR_NOIGNORE *draw_program(const char *outpath, int * over the output for a reason src/graphics_akgl.c explains -- clearing is * the host's prerogative -- and this is the host doing it. */ - FAIL_ZERO_RETURN(errctx, SDL_SetRenderDrawColor(renderer->sdl_renderer, 0, 0, 0, 0xff), + FAIL_ZERO_RETURN(errctx, SDL_SetRenderDrawColor(akgl_renderer->sdl_renderer, 0, 0, 0, 0xff), AKGL_ERR_SDL, "%s", SDL_GetError()); - FAIL_ZERO_RETURN(errctx, SDL_RenderClear(renderer->sdl_renderer), + FAIL_ZERO_RETURN(errctx, SDL_RenderClear(akgl_renderer->sdl_renderer), AKGL_ERR_SDL, "%s", SDL_GetError()); /* stdout: a program that errors puts its "? line : CLASS message" there, which is what the caller shows when a figure comes out wrong. */ PASS(errctx, akbasic_sink_init_stdio(&SINK, &SINKSTATE, stdout, NULL)); PASS(errctx, akbasic_runtime_init(&RUNTIME, &SINK)); - PASS(errctx, akbasic_graphics_init_akgl(&GRAPHICS, &GRAPHICSSTATE, renderer)); - PASS(errctx, akbasic_sprite_init_akgl(&SPRITES, &SPRITESSTATE, renderer, &GRAPHICSSTATE)); + PASS(errctx, akbasic_graphics_init_akgl(&GRAPHICS, &GRAPHICSSTATE, akgl_renderer)); + PASS(errctx, akbasic_sprite_init_akgl(&SPRITES, &SPRITESSTATE, akgl_renderer, &GRAPHICSSTATE)); PASS(errctx, akbasic_runtime_set_devices(&RUNTIME, &GRAPHICS, NULL, NULL, &SPRITES)); PASS(errctx, akbasic_runtime_load(&RUNTIME, SOURCE)); PASS(errctx, akbasic_runtime_start(&RUNTIME, AKBASIC_MODE_RUN)); @@ -151,7 +151,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *draw_program(const char *outpath, int */ PASS(errctx, akbasic_sprite_akgl_render(&SPRITES)); - shot = SDL_RenderReadPixels(renderer->sdl_renderer, NULL); + shot = SDL_RenderReadPixels(akgl_renderer->sdl_renderer, NULL); FAIL_ZERO_RETURN(errctx, (shot != NULL), AKGL_ERR_SDL, "Couldn't read the target back: %s", SDL_GetError()); if ( !IMG_SavePNG(shot, outpath) ) { @@ -196,9 +196,9 @@ int main(int argc, char **argv) CATCH(errctx, read_program(argv[1])); CATCH(errctx, draw_program(argv[2], w, h)); } CLEANUP { - if ( window != NULL ) { - SDL_DestroyWindow(window); - window = NULL; + if ( akgl_window != NULL ) { + SDL_DestroyWindow(akgl_window); + akgl_window = NULL; } SDL_Quit(); } PROCESS(errctx) {