• src/doors/termgfx/keymode

    From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Thursday, July 09, 2026 01:55:00
    https://gitlab.synchro.net/main/sbbs/-/commit/cf67bccb2a7bf5111196bd0e
    Added Files:
    src/doors/termgfx/keymode.c keymode.h
    Modified Files:
    src/doors/syncretro/syncretro_input.c src/doors/termgfx/CMakeLists.txt Log Message:
    termgfx: hoist the terminal key-mode negotiation into keymode.c

    SyncDOOM, SyncDuke, SyncConquer and SyncRetro each carried their own copy
    of the same keyboard negotiation: enable SyncTERM physical key reports
    when the CTDA reply advertises cap 8, else push the kitty keyboard flags
    when the terminal answers the CSI?u query, else fall back to the byte
    path; undo whichever was enabled on the way out. Four copies of the evdev->ASCII table (byte-for-byte identical), three of the kitty CSI-u parameter decoder, and an evdev/kitty active-flag pair referenced 8 to 28
    times per door.

    The copies had already drifted, which is the argument for the module:

    * SyncConquer never armed the enable-time settle window. SyncTERM
    re-reports the keys held at the instant physical key reports are
    enabled -- e.g. the Enter still down from picking the door off the
    BBS menu -- and acting on those presses a button before the game has
    drawn a frame. The other three drop press edges for 500ms.

    * SyncRetro's modifier decode recognized only the left-hand Ctrl and
    Alt keycodes, so right Ctrl and right Alt were read as ordinary keys.

    Both are fixed by construction here: there is one settle window and one modifier classifier.

    keymode.h follows the caps.h/term.h convention -- I/O-free. It owns the
    state and builds the byte sequences; the door emits them through its own
    output path and keeps the only genuinely per-game part, the map from a
    key to that game's action. termgfx_keymode_restore() emits only what was actually enabled, so a door can call it unconditionally from its leave
    path, and it is idempotent.

    This commit adds the module and moves SyncRetro onto it. The three
    shipped doors follow in their own commits so each stays bisectable.

    Unit-tested (enable/restore/idempotence, evdev-wins precedence both ways,
    the settle window across a uint32 millisecond clock wrap, the kitty
    event-type and modifier fields, the evdev tables); SyncRetro re-verified against its fake-terminal harness on all three key paths.

    ---
    þ 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 Saturday, July 11, 2026 04:50:00
    https://gitlab.synchro.net/main/sbbs/-/commit/83c51c67802d86a57a9ec4da
    Modified Files:
    src/doors/termgfx/keymode.c
    Log Message:
    termgfx: map the numeric keypad in evdev key mode

    The evdev keycode -> ASCII table stopped at code 57, so the numeric
    keypad (numlock-on codes 71-83, plus 96 and 98) returned 0 and did
    nothing in evdev mode (SyncTERM) -- notably numpad Enter (code 96) would
    not select a menu item, though the main Enter (28) worked. The kitty
    path already mapped the keypad (PUA 57399..57414 -> "0123456789./*-+\r").

    Map those keypad codes to the same ASCII in termgfx_evdev_ascii() so
    evdev and kitty clients behave identically. Purely additive for the
    three doors that link termgfx (SyncConquer, SyncDuke, SyncDOOM): those
    keycodes produced nothing before.

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

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