diff --git a/README.md b/README.md index 0bbf11f..8801270 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/TODO.md b/TODO.md index a466612..6058358 100644 --- a/TODO.md +++ b/TODO.md @@ -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 12–17** — 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`, diff --git a/deps/libakgl b/deps/libakgl index 5f03475..42b60f7 160000 --- a/deps/libakgl +++ b/deps/libakgl @@ -1 +1 @@ -Subproject commit 5f03475e0fa81ec2fbd966526f5ba2800faf3b9d +Subproject commit 42b60f725dbbe8450ee07995bb3eb175adc88e4f