Bump libakgl to 42b60f7: every gap this port filed is now closed

All four capability gaps filed against libakgl have landed upstream, so nothing
in this repository is waiting on that library any more.

  text measurement   -> akgl_text_measure, akgl_text_measure_wrapped
  immediate drawing  -> akgl_draw_point/_line/_rect/_filled_rect/_circle/
                        _flood_fill/_copy_region/_paste_region
  audio              -> akgl_audio_init/_tone/_envelope/_waveform/_volume/
                        _stop/_voice_active/_mix
  console input      -> akgl_controller_poll_key, akgl_controller_flush_keys

The signatures came back close to what was filed, and two details are worth
recording for whoever writes src/sink_akgl.c next. The draw calls take an
akgl_RenderBackend * as their first argument rather than reaching for a global
renderer, which fits goal 3's rule that the interpreter draws through whatever
renderer the host already initialized. And the audio API is a synthesised-voice
one -- a tone on a numbered voice plus a separate ADSR envelope -- which is the
shape PLAY and ENVELOPE actually need, rather than the sample playback
SDL3_mixer would have offered.

Checked rather than assumed: libakgl's status band is still 256-260, so the
coordinated range map in CLAUDE.md is unaffected and akbasic's 512-767 does not
move.

Documentation that described these as open is corrected in the same commit --
section 7's gap list, section 3's "known gap" note, the priority list, the
dependency table, and the README's unimplemented-verbs section all said blocked
and no longer are. Section 3 also gains the thing that replaced the gap: the
character grid comes from akgl_text_measure(font, "A", &w, &h), the direct
equivalent of the font.SizeUTF8("A") at basicruntime.go:96, and
akgl_text_measure_wrapped takes the same wraplength argument
akgl_text_rendertextat does so the measurement and the draw cannot disagree
about where a line breaks.

One caveat recorded rather than papered over: -DAKBASIC_WITH_AKGL=ON has never
been configured in this repository, because until now there was nothing to build
against. The akbasic_akgl target is unproven and needs libakgl's own submodules
present. Expect to fix something there on the first attempt.

Default build unaffected: ctest 61/61, doxygen exits 0, no warnings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 07:56:02 -04:00
parent 506af093a1
commit 7f16337a16
3 changed files with 51 additions and 24 deletions

View File

@@ -410,7 +410,7 @@ Plus six more the original already carried: a leading `0` selects base 8, so `PR
* `USING`, `VERIFY`, `VOL`, `WAIT`, `WIDTH`, `WINDOW`
* The I/O-channel variants (`GETIO`, `INPUTIO`, `OPENIO`, `PRINTIO`, `RECORDIO`)
Four of those are blocked on capabilities `libakgl` does not have yet — text measurement, immediate-mode drawing, audio, and a non-blocking keystroke read. Rather than work around them here, they are filed in [`deps/libakgl/TODO.md`](deps/libakgl/TODO.md) under "API gaps blocking akbasic", with the entry point each one wants.
None of those is blocked on a missing `libakgl` capability any more. Four were — text measurement, immediate-mode drawing, audio, and a non-blocking keystroke read — and rather than work around them here they were filed in [`deps/libakgl/TODO.md`](deps/libakgl/TODO.md) under "API gaps blocking akbasic". All four have since landed upstream (`akgl_text_measure`, the `akgl_draw_*` family, `akgl_audio_*`, and `akgl_controller_poll_key`), so what is left is akbasic-side work.
## Deliberately out of scope

71
TODO.md
View File

@@ -371,9 +371,18 @@ such ceiling because it called `make()`.
## 3. Remaining work: the akgl text sink
**`src/sink_akgl.c`.** Implement the §1.5 vtable against `akgl_text_loadfont` and
`akgl_text_rendertextat`. Owns the cursor, the wrap, and the scroll — everything in
`basicruntime_graphics.go` except `Write`/`Println`, which are now the sink interface itself.
**Unblocked as of `libakgl` 42b60f7** — the text-measurement call this waited on now exists.
**`src/sink_akgl.c`.** Implement the §1.5 vtable against `akgl_text_loadfont`,
`akgl_text_rendertextat` and `akgl_text_measure`. Owns the cursor, the wrap, and the scroll —
everything in `basicruntime_graphics.go` except `Write`/`Println`, which are now the sink
interface itself.
The character grid comes from `akgl_text_measure(font, "A", &w, &h)`, which is the direct
equivalent of the `font.SizeUTF8("A")` at `basicruntime.go:96` that the reference derives
`maxCharsW`/`maxCharsH` from. For wrapping, `akgl_text_measure_wrapped` takes the same
`wraplength` argument `akgl_text_rendertextat` already does, so the measurement and the draw
cannot disagree about where a line breaks.
**The interpreter does not own the window, the renderer, or the game loop.** The sink draws
through whatever renderer the host already initialized. `akbasic_sink_init_akgl()` takes the
@@ -389,13 +398,15 @@ guaranteed to be unnamed. It is idempotent, so calling it from both `akbasic_sin
and a host that already did is harmless.
`PASS` its result; a range collision is an initialization failure, not a warning.
**Known gap:** `libakgl` has no text-measurement call — the Go code needs
`font.SizeUTF8("A")` (`basicruntime.go:96`) to compute `maxCharsW`/`maxCharsH`, and there is no
`akgl_text_*` equivalent. **File it in `deps/libakgl/TODO.md`** before writing a workaround.
See §7.
**Still to check before starting:** `-DAKBASIC_WITH_AKGL=ON` has never been configured in this
repository, because until now there was nothing to build against. It needs `libakgl`'s own
submodules present (`git submodule update --init --recursive` pulls SDL and friends), and the
`akbasic_akgl` target in `CMakeLists.txt` has therefore never been compiled. Expect to fix
something there on the first attempt.
*Acceptance:* `tests/sink_akgl.c` against the offscreen renderer harness, or — if that harness
does not exist yet — a known-failing test plus a `libakgl` TODO entry saying so.
*Acceptance:* `tests/sink_akgl.c` against the offscreen renderer harness described in
`deps/libakgl/TODO.md` under "Remaining work". If that harness still does not exist, a
known-failing test plus a `libakgl` TODO entry saying so.
---
@@ -595,15 +606,29 @@ When phase 7 or phase 8 needs something `libakgl` does not have, **stop and file
the `akgl_*` entry point should look like, and what tests would cover it. Growing `libakgl` to
serve the interpreter is a wanted outcome, not a detour.
Known gaps, to be filed as they are reached:
**All four gaps filed so far are now closed upstream**, at `libakgl` 42b60f7. Nothing here is
blocked on `libakgl` any more; what remains is akbasic-side work.
1. **Text measurement.** No `akgl_text_*` call returns the pixel size of a string in a font.
`PRINT` wrapping, the cursor, and `WIDTH` all need it. Blocks 7.1.
2. **Immediate-mode drawing.** `src/draw.c` is at 0% coverage and there is no public API for
lines, boxes, circles or flood fill. Blocks group G.
3. **Audio.** There is no mixer-backed API at all. Blocks group I entirely.
4. **Console input.** `GET`/`GETKEY` need a non-blocking keystroke read that does not require
the interpreter to own the SDL event loop. Blocks part of group E.
| Was blocking | Closed by |
|---|---|
| Text measurement (§3, the akgl sink) | `akgl_text_measure`, `akgl_text_measure_wrapped` |
| Immediate-mode drawing (§4 group G) | `akgl_draw_point`/`_line`/`_rect`/`_filled_rect`/`_circle`/`_flood_fill`/`_copy_region`/`_paste_region` |
| Audio (§4 group I) | `akgl_audio_init`/`_tone`/`_envelope`/`_waveform`/`_volume`/`_stop`/`_voice_active`/`_mix` |
| Console input (§4 group E) | `akgl_controller_poll_key`, `akgl_controller_flush_keys` |
Two notes for whoever picks up §3 next. The draw calls take an `akgl_RenderBackend *` as their
first argument rather than reaching for a global renderer, which fits the rule that the
interpreter draws through whatever renderer the host already initialized. And the audio API is
a synthesised-voice one — `akgl_audio_tone(voice, hz, ms)` with a separate ADSR envelope —
which is the shape `PLAY` and `ENVELOPE` need, rather than the sample playback SDL3_mixer
would have given.
Building against any of it needs `-DAKBASIC_WITH_AKGL=ON`, which pulls in `libakgl`'s own
submodules (SDL and friends). Those are not initialized in a default clone; `git submodule
update --init --recursive` gets them.
No gaps are currently outstanding. When the next one turns up, file it there rather than
working around it here.
---
@@ -660,11 +685,13 @@ keeping:
What remains, in priority order:
1. **§3 — the akgl text sink.** Blocked on `libakgl` having no text-measurement call; that gap
needs filing in `deps/libakgl/TODO.md` before any workaround is written.
2. **§4 — the language completion work queue.** Groups A, B, D, F and J need nothing from
`libakgl` and can start immediately. Multiple statements per line (the `COLON` token exists
and nothing consumes it) should come first, because `DO`/`LOOP` reads badly without it.
1. **§3 — the akgl text sink.** No longer blocked: `libakgl` 42b60f7 landed
`akgl_text_measure`, so this is now ordinary work. Note that `-DAKBASIC_WITH_AKGL=ON` has
never been configured here, so the `akbasic_akgl` target is unproven.
2. **§4 — the language completion work queue.** Nothing in it is blocked on `libakgl` any more:
groups G, I and part of E were, and `libakgl` 42b60f7 closed all three. Multiple statements
per line (the `COLON` token exists and nothing consumes it) should still come first, because
`DO`/`LOOP` reads badly without it.
3. **§6 items 1217** — the defects the port uncovered. Item 12 is the one that produces a
*wrong answer* rather than a refused one, and should be fixed first.
4. **Mutation survivors in `src/value.c`.** The harness is in place (`scripts/mutation_test.py`,

2
deps/libakgl vendored