• src/doors/termgfx/test/te

    From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Sat Jul 25 23:10:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/f7b071a67065b38a52c9d738
    Added Files:
    src/doors/termgfx/test/test_termgfx_termio_palfade.c
    Modified Files:
    src/doors/termgfx/termgfx_termio.c src/doors/termgfx/test/unit_termgfx_termio.sh
    Log Message:
    termgfx: patch a palette change instead of repainting the frame

    Any change to the palette forced a whole frame on both tiers. The reasons
    were real: JXL bakes the palette into each frame's RGB pixels, so a fade
    moves every affected pixel's colour while leaving the INDEX buffer the
    diff looks at completely unchanged; and sixel colour registers are shared
    with what is already drawn, so redefining one recolours the screen under
    the parts a patch would not repaint.

    Both are about the same thing -- which pixels a moved palette entry
    actually affects -- and that is computable. Record WHICH entries moved,
    and let the tile diff treat a tile drawn with one of them as dirty even
    when its indices are identical. The gate then has nothing left to protect
    and both tiers drop it.

    What remains is telling the terminal. A SyncTERM box carries no palette by design (its registers persist) and all 256 would dwarf a small box, so the first box of such a frame carries exactly the entries that moved. Every
    other terminal resets registers per image and its used-colour subset
    already covers this; the JXL tier carries RGB pixels and needs nothing.

    Measured on a played Flight of the Amazon Queen session over Windows
    Terminal, this was the whole of what was left: 267 frames repainting for
    this reason, carrying 52.9MB of the session's 68.6MB -- 77% of every byte
    sent, after the bottom-strand fix had already halved the total.

    The new test pins the case only this can catch: a PURE fade, where not one index changes and only what an index means moves. It is deliberately local
    -- a flat background with the moving colour confined to one block -- since
    a colour spread across every tile legitimately dirties the whole frame and would prove nothing.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Sat Jul 25 23:10:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/0b7911f4534019ede60ff740
    Added Files:
    src/doors/termgfx/test/test_termgfx_termio_tiercycle.c
    Modified Files:
    src/doors/syncscumm/door/syncscumm.cpp src/doors/termgfx/termgfx_termio.c termgfx_termio.h src/doors/termgfx/test/unit_termgfx_termio.sh
    Log Message:
    termgfx: let a door step the graphics tier, and give syncscumm F4

    SyncTERM is the only terminal that reaches the JXL tier, which makes it the only one that never draws a sixel -- including the sixel code written specifically FOR SyncTERM, where a dirty box carries no colour registers because they persist, so a palette change has to ride a delta on the first
    box. That path could only run on a SyncTERM talked out of JXL, and nothing could do the talking. It has been shipped and measured but never once
    exercised live on the terminal it exists for.

    termgfx_termio_tier_cycle() steps to the next tier the client can actually
    draw and invalidates the frame cache, because the client is holding a
    picture drawn by the other encoder and the two share none of the state a
    dirty patch assumes. A no-op where only one tier is available, rather than
    a confusing reset.

    It is exposed for the door to bind, NOT taken as a hotkey here. The obvious
    key is F4 -- it is what the sibling doors use -- but syncrpg already spends
    F4 on its resolution toggle and prints it on its help card, and this file is shared with syncrpg. Ctrl-S can be global because no door wants it; this one has an owner already. Taking it here would have broken syncrpg silently.

    syncscumm binds F4 and stops forwarding it to ScummVM, swallowing both
    edges so a release cannot reach the engine alone. F4 is not a ScummVM global (F5 is the menu); if a game turns out to want it, the note there says to
    move the binding rather than forward it, or the tier becomes unreachable
    again.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Sun Jul 26 01:15:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/55237f6a0599715342ca457a
    Added Files:
    src/doors/termgfx/test/test_termgfx_termio_mouse_drawn.c
    Modified Files:
    src/doors/termgfx/termgfx_termio.c src/doors/termgfx/test/unit_termgfx_termio.sh
    Log Message:
    termgfx: map the mouse against the rect the frame was drawn in

    The same defect just fixed in syncconquer lives here, and here it is worse.
    The mapper asked termgfx_image_rect() where the image goes, which answers
    what the CENTERING wanted. Two things then move it before it reaches the terminal: a sixel is placed by CUP, so its true origin is the addressed
    cell's pixel -- the offset rounded down to the cell grid -- and the vstep
    trim can shrink and re-centre the image after that rect was computed.
    Neither is visible to the mapper, so every click carried the difference as
    a constant bias.

    It goes the way that hurts. The image is drawn ABOVE where the mapper
    believes it starts, so clicks map high and the top edge of a target stops answering. At the geometry a live Windows Terminal reported -- 1330x1480,
    20px cells -- the fit asks for 1330x831 at y=314, the trim takes it to 1248x780, and it is finally drawn at y=320: an origin the mapper had no way
    to know. This one costs syncscumm most, where pointing at things IS the
    game.

    present() now records the rect it actually drew, on both the indexed and truecolor paths, and the mapper inverts that, falling back to the requested rect only before the first frame. JXL is placed by pixel offset and was
    already exact; recording it there too keeps one answer rather than two.

    The test drives a real present at that geometry and reads the placement off
    the WIRE -- the CUP that positions the sixel and the raster attributes that state its size -- then clicks the image's own corners. The far corner is
    what pins it: the near corner clamps to (0,0) whether the mapper is biased
    or not, so only the far one fails when it is.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net