• src/doors/syncscumm/door/sst_io.c src/doors/syncscumm/test/test_sst_io

    From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Sat Jul 18 03:02:48 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/465b69d75e64a110152f6b8b
    Modified Files:
    src/doors/syncscumm/door/sst_io.c src/doors/syncscumm/test/test_sst_io_present_pending.c
    Log Message:
    syncscumm: clear the present-pending flag past the dedupe early-return

    cc6f2373ec added a retry for a present() frame a static screen
    stranded: a gated call retains the frame (g_pending_idx/g_pending_pal, g_present_pending=1) and sst_io_tick() re-invokes sst_io_present()
    with it once the gate clears. The clear (g_present_pending = 0) sat
    AFTER the whole-frame dedupe early-return, so a retry whose retained
    frame happened to equal g_last_fb took the dedupe return and never
    reached the clear.

    Repro: a gate defers frame A, retaining it; while still congested the
    engine presents frame B that is byte-identical to g_last_fb (e.g. a
    cursor blink-and-revert -- video_term.cpp's dirty check keys off the
    engine's own dirty/cursor state, not what was actually sent); since
    idx != g_pending_idx the retain overwrites g_pending_idx with B (==
    g_last_fb); the gate clears and sst_io_tick() retries with
    g_pending_idx, which now dedupes out. From then on sst_io_tick()
    re-invokes sst_io_present() every poll forever on an idle static
    screen: a 64000-byte memcmp per poll, plus a stats redraw + wire flush
    every poll if the Ctrl-S bar is on.

    Move the clear to fire for every call that gets past BOTH defer gates
    (pacing and backpressure), before the g_need_st handling. Past both
    gates the frame is no longer stranded -- it either goes out below or
    turns out identical to what the terminal already shows (dedupe); in
    both cases whatever was retained for it has been handled. A later
    frame that defers again is re-retained by the gate code, unaffected by
    clearing here.

    Also factor the retain snippet (memcpy idx/pal into g_pending_*, set g_present_pending=1) that was duplicated verbatim at both gates into a
    single sst_present_retain() helper, called at both call sites, so the
    two copies can no longer drift out of hand-sync.

    Adds a convergence regression to test_sst_io_present_pending.c: gate a
    retry of the exact frame just sent (so the retained frame equals
    g_last_fb), let sst_io_tick() retry it into the dedupe path, and
    assert sst_io_test_present_pending() reads 0 afterward, plus that a
    further tick is a true no-op. Verified RED against the pre-fix
    sst_io.c (assertion failure) and GREEN with this change.
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)