Skip to content

Set the pending-wrap flag only at the phantom column - #115

Merged
tomlm merged 3 commits into
tomlm:mainfrom
JohnCampionJr:fix/pending-wrap-states-a-fact
Aug 30, 2026
Merged

tomlm merged 3 commits into
tomlm:mainfrom
JohnCampionJr:fix/pending-wrap-states-a-fact

Conversation

@JohnCampionJr

Copy link
Copy Markdown
Collaborator

Fixes the asciiquarium rendering corruption: duplicated fragments trailing left-moving sprites, missing characters on right-moving ones.

Root cause

SetCursorRaw set PendingWrap = true on every raw advance — after every printed character — documented as harmless because "only the boundary column reads it." #93 broke that contract by adding two readers that consult the flag anywhere: SettleForEditing (ICH/DCH/ECH) and the CUB phantom-column adjustment. From then on, any CUB or DCH issued immediately after printing acted one column left of the cursor.

It looked rate- and machine-dependent, but never was: it needs a print immediately followed by a relative-back or edit in the stream, which curses emits as a cursor-motion optimisation — so it tracked what the animation happened to draw. A 16-byte repro settles it:

ESC[5;1H AB ESC[1P tail    ->  "Atail"  (the B is eaten; xterm keeps "ABtail")

Fix

The flag now states a fact instead of trusting its readers: PendingWrap = x > _scrollRight, true exactly at the one-past-the-margin position only a fill can produce. DECRC's explicit restore still runs through SetPendingWrap afterwards, so the saved ambiguity survives.

Verification

  • The three repro tests fail on main, pass with the fix; two guard tests prove autowrap and the fill-then-insert settle case still work.
  • Full suite: 2,034/2,034.
  • A recorded 1.7MB corrupted asciiquarium session replayed through the emulator: 790 corrupted-buffer snapshots on main, 0 with the fix.
  • 8-minute live asciiquarium soak on the machine that previously glitched within seconds: clean.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

JohnCampionJr and others added 3 commits August 30, 2026 16:43
SetCursorRaw set PendingWrap = true on EVERY raw advance -- after every
printed character -- documented as harmless because "only the boundary
column reads it". tomlm#93 quietly broke that contract by adding two readers
that consult the flag anywhere: SettleForEditing (ICH/DCH/ECH) and the
CUB phantom-column adjustment. From then on, any CUB or DCH issued
immediately after printing acted one column LEFT of the cursor.

On screen that was the asciiquarium bug: duplicated fragments trailing
left-moving sprites (CUB off by one leaves the old cells unerased) and
characters eaten from right-moving ones (DCH deletes one cell early).
It looked rate- and machine-dependent, but it never was -- it needed a
print immediately followed by a relative-back or edit in the stream,
which curses emits as a cursor-motion optimisation, so it tracked what
the animation happened to draw. A 16-byte repro settles it:
ESC[5;1H AB ESC[1P eats the B.

The fix makes the flag state a fact instead of trusting its readers:
PendingWrap = x > _scrollRight, true exactly at the one-past-the-margin
position only a fill can produce. DECRC's explicit restore still runs
through SetPendingWrap afterwards, so the saved ambiguity survives.

Tests: the three repros (DCH after print, CUB after print, a stale flag
carried across a cursor address), plus two guards proving the fix does
not regress what the flag is FOR -- autowrap at the last column, and
SettleForEditing's fill-then-insert case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tomlm
tomlm merged commit fef1c1b into tomlm:main Aug 30, 2026
5 checks passed
@JohnCampionJr
JohnCampionJr deleted the fix/pending-wrap-states-a-fact branch August 31, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants