^[control-codes live in libghostty-vt

Control sequences

XTMODKEYS Set Key Modifier Options

Choose how keys pressed with Shift, Control or Alt are sent, and ask what is chosen.

CSI > Pp ; Pv m
xterm
Functions using CSI

A key pressed with Shift, Control, Alt or Meta has to be sent somehow, and xterm has several ways. CSI > Pp ; Pv m sets resource Pp to Pv; CSI ? Pp m, XTQMODKEYS, asks for its value, and is answered as CSI > Pp ; Pv m. The > is what separates it from SGR.

PpResourceSays how to sendDefault
0modifyKeyboardthe keyboard as a whole0
1modifyCursorKeysthe arrow keys, Home and End2
2modifyFunctionKeysthe function keys2
3modifyKeypadKeysthe keypad0
4modifyOtherKeyseverything else, such as Control and ,0
6modifyModifierKeysthe modifier keys themselves0
7modifySpecialKeysother special keys0

For the cursor and function keys the value says where the modifier goes, -1 leaving it out and 0 to 3 putting it in a progressively more explicit place, so that Shift and Right arrow is CSI C, CSI 2 C, CSI 2 C, CSI 1 ; 2 C or CSI > 1 ; 2 C. modifyOtherKeys of 1 or 2 makes keys that normally send an ordinary character, or nothing, send CSI 27 ; modifier ; code ~ instead, so that a program can tell Control and , from ,; 2 does so for more keys than 1. Editors and shells set it to read key combinations a terminal otherwise loses (ptyx.h, modifyCursorKey).

The forms, in a default xterm (CASE_SET_MOD_FKEYS, set_mod_fkeys, report_mod_fkeys):

  • CSI > Pp ; Pv m sets one; CSI > Pp m, with no value, puts it back as it started; CSI > m puts back 1 to 5.
  • CSI > Pp n turns one off, setting it to -1; CSI > n turns off the function keys’.
  • CSI ? Pp m asks; several at once, CSI ? 1 ; 2 m, get an answer each. A value of -1 is reported as 65535, its sixteen bits read as unsigned, and so is a resource xterm does not have.
  • DECRQSS asks as well: DCS $ q > Pp m ST is answered DCS 1 $ r > Pp ; Pv m ST (misc.c).
  • DECSTR and RIS put them all back as they started (ReallyReset).

What a key sends cannot be seen without pressing it, but XTGETTCAP answers for a key by pretending to press it, so a case can ask it what Shift and Right arrow sends now.

libghostty-vt keeps only one thing from all this: whether modifyOtherKeys is 2, which its key encoder uses. It accepts CSI > 4 ; 2 m and turns that off on most other XTMODKEYS and on CSI > n, and ignores the rest. It does not answer XTQMODKEYS or the DECRQSS form. The cases follow a default xterm, so those that ask are known differences.

Validation

XTMODKEYS-1: The defaults

\e[?0;1;2;3;4m
|______|
reply \e[>0;0m\e[>1;2m\e[>2;2m\e[>3;0m\e[>4;0m

XTMODKEYS-2: Set modifyOtherKeys

\e[>4;2m
\e[?4m
|______|
reply \e[>4;2m

XTMODKEYS-3: Put it back

\e[>4;2m
\e[>4m
\e[?4m
|______|
reply \e[>4;0m

XTMODKEYS-4: Turned off

\e[>n     # the function keys
\e[?2m
|______|
reply \e[>2;65535m

XTMODKEYS-5: What Shift and Right arrow sends

\e[>1;3m
\eP+q6B524954\e\\  # kRIT: CSI > 1 ; 2 C
|______|
reply \eP1+r6B524954=1B5B3E313B3243\e\\

XTMODKEYS-6: Through DECRQSS

\eP$q>1m\e\\
|______|
reply \eP1$r>1;2m\e\\

XTMODKEYS-7: Not SGR

\e[>4;2m
A
|A_____|
attr 1,1 plain
reply none
Every example on these pages runs in your browser, in libghostty-vt compiled to WebAssembly.