# Key Modifier Options (XTMODKEYS, XTQMODKEYS)

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

- **Sequence:** `CSI > Pp ; Pv m`
- **xterm:** [Functions using CSI](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)

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](https://control-codes.page/csi/sgr/index.html.md).

| `Pp` | Resource | Says how to send | Default |
| --- | --- | --- | --- |
| `0` | `modifyKeyboard` | the keyboard as a whole | `0` |
| `1` | `modifyCursorKeys` | the arrow keys, Home and End | `2` |
| `2` | `modifyFunctionKeys` | the function keys | `2` |
| `3` | `modifyKeypadKeys` | the keypad | `0` |
| `4` | `modifyOtherKeys` | everything else, such as Control and `,` | `0` |
| `6` | `modifyModifierKeys` | the modifier keys themselves | `0` |
| `7` | `modifySpecialKeys` | other special keys | `0` |

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`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/ptyx.h#L3384-L3409),
[`modifyCursorKey`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/input.c#L785-L801)).

The forms, in a default xterm
([`CASE_SET_MOD_FKEYS`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L6343-L6382),
[`set_mod_fkeys`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L2378-L2424),
[`report_mod_fkeys`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L2468-L2507)):

- `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](https://control-codes.page/dcs/decrqss/index.html.md) asks as well: `DCS $ q > Pp m ST` is answered
  `DCS 1 $ r > Pp ; Pv m ST`
  ([`misc.c`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/misc.c#L5062-L5105)).
- DECSTR and RIS put them all back as they started
  ([`ReallyReset`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L14420)).

What a key sends cannot be seen without pressing it, but
[XTGETTCAP](https://control-codes.page/dcs/xtgettcap/index.html.md) 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

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

Input, one step per line:

```text
\e[?0;1;2;3;4m
```

Expected screen:

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

### XTMODKEYS-2: Set modifyOtherKeys

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

Input, one step per line:

```text
\e[>4;2m
\e[?4m
```

Expected screen:

```text
|______|
reply \e[>4;2m
```

### XTMODKEYS-3: Put it back

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

Input, one step per line:

```text
\e[>4;2m
\e[>4m
\e[?4m
```

Expected screen:

```text
|______|
reply \e[>4;0m
```

### XTMODKEYS-4: Turned off

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

Input, one step per line:

```text
\e[>n     # the function keys
\e[?2m
```

Expected screen:

```text
|______|
reply \e[>2;65535m
```

### XTMODKEYS-5: What Shift and Right arrow sends

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

Input, one step per line:

```text
\e[>1;3m
\eP+q6B524954\e\\  # kRIT: CSI > 1 ; 2 C
```

Expected screen:

```text
|______|
reply \eP1+r6B524954=1B5B3E313B3243\e\\
```

### XTMODKEYS-6: Through DECRQSS

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

Input, one step per line:

```text
\eP$q>1m\e\\
```

Expected screen:

```text
|______|
reply \eP1$r>1;2m\e\\
```

### XTMODKEYS-7: Not SGR

Input, one step per line:

```text
\e[>4;2m
A
```

Expected screen:

```text
|A_____|
attr 1,1 plain
reply none
```

---

This is the Markdown version of <https://control-codes.page/csi/xtmodkeys/>. 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>.
