Forward WINDOW through the tee sink
`akbasic_sink_init_tee()` wired write, writeln, readline, clear and -- conditionally -- moveto, and never `window`. The standalone AKGL frontend runs the interpreter against a tee, so the fully implemented `sink_window()` underneath it was unreachable: `WINDOW 0, 0, 20, 4` answered "WINDOW needs a text device with a character grid, and this one has none" on the one build that has a character grid. It reads as an oversight rather than a decision, because `moveto` directly above it is forwarded with reasoning that applies unchanged. `tee_window()` is that function, modelled on `tee_moveto()`, offered only when a half can take it so the refusal still reads correctly through a stdio-only pair. **Writing the test found a second one.** Both optional entry points are assigned conditionally, and neither initializer cleared them first -- so a caller with a sink on the stack got whatever was in that memory, and `CHAR` and `WINDOW` decide whether they can act by testing those pointers for NULL. Both `akbasic_sink_init_tee()` and `akbasic_sink_init_stdio()` now clear them. An initializer that leaves a field alone is not an initializer. tests/sink_tee.c gains a stand-in sink with a grid -- the two stdio halves have neither entry point, which is exactly why they could not show that either is forwarded -- and asserts both directions, the arguments arriving intact, and that a grid-less pair still offers neither. Verified end to end: `WINDOW 0, 0, 20, 4` then `PRINT` succeeds under build-akgl/basic and still refuses by name under build/basic. What this does *not* fix is that a drawing still has to be re-issued every frame: the frontend never clears and SDL is double-buffered, so a drawing issued once appears in one buffer only. Shrinking the text area makes the rest of the window the program's; keeping something there is still the program's job. That half is documented rather than fixed, and is filed as TODO.md section 9 item 5. TODO.md section 6 item 31, struck. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
15
TODO.md
15
TODO.md
@@ -1932,7 +1932,20 @@ in either corpus runs for minutes, which is why the first of these had never bee
|
||||
has to be settled with it: while the pool is dry the failure cannot be trapped, so a
|
||||
program cannot even report this one against itself.
|
||||
|
||||
31. **`WINDOW` cannot be reached in the standalone AKGL build.** `akbasic_sink_init_tee()`
|
||||
31. ~~**`WINDOW` cannot be reached in the standalone AKGL build.**~~ **Done** — `tee_window()`
|
||||
(`src/sink_tee.c`) forwards to whichever half offers one, offered only when one does,
|
||||
with the case in `tests/sink_tee.c` beside the `moveto` one. `WINDOW 0, 0, 20, 4` now
|
||||
works in the windowed build and still refuses by name without a grid.
|
||||
|
||||
**`akbasic_sink_init_tee()` and `akbasic_sink_init_stdio()` also now clear `moveto` and
|
||||
`window` rather than leaving them alone.** Writing the test found it: both are
|
||||
conditional in the tee, so a caller with a sink on the stack got whatever was in that
|
||||
memory, and `CHAR` decides whether it can move a cursor by testing that pointer for
|
||||
NULL. An initializer that leaves a field alone is not an initializer.
|
||||
|
||||
The grid-size half of this item is closed separately; see item 31a below.
|
||||
|
||||
The original report: `akbasic_sink_init_tee()`
|
||||
(`src/sink_tee.c:143`) wires `write`, `writeln`, `readline`, `clear` and -- conditionally
|
||||
-- `moveto`, and never sets `window`. The standalone frontend runs the interpreter against
|
||||
the tee (`src/frontend_akgl.c:215`), so the fully implemented `sink_window()` underneath it
|
||||
|
||||
Reference in New Issue
Block a user