^[control-codes live in libghostty-vt

Device control strings

DECUDK User Defined Keys

Load the strings the function keys send, and optionally lock them against being loaded again.

DCS Pc ; Pl | Ky1/St1 ; … ; Kyn/Stn ST
Defaults
Pc = 0, clear all keys first; Pl = 0, lock the keys
DEC STD 070
§11.3 User Defined Keys, p. 11-9
VT510
DECUDK—User Defined Keys

DECUDK is a device control string that tells the terminal what its shifted function keys should send. Each definition in the string is a key number, a /, and the string the key sends, written as pairs of hexadecimal digits: 17/414243 makes F6 send ABC. Definitions are separated by ;.

The two parameters decide what happens to the keys already defined. DEC STD 070 describes them on p. 11-11:

Parameter0, or left out1
Pc, clear (DEC STD 070 calls it Pe)clear every key before loadingclear only the keys being loaded
Pl, locklock the keys against being loaded againleave them unlocked

Locking “causes all subsequent DECUDK sequences to be ignored (until the lock is cleared by the terminal user under local control)”. The host can ask whether the keys are locked with the DEC device status report CSI ? 25 n, which answers CSI ? 20 n for unlocked and CSI ? 21 n for locked (p. 11-7). DECUDK is defined for level 2 and above.

xterm implements DECUDK at level 2 and above, which takes in its default level 4. It clears the keys when Pc is 0, and loads the definitions (misc.c, parse_decudk). It never looks at Pl, so the keys are never locked, and its answer to CSI ? 25 n is always the same (charproc.c):

reply.a_param[count++] = 20;	/* UDK always unlocked */

The cases follow xterm, so a lock asked for with Pl 0 is not taken.

What a key sends once it is defined is keyboard input, which the case runner cannot press, so the cases check only that the string is consumed and what the terminal reports afterwards.

Not in libghostty-vt

libghostty-vt has no user-defined keys. Its DCS handler in dcs.zig recognizes tmux control mode, XTGETTCAP (DCS + q) and DECRQSS (DCS $ q), and drops every other device control string, DECUDK included, without acting on it. It does not answer CSI ? 25 n at all (see DECDSR), which is why DECUDK-2 fails.

Validation

DECUDK-1: The definitions are not displayed

\eP0;1|17/414243;18/444546\e\\   # F6 sends ABC, F7 sends DEF
X
|X____|
cursor 1,2
reply none

DECUDK-2: xterm does not lock the keys

\eP1;0|17/414243\e\\   # Pl 0 asks for the keys to be locked
\e[?25n                # are they?
|_____|
reply \e[?20n

xterm answers that the keys are unlocked, as it always does. DEC STD 070 would lock them and answer CSI ? 21 n. libghostty-vt sends nothing.

Every example on these pages runs in your browser, in libghostty-vt compiled to WebAssembly.