Erase what the text layer drew: three GUI bugs, one cause
The sink painted glyphs on top of whatever was already on the renderer and
never erased anything, and the host deliberately does not clear the frame so a
DRAW survives. The grid was always correct; the screen kept the previous
frame. That surfaced as three separate-looking faults:
- a backspaced character stayed on screen, including across a line wrap
- a scroll left the old rows behind, "papered over with garbage"
- the editing cursor smeared an underscore along every column it passed
through, which read as an underline under the text being typed
render() now fills each row it is about to draw, and each row that has emptied
since it last drew, with the background first. Row by row rather than one
clear over the whole area: the text layer is authoritative over the rows it
occupies and leaves every other pixel alone, so a picture behind the text
loses only the strips the text uses instead of all of it.
echo_line() also truncates rows that hold nothing but the spaces its own erase
pass wrote, which is what backspacing back across a wrap leaves behind. A row
of spaces is text as far as render() is concerned, so it would have been
repainted forever and kept erasing whatever was under it.
The cursor glyph is removed outright, as asked. The smearing was the erase bug
rather than the cursor, so one could come back and behave -- a block would
read better than an underscore.
Four pixel-level tests, because grid assertions could never have caught any of
this. Each was checked by reverting the fix and watching it fail; two of them
were vacuous when first written and are noted as such where they are fixed.
The real-keyboard test also now rubs out a character and scrolls forty lines.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
48
TODO.md
48
TODO.md
@@ -672,7 +672,7 @@ Also on the queue, from the reference's own defect list:
|
||||
`tests/language/statements/multiple_per_line.bas` asserts what actually runs.
|
||||
|
||||
**One thing about it needed a decision rather than a transcription**, and it is recorded as
|
||||
deviation 29 in §5: BASIC 7.0 scopes everything after `THEN` to the condition, so
|
||||
deviation 31 in §5: BASIC 7.0 scopes everything after `THEN` to the condition, so
|
||||
`IF C THEN A : B` must run neither `A` nor `B` when `C` is false. The parser takes exactly one
|
||||
statement per arm, so the rest of the line reaches the statement loop as ordinary statements
|
||||
and the branch has to tell the loop whether to run them.
|
||||
@@ -844,22 +844,46 @@ deviations from the reference's *program*: `main.go` and the SDL half of
|
||||
exactly, and the whole golden corpus is driven through this loop to prove it. What it
|
||||
changes for a *user* is that the window closes when asked.
|
||||
|
||||
26. **The frame is not cleared between frames.** The reference blits a text surface onto the
|
||||
window surface and calls `UpdateSurface`. Here the graphics verbs draw straight to the
|
||||
renderer rather than into a display list, so a `SDL_RenderClear` at the top of each frame
|
||||
would erase every `DRAW` at the end of the frame it was issued in. The text layer is drawn
|
||||
over whatever is already there. **What this costs:** text that has scrolled away leaves its
|
||||
pixels behind wherever a graphics verb has drawn, because nothing repaints that region. A
|
||||
`SCNCLR` does not repaint it either — it clears the text grid, not the bitmap. `GRAPHIC 0`
|
||||
is the verb that wipes the drawing surface.
|
||||
26. **The frame is not cleared, but the text layer erases its own rows.** The reference blits a
|
||||
text surface onto the window surface and calls `UpdateSurface`. Here the graphics verbs draw
|
||||
straight to the renderer rather than into a display list, so a `SDL_RenderClear` at the top
|
||||
of each frame would erase every `DRAW` at the end of the frame it was issued in. The host
|
||||
therefore does not clear.
|
||||
|
||||
27. **The window is closed by the host, and that is not `QUIT`.** Closing the window stops the
|
||||
**The sink erases row by row instead**, filling each row it is about to draw — and each row
|
||||
that has emptied since it last drew — with its background colour first. That makes the text
|
||||
layer authoritative over the rows it occupies and leaves every other pixel alone, which is
|
||||
how a C128's text plane sits over its bitmap.
|
||||
|
||||
**This was originally "the text layer is drawn over whatever is already there", and that was
|
||||
a bug rather than a deviation.** The grid was always right and the screen kept the previous
|
||||
frame, which surfaced as three separate-looking faults reported together: a backspaced
|
||||
character that stayed on screen, a scroll that appeared to overwrite the display with
|
||||
garbage, and the editing cursor smearing an underscore along every column it had passed
|
||||
through. One cause, three symptoms. `tests/akgl_frontend.c` asserts the fix at pixel level —
|
||||
grid assertions could never have caught it, because the grid was never wrong.
|
||||
|
||||
**What it still costs:** a program that draws a picture and then prints loses the strip of
|
||||
picture behind each text row it uses. That is the deliberate half of the trade — the
|
||||
alternative was clearing the whole frame, which loses the picture entirely.
|
||||
|
||||
27. **There is no cursor.** The reference draws one as a literal underscore glyph (`drawCursor`,
|
||||
`basicruntime_graphics.go:33`) and this did too. Removed on request: it sat under the text
|
||||
being typed and made it hard to read.
|
||||
|
||||
Worth separating the two reasons it was unbearable, because only one of them is gone. It
|
||||
*smeared* — an underscore left at every column the cursor passed through — and that was
|
||||
deviation 26's missing erase, now fixed. It also simply sits under the text, which is what
|
||||
an underscore cursor does in a font whose glyphs reach the baseline. A cursor could come back
|
||||
and behave now; a block or a different colour would read better than an underscore.
|
||||
|
||||
28. **The window is closed by the host, and that is not `QUIT`.** Closing the window stops the
|
||||
frontend and leaves `obj->mode` alone; it does not set `AKBASIC_MODE_QUIT`. The distinction
|
||||
is invisible to the standalone program, which exits either way, and load-bearing for an
|
||||
embedding host: a game that closes its own window has not decided that the script is
|
||||
finished, and a script that ran `QUIT` has. `tests/akgl_frontend.c` asserts both halves.
|
||||
|
||||
28. **Piped input still works in an AKGL build.** With a terminal on stdin the window is the
|
||||
29. **Piped input still works in an AKGL build.** With a terminal on stdin the window is the
|
||||
console and lines come from its editor; piped or redirected, they come from the pipe. This
|
||||
is not in the reference, which always reads `os.Stdin` in REPL mode. It exists so
|
||||
`basic < program.bas` behaves the same in both builds — and so the golden corpus can be
|
||||
@@ -879,7 +903,7 @@ deviations from the reference's *program*: `main.go` and the SDL half of
|
||||
|
||||
### Deviations in statement separation
|
||||
|
||||
29. **A branch decides who owns the rest of its line.** BASIC 7.0 scopes every statement after
|
||||
31. **A branch decides who owns the rest of its line.** BASIC 7.0 scopes every statement after
|
||||
`THEN` to the condition, and the reference has no opinion on the matter because it never
|
||||
consumed the `COLON` token at all. The parser here takes exactly one statement for each arm,
|
||||
so the rest of the line arrives at the statement loop as ordinary top-level statements and
|
||||
|
||||
Reference in New Issue
Block a user