• src/doors/termgfx/termgfx_termio.c xtrn/syncbass/syncscumm.example.ini

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Thursday, July 23, 2026 02:18:02
    https://gitlab.synchro.net/main/sbbs/-/commit/2a6fb1b4a4d713b6bc516dcb
    Modified Files:
    src/doors/termgfx/termgfx_termio.c xtrn/syncbass/syncscumm.example.ini Log Message:
    termgfx: end the game when the player has stopped playing

    The sixth and last door to get idle-user detection, and the only one where
    the work belongs in SHARED termio rather than the door: every door driven through termgfx_termio gets it at once. syncscumm today, syncrpg by
    inheritance -- neither needed a line of its own.

    Same reasoning as the siblings: the BBS's own "Maximum Inactivity" cannot
    see a DSR-paced door, since that counter is reset by any socket read and
    frame pacing makes the terminal answer ~10x/second on its own. Presence has
    to be judged on real input, which only the door can tell apart from its own pace-acks.

    Warn-then-terminate, countdown on the bottom row the stats bar already owns, same wording as the siblings, 10-minute default. Configured by -i<seconds>
    or the door's own <name>.ini [idle] section; the argument wins when given,
    so a launcher can pass -i0 to excuse an exempt user. Unset is NOT off, which the shipped template now says.

    THE POLL RUNS OFF THE INPUT PUMP, NOT THE PRESENT PATH, and that is the one decision here that had to be got right. Every sibling door polls per
    presented frame, which works because they present continuously. This one
    does not: SyncscummTermGraphicsManager calls present() only when the
    engine's own dirty flag says the frame CHANGED. A player who walks away at a static screen therefore produces no frames at all -- so a present-driven countdown would stall exactly when it is needed, which is the whole point of the feature. It rides termgfx_termio_pump(), the door's documented "call
    every poll" service point, and for the same reason the countdown paints
    itself the instant its text changes instead of waiting for a present. termgfx_toggle_stats() already used that trick for the stats bar on a static scene; this is the same problem.

    The input hook really is a single chokepoint here, unlike the two
    dispatchers syncdoom needed and the three syncduke needed. Nothing
    synthesizes key events on this path -- as termgfx_key_press()'s own comment notes, a legacy key is one KEY_DOWN and no release ever follows -- so termgfx_key_event() can be hooked directly without forging activity for a player who has left. Mouse reports are the only other real source, motion included, which matters for a point-and-click adventure that can go a long
    time between keystrokes.

    Input that answers the countdown is consumed rather than delivered, so
    "press any key" cannot also act in the game.

    -i is matched on an ALL-DIGIT suffix and marked consumed, so it never
    reaches the engine's argv. A prefix match would have been wrong for the
    usual reason: a game engine's own word option starting with -i (DOOM's -iwad
    is the cautionary case) must pass through untouched.

    Expiry sets g_quit, the existing clean-quit flag, rather than exiting here
    -- the engine then unwinds through its normal shutdown, saving whatever it saves, exactly as a player-initiated quit does. The log line is latched,
    since g_quit only ASKS the engine to stop and it takes several pump cycles
    to unwind; without that the same line landed three or four times per idle
    exit.

    Verified offline against the real door (-i fires to the millisecond, -i0
    never does, the ini alone works with no -i) and on a live node, where an instrumented run also confirmed the ini reaching the live process and no spurious wakes from the terminal's own chatter.

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

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