lib-vt: answer XTGETTCAP queries

Ghostty's full termio path answers XTGETTCAP from the static terminfo
map, but terminal/stream_terminal.zig, which backs libghostty-vt,
parses the same DCS request and then discards it. There is no XTGETTCAP
effect either, so an embedder cannot restore the replies through the
C API.

Programs query these over SSH instead of assuming the remote host has
the client's terminfo entry. This matters more for an embedder than for
the desktop app, which can install its entry on the remote through
shell integration.

Answer the queries in stream_terminal the same way termio does: look
up each requested key in the static terminfo map and write the reply
to the pty, skipping the lookups entirely when no write_pty effect is
set. The map now stores null-terminated responses so they can be
handed straight to write_pty without copying. terminal/dcs.zig and the
termio path are unchanged.

"TN" is handled separately. It names the terminfo entry the terminal
runs as, so it has to agree with TERM -- which is set in
termio/Exec.zig, a layer libghostty-vt does not contain. The library
never sees TERM and cannot answer on the embedder's behalf, and
answering with Ghostty's own entry from the static map would misreport
every embedder, so "TN" is intercepted before the map lookup. The name
is instead configured through a new option,
GHOSTTY_TERMINAL_OPT_TERMINFO_NAME: the string is copied into the
terminal, names longer than 128 bytes are rejected, and while unset
the query goes unanswered.

This is the first dependency from src/terminal on src/terminfo, so
libghostty-vt now carries Ghostty's terminfo table: +16,023 bytes
(+1.9%) on a wasm32-freestanding ReleaseSmall build.

Signed-off-by: Fredrik Fornwall <fredrik@fornwall.net>
This commit is contained in:
Fredrik Fornwall
2026-08-09 02:59:37 +02:00
committed by Mitchell Hashimoto
parent 163e229756
commit 74efadb446
4 changed files with 329 additions and 21 deletions

View File

@@ -1210,6 +1210,22 @@ typedef enum GHOSTTY_ENUM_TYPED {
* Input type: size_t*
*/
GHOSTTY_TERMINAL_OPT_UNKNOWN_MAX_BYTES = 36,
/**
* Set the name of the terminfo entry this terminal runs as, reported
* in response to an XTGETTCAP query for "TN" (e.g. "xterm-256color").
*
* The string data is copied into the terminal. A NULL value pointer
* clears the name (equivalent to setting an empty string). A name
* longer than 128 bytes returns GHOSTTY_INVALID_VALUE.
*
* If this is unset then we don't report anything for an XTGETTCAP
* TN query, because we don't know what the embedding terminal around
* libghostty is advertising itself as.
*
* Input type: GhosttyString*
*/
GHOSTTY_TERMINAL_OPT_TERMINFO_NAME = 37,
GHOSTTY_TERMINAL_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyTerminalOption;