# Restore DEC Private Mode Values (XTRESTORE)

> Put back private modes saved with XTSAVE.

- **Sequence:** `CSI ? Pm r`
- **xterm:** [Functions using CSI](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)

`CSI ? Pm r` sets each private mode it names back to the state
[XTSAVE](https://control-codes.page/csi/xtsave/index.html.md), `CSI ? Pm s`, saved for it. Restoring a mode is the
same as setting or resetting it with [DECSET](https://control-codes.page/csi/decset/index.html.md) or
[DECRST](https://control-codes.page/csi/decrst/index.html.md), side effects included: restoring
[origin mode](https://control-codes.page/modes/decom/index.html.md) moves the cursor home, as setting it does. The
saved state stays where it is, so the same mode can be restored again.

Only xterm defines it: DEC's terminals had nothing like it, and none of
 ECMA-48, DEC STD 070 or the VT220 and VT510 manuals mentions it. Its
parameters are [private mode](https://control-codes.page/modes/index.html.md) numbers, the same as
[DECSET](https://control-codes.page/csi/decset/index.html.md)'s, and the `?` is what keeps it apart from another
sequence with the same final byte. Without the `?`, `CSI r` is [DECSTBM](https://control-codes.page/csi/decstbm/index.html.md).

xterm restores each mode in turn in
[`restoremodes`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L8405-L8848) and skips a number it does not
know, and so does libghostty-vt. They part over a mode that was never saved.
xterm keeps its saved states in a table that starts out zeroed
([`charproc.c`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L10844), when the terminal is created), so
restoring a mode it never saved resets it, whatever its default: restoring
autowrap turns it off ([`charproc.c`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L8464-L8467)), and
restoring mode 25 hides the cursor. Nothing but XTSAVE writes that table, so
a reset, [RIS](https://control-codes.page/esc/ris/index.html.md), leaves what was saved in place. libghostty-vt
starts its saved states at each mode's default instead, so restoring an
unsaved mode puts the default back, and RIS returns every saved state to
the default. Those cases are known differences.

## Validation

### XTRESTORE-1: The saved state comes back

Input, one step per line:

```text
\e[?7l
\e[?7s     # off
\e[?7h
\e[?7r
ABCDEFG
```

Expected screen:

```text
|ABCDG|
|_____|
cursor 1,5
```

### XTRESTORE-2: It can be restored again

Input, one step per line:

```text
\e[?7l
\e[?7s
\e[?7h
\e[?7r
\e[?7h
\e[?7r     # still off
ABCDEFG
```

Expected screen:

```text
|ABCDG|
|_____|
cursor 1,5
```

### XTRESTORE-3: A mode that is not known does not stop the others

Input, one step per line:

```text
\e[?7s
\e[?7l
\e[?9999;7r
ABCDEFG
```

Expected screen:

```text
|ABCDE|
|FG___|
cursor 2,3
```

### XTRESTORE-4: Restoring origin mode moves the cursor home

Input, one step per line:

```text
\e[?6s
\e[3;5H
\e[?6r
X
```

Expected screen:

```text
|X____|
|_____|
|_____|
cursor 1,2
```

### XTRESTORE-5: A mode never saved is reset

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

Input, one step per line:

```text
\e[?7r     # autowrap was never saved
ABCDEFG
```

Expected screen:

```text
|ABCDG|
|_____|
cursor 1,5
```

### XTRESTORE-6: The cursor too

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

Input, one step per line:

```text
\e[?25r
\e[?25$p
```

Expected screen:

```text
|_____|
reply \e[?25;2$y
```

### XTRESTORE-7: A reset leaves what was saved

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

Input, one step per line:

```text
\e[?7l
\e[?7s
\ec        # RIS: autowrap on again
\e[?7r
ABCDEFG
```

Expected screen:

```text
|ABCDG|
|_____|
cursor 1,5
```

---

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