• ANSI attribute tracking: 256-color/24-bit and bright (90-107) SGR code

    From Rob Swindell@1:103/705 to GitLab issue in main/sbbs on Fri Oct 30 15:08:52 2026
    open https://gitlab.synchro.net/main/sbbs/-/work_items/1315

    `ANSI_Terminal::handle_SGR_sequence()` (`src/sbbs3/ansi_terminal.cpp`) tracks the terminal's current attribute (`term->curatr`) from every SGR sequence sent. It mishandles the extended color forms, so the tracked attribute drifts from what the terminal is actually showing:

    - **256-color and 24-bit color (`38;5;n`, `48;5;n`, `38;2;r;g;b`, `48;2;r;g;b`):** the parameters are walked one at a time with `get_pval()`, so the sub-parameters after 38/48 are treated as standalone SGR codes. For example, in `ESC[38;5;196m` the `5` sets BLINK (or BG_BRIGHT with iCE color). In `ESC[38;2;255;0;44m` the `0` resets the tracked attribute to `ANSI_NORMAL` and the `44` sets a blue background. cterm consumes these sub-parameters (`cterm_parse_extended_colour()`).
    - **Bright colors (`90`-`97`, `100`-`107`):** ignored, so the tracked foreground/background keeps its previous value. cterm implements them as the color plus bright (`set_bright_fg()`/`set_bright_bg()`): `9x` sets the foreground color and HIGH; `10x` sets the background color and the bright-background bit when iCE color is enabled.
    - **`2` (faint):** handled as "bold off". Less important, since terminals that don't do faint generally ignore it.

    ### Why it matters

    `attrstr()` computes each color change as a diff against the tracked attribute, so drift means later changes can omit codes that are needed (or send unnecessary ones). The most visible case is `pause()`, which saves `term->curatr` before printing its prompt and restores it afterwards (`getkey.cpp`). That is what lets a pause in the middle of ANSI art return to the art's colors after the "[Hit a key]" prompt. Art or message text that uses any of the codes above can come back from a pause in the wrong colors.

    ### Fix

    - Skip the sub-parameters of 38 and 48 the way cterm does: the 2 parameters of `5;n`, the 4 of `2;r;g;b`, or the single parameter when the rest are colon-separated (`38;5:n`, `38;2:r:g:b`; `get_pval()` already truncates at `:`, so `5:196` is currently read as `5`, i.e. blink). With no way to represent the exact extended color, mark the foreground/background unknown (`FG_UNKNOWN`/`BG_UNKNOWN`) so the next explicit color is always sent.
    - Track `90`-`97` as the corresponding color with `HIGH`, and `100`-`107` as the background color with `BG_BRIGHT` when the terminal is in iCE color mode, matching cterm.

    ### Related, lower priority

    After a raw `ESC[7m`, the tracker swaps the stored foreground/background (and `set_color()` swaps subsequent ones), but `attrstr()` emits colors unswapped alongside `7`. The two disagree until the next reset. Only reachable through raw ANSI, since only the PETSCII terminal sets `REVERSED` itself.

    *Authored by Claude (Claude Code), on behalf of @rswindell*
    --- SBBSecho 3.38-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)