# User Defined Keys (DECUDK)

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

- **Sequence:** `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](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n975/mode/1up)
- **VT220:** [§4.15 User Defined Keys (DECUDK)](https://vt100.net/docs/vt220-rm/chapter4.html#S4.15)
- **VT510:** [DECUDK—User Defined Keys](https://vt100.net/docs/vt510-rm/DECUDK.html)

DECUDK is a [device control string](https://control-codes.page/esc/dcs/index.html.md) 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](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n977/mode/1up):

| Parameter | `0`, or left out | `1` |
| --- | --- | --- |
| `Pc`, clear (DEC STD 070 calls it `Pe`) | clear every key before loading | clear only the keys being loaded |
| `Pl`, lock | lock the keys against being loaded again | leave 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](https://control-codes.page/csi/decdsr/index.html.md) `CSI ? 25 n`, which answers
`CSI ? 20 n` for unlocked and `CSI ? 21 n` for locked
([p. 11-7](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n973/mode/1up)).
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`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/misc.c#L5367-L5373),
[`parse_decudk`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/misc.c#L4396-L4437)).
It never looks at `Pl`, so the keys are never locked, and its answer to
`CSI ? 25 n` is always the same
([`charproc.c`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L4655-L4660)):

```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](https://control-codes.page/csi/decdsr/index.html.md)), which is why DECUDK-2 fails.

## Validation

### DECUDK-1: The definitions are not displayed

Input, one step per line:

```text
\eP0;1|17/414243;18/444546\e\\   # F6 sends ABC, F7 sends DEF
X
```

Expected screen:

```text
|X____|
cursor 1,2
reply none
```

### DECUDK-2: xterm does not lock the keys

*A known difference: libghostty-vt does not pass this case.*

Input, one step per line:

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

Expected screen:

```text
|_____|
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.

---

This is the Markdown version of <https://control-codes.page/dcs/decudk/>. On that page every validation case runs live in libghostty-vt, the terminal emulation core of Ghostty, compiled to WebAssembly.

The validation cases are written in the notation described in <https://control-codes.page/notation/index.html.md>.
