TermBuf::EnvironmentDetector
Stability: internal
Guesses what a terminal can do from the environment it was started in.
This runs before anything is written to the terminal, and it is all a non-interactive session ever gets. It is deliberately pessimistic: a name nobody recognises comes back with nothing, because a screen full of escape sequences is worse than plain text.
Probing runs afterwards and overrides whatever this decided, so being wrong here costs accuracy only where a terminal cannot be asked directly.
Constants
The Terminal.app that shipped with macOS Tahoe. Earlier ones take a 24 bit colour and quantize it to the palette.
What Terminal.app does and never admits to.
It answers no DECRQM, no DECRQSS and no XTGETTCAP, so nothing about these
four can be asked and the table was the only evidence there was — and the
table said no to all four. Watched on 2026-09-10 against 470.2: mode 1004
sends a focus report on the way out and back in, mode 1006 reports a
click, OSC 2 renames the window, and DECSCUSR changes the cursor's shape.
See measurements/CAPS.md.
Blinking text, which some current terminals parse and then ignore.
There is no query for this and terminfo is no help: ghostty's own entry
declares blink=\E[5m and nothing on screen blinks. An attribute that
is inert rather than harmful is exactly the kind nobody bothers to
report accurately, so the only evidence is having looked.
Writing the system clipboard with OSC 52.
A table entry rather than a measurement: OSC 52 answers nothing on a
write, so there is no query and no probe, only the terminal's
documentation and its source. Kitty, ghostty, WezTerm and foot all
document the write, and all four ship it enabled; xterm supports it and
ships it off, which is indistinguishable from not having it, so it
stays out. TERMBUF_CAPS=+osc52_clipboard covers a terminal that has it
and is not named here.
The kitty colour stack, which ghostty parses and then does nothing with.
There is no query for this either: CSI # R goes unanswered. The only
evidence is having watched it, and it was — against ghostty 1.3.2, an OSC
11 read back after a push, a set, and a pop gives the value that was set,
not the one that was pushed. Without a working pop a colour change cannot
be given back, which is the whole reason ColorStack is gated on this
flag; claiming it wrongly leaves a terminal recoloured after the program
has exited.
Capabilities a name is known not to imply, however much the rest of the environment says otherwise.
Separate from the pattern tables because composition is additive: under
ghostty with a TERM of xterm-256color, the xterm entry would put
blink back after the ghostty entry left it out, and the 256color
pattern puts strike-through back on Terminal.app. A name that identifies
the terminal outranks one that only describes a family.
foot, a current terminal with no protocols borrowed from kitty.
A current terminal that neither blinks nor keeps colours for you.
Capabilities.normalize drops the rapid variant along with the slow one,
so taking both off here is belt and braces.
kitty, whose protocols the other current terminals borrowed, less the two attributes it does not draw.
What kitty and its imitators add on top of a current terminal.
What kitty does not draw.
Measured against 0.48.2: SGR 53 leaves the text unmarked where SGR 4
underlines it, and SGR 8 leaves it visible. Kitty's own terminfo agrees,
declaring blink and smxx and neither an overline nor invis — where
ghostty's declares invis=\E[8m. Ghostty, WezTerm and foot draw both, so
they stay in Capabilities::MODERN and come off the one terminal by name.
Set by a terminal that has no TERM_PROGRAM of its own, or as well as
one. Each carries the name of the terminal that sets it, so a marker
inherited from whatever opened the window can be told from one the
terminal reading the output set itself.
Capabilities a multiplexer answers a mode report for on its own account.
tmux 3.7c answers ?1004;1$y because it implements mode 1004; whether
a focus report reaches the application past it is a question about its
options rather than about the mode, and the answer measured was no. So a
present report for 1004 is evidence about the multiplexer and not about
the terminal, and Prober records that it was answered without letting
it add the capability back.
Mode 1006 is not here any more: tmux answers ?1006;1$y and forwards
the report as well, so its yes is worth what it says. screen answers no
DECRQM at all, so nothing about it turns on this list either way.
An absent report is still trusted, and so are the other three modes:
2026, 2027 and 2004 are handled by the multiplexer itself rather than
forwarded, and the measurement agrees that synchronized output through
tmux works.
Matched against TERM_PROGRAM. These beat the TERM guess, since the
terminal names itself here rather than naming its terminfo entry.
Terminals that get a grapheme cluster's column count wrong, matched
against the terminal that names itself the way PROGRAM_PATTERNS is. A
terminal has to be measured before it goes in here; see Quirk.
Matched against TERM, most specific first.
A multiplexer sits between the application and the terminal and does not forward everything. The kitty protocols in particular are swallowed, so they come off until a probe says otherwise.
Focus reports and the window title come off for a different reason: both
depend on how the multiplexer was configured, and both default to off.
Watched on 2026-09-10 — under tmux 3.7c with no config no focus report
reached the application and the title was never set, because
focus-events and set-titles ship off; under GNU screen, 4.00.03
sent neither and 5.0.2 sent the title and not the focus. A configuration
that does forward them says so with
TERMBUF_CAPS=+focus_events,+titles.
The mouse is not on this list, because tmux forwards a click: mode
1002 with SGR encoding reported one through 3.7c with no config, where
the first round asked with mode 1000 and saw nothing. THROUGH_SCREEN
takes it off under screen alone.
CursorShape stays: DECSCUSR reached the terminal through tmux and
through screen 5.0.2, and screen 4.00.03 swallowing it costs nothing
visible. See measurements/CAPS.md.
What GNU screen costs on top of THROUGH_MULTIPLEXER.
4.00.03 delivered no mouse report at all on 2026-09-10, under mode 1002
and the SGR encoding, where tmux delivered one. 5.0.2 did deliver one,
and nothing in the environment tells 4 from 5 — both set TERM=screen,
both pass TERM_PROGRAM through from the terminal underneath, and
neither answers XTVERSION — so the worse of the two is assumed and
TERMBUF_CAPS=+mouse_sgr is how the better one says what it is.
WezTerm, which took kitty's graphics protocol and not the rest of it.
Instance methods
Capabilities a name is known not to imply, however much the rest of the environment says otherwise.
Separate from the pattern tables because composition is additive: under
ghostty with a TERM of xterm-256color, the xterm entry would put
blink back after the ghostty entry left it out, and the 256color
pattern puts strike-through back on Terminal.app. A name that identifies
the terminal outranks one that only describes a family.
Guesses from TERM, TERM_PROGRAM, COLORTERM, VTE_VERSION, and the
marker variables terminals set for themselves.
Which mode reports this environment cannot take at face value: what a
multiplexer answers for itself, and under screen what THROUGH_SCREEN
took off as well. Prober takes this and adds no capability for a mode
report that says yes to one of them.
screen answers no DECRQM of its own, so a yes for 1006 under it came
from the terminal behind it and is about a click screen 4.00.03 does
not forward. What the tables take off for screen, a mode report does
not give back.