Skip to content

Report the terminal color scheme via CSI 996 and Mode 2031 - #659

Merged
dakra merged 3 commits into
mainfrom
color-scheme-report
Aug 30, 2026
Merged

dakra merged 3 commits into
mainfrom
color-scheme-report

Conversation

@dakra

@dakra dakra commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Supersedes #657 by @ayanamists — the first commit is that PR squashed verbatim; the second reworks it.

What

  • Answer CSI ? 996 n with CSI ? 997 ; 1 n (dark) / ; 2 n (light).
  • Push an unsolicited CSI ? 997 n to clients that enabled Mode 2031 when the classification changes (theme change, disable-theme, ghostel-sync-theme).
  • A child's own OSC 11 override never affects the answer.

Changes relative to #657

  • Light/dark comes from Emacs, not a luma cutoff. Every OSC 11 consumer uses a different heuristic (Neovim BT.601 ≥ 0.5, Emacs TUI unweighted average < 0.6, Emacs GUI color-dark-p), so the terminal reports what Emacs itself considers dark: color-dark-p on the ghostel-default background, passed to the module as a 4th argument of ghostel--set-default-colors. #808080 is dark, as in Emacs. The Zig classifier and its (never compiled) unit test are gone.
  • 997 only on an actual change. The PR pushed on every set-default-colors, and load-theme fires enable-theme-functions twice (user pseudo-theme + theme), so every theme change produced two identical reports.
  • PTY write outside the terminal lock, and best effort. Writing under term_mutex could stall the reader thread on a full PTY; a write error no longer escapes the theme hook after the scheme is committed.
  • disable-theme re-syncs; ghostel-sync-theme skips buffers killed by re-entrant sentinels.
  • Tests: the 996 query is sent through the child so the native reader-thread path is exercised; the recorder no longer truncates, so duplicate reports fail; 2031 test asserts one push per change across three re-seeds. The PR's ConPTY port of the recorder script is kept, with the reader/deadline loop shared by the recorder, reply-probe and raw-echo scripts via test/fixtures/pty_setup.py.

Verified live (both PTY backends)

996 answers under modus-vivendi/operandi and mid-grey backgrounds, exactly one 997 per light↔dark switch, per-buffer 2031 gating, dead-process write silent, OSC 11 override ignored, RIS keeps the scheme, OSC 10/11/4 palette sync unchanged.

Not addressed here

The pinned libghostty's OSC 111 (reset background) copies default into override, so after a child resets, OSC 11;? returns the stale colour across theme switches (fixed upstream). This — not 997, which Neovim 0.12 ignores — is what sets nvim's &background; needs a ghostty bump.

Reloading an already-active theme emits two transient 997s (disable → enable); accepted as harmless.

ayanamists and others added 2 commits August 30, 2026 00:36
libghostty parses CSI ? 996 n and tracks DEC Mode 2031, but ghostel
provided no color-scheme callback and never pushed CSI ? 997 n, so
multiplexers that cache OSC 11 colors kept stale values after an Emacs
theme change.

Answer 996 from the seeded default background (BT.601 luma, 0.5
midpoint) and push 997 from ghostel--set-default-colors when Mode 2031
is enabled.

The PTY byte recorder test helper drops the POSIX-only tty and select
modules so the new tests also run under ConPTY.
Report the scheme Emacs assigns to the ghostel-default background
instead of a BT.601 luma cutoff: every OSC 11 consumer (Neovim, Emacs
TUI, Emacs GUI) uses a different heuristic, so the terminal's own
answer should match what Emacs is showing.  The classification is
stored on the terminal (the native process reads it through a pointer
for the reader-thread 996 answer) and a 997 is pushed only when it
changes, which also stops the double report per load-theme from
enable-theme-functions firing for the `user' pseudo-theme.

The 997 write happens after the terminal lock is released so a full
PTY cannot stall the reader thread, and is best effort: a client can
re-query with 996.  disable-theme now re-syncs too, and
ghostel-sync-theme skips buffers killed by re-entrant sentinels.

The recorder, reply-probe and raw-echo test scripts share one
reader/deadline loop in pty_setup.py; the recorder no longer truncates
at the requested byte count, so duplicated reports fail the tests.
@dakra
dakra requested a review from emil-e August 30, 2026 10:37
Move the terminal mutex to GhostelTerm and make NativeProcess borrow its owner. Separate handler terminal state from transport effects and lock terminal-dependent input encoding consistently.
@dakra
dakra merged commit da0b5c7 into main Aug 30, 2026
33 checks passed
@dakra
dakra deleted the color-scheme-report branch August 30, 2026 21:09

This branch was previously deployed

1 inactive deployment
github-pages — da0b5c77 Deployed Aug 30, 2026 by dakra via deploy #177
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants