# Alternate Screen (?47, ?1047, ?1048, ?1049)

> Switch to a second screen for full-screen programs, and back to the one the shell was using.

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

A terminal keeps two screens. The *normal* one is where the shell runs and
has the scrollback; the *alternate* one has no scrollback, and a full-screen
program such as an editor or a pager switches to it on starting and back on
leaving, so that the shell's screen reappears as it was. None of this is
DEC's: it is xterm's, and these modes are xterm's numbers in DEC's private
space.

| Mode | Set | Reset |
| --- | --- | --- |
| `47` | switch to the alternate screen | switch to the normal screen |
| `1047` | switch to the alternate screen | clear the alternate screen, then switch to the normal screen |
| `1048` | save the cursor, as [DECSC](https://control-codes.page/esc/decsc/index.html.md) | restore the cursor, as DECRC |
| `1049` | save the cursor, switch to the alternate screen, and clear it | switch to the normal screen, and restore the cursor |
| `1046` | allow the switch | forbid it, and switch to the normal screen |

`1049` is the one programs use: it is what the `smcup` and `rmcup`
capabilities of most terminfo entries for xterm-like terminals send.

The cases follow xterm
([`dpmodes`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L7732-L7771) and [L7906-L7922](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L7906-L7922),
[`ToAlternate`, `FromAlternate`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L9529-L9561)):

- The switch itself moves nothing. The cursor stays where it was, and so do
  the character attributes, unless `1048` or `1049` saves and restores
  them.
- What is on the alternate screen stays there between visits, unless
  `1047` clears it on the way out or `1049` on the way in.
- Each screen has its own saved cursor, for DECSC and DECRC as much as for
  these modes. `1049` saves into the slot of the screen it was on when it
  was set, and restores from the normal screen's
  ([`CursorSave`, `CursorRestore`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/cursor.c#L419-L522)).
- [RIS](https://control-codes.page/esc/ris/index.html.md) goes back to the normal screen.

DECRQM is answered with xterm's own state rather than each mode's
([`do_dec_rqm`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/misc.c#L5610-L5619),
[L5713-L5718](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/misc.c#L5713-L5718)). `47`, `1047` and `1049` all
report whether the alternate screen is showing, whichever of them switched
to it. `1048` reports whether the current screen has a saved cursor at all.
`1046` reports the flag behind it, which is set when switching is
*forbidden*, so it answers the opposite of the mode's own sense: reset after
`CSI ? 1046 h`. The cases expect all of this, bugs included.

libghostty-vt does the switching as xterm does, and has the same per-screen
saved cursor. Its DECRQM answers each mode from its own flag, and it does
not know mode `1046` at all, so the cases about those are known
differences.

## Validation

### ALT-1: The alternate screen starts blank

Input, one step per line:

```text
AB
\e[?47h
```

Expected screen:

```text
|____|
|____|
cursor 1,3
```

### ALT-2: And the normal screen comes back as it was

Input, one step per line:

```text
AB
\e[?47h
X
\e[?47l
```

Expected screen:

```text
|AB__|
|____|
cursor 1,4
```

### ALT-3: The cursor stays where it was

Input, one step per line:

```text
AB
\e[2;2H
\e[?47h
```

Expected screen:

```text
|____|
|____|
cursor 2,2
```

### ALT-4: 47 keeps the alternate screen between visits

Input, one step per line:

```text
AB
\e[?47h
X
\e[?47l
\e[?47h   # back again
```

Expected screen:

```text
|__X_|
|____|
cursor 1,4
```

### ALT-5: 1047 clears it on the way out

Input, one step per line:

```text
AB
\e[?1047h
X
\e[?1047l
\e[?1047h
```

Expected screen:

```text
|____|
|____|
cursor 1,4
```

### ALT-6: But not on the way in

Input, one step per line:

```text
AB
\e[?47h
X
\e[?47l
\e[?1047h
```

Expected screen:

```text
|__X_|
|____|
cursor 1,4
```

### ALT-7: 1049 clears it on the way in

Input, one step per line:

```text
AB
\e[?47h
X
\e[?47l
\e[?1049h
```

Expected screen:

```text
|____|
|____|
cursor 1,4
```

### ALT-8: And restores the cursor on the way out

Input, one step per line:

```text
AB
\e[?1049h
\e[2;4H
X
\e[?1049l
```

Expected screen:

```text
|AB__|
|____|
cursor 1,3
```

### ALT-9: 1049 saves the character attributes too

Input, one step per line:

```text
\e[1m     # bold
\e[?1049h
\e[0m
\e[?1049l
B
```

Expected screen:

```text
|B___|
|____|
attr 1,1 bold
```

### ALT-10: Resetting 1049 on the normal screen still restores

Input, one step per line:

```text
\e[2;3H
\e7
\e[1;1H
\e[?1049l
```

Expected screen:

```text
|____|
|____|
cursor 2,3
```

### ALT-11: Each screen has its own saved cursor

Input, one step per line:

```text
\e[2;2H
\e7       # saved for the normal screen
\e[?47h
\e[2;4H
\e7       # saved for the alternate screen
\e[?47l
\e[1;1H
\e8
```

Expected screen:

```text
|____|
|____|
cursor 2,2
```

### ALT-12: Nothing saved on the alternate screen

Input, one step per line:

```text
\e[2;2H
\e7
\e[?47h
\e[1;4H
\e8       # the alternate screen has nothing saved: home
```

Expected screen:

```text
|____|
|____|
cursor 1,1
```

### ALT-13: 1049 set on the alternate screen

Input, one step per line:

```text
AB
\e[?47h
X
\e[?1049h # saves into the alternate screen's slot
\e[?1049l # restores from the normal screen's: nothing there
```

Expected screen:

```text
|AB__|
|____|
cursor 1,1
```

### ALT-14: 1048 is DECSC and DECRC

Input, one step per line:

```text
\e[2;3H
\e[?1048h
\e[1;1H
\e[?1048l
```

Expected screen:

```text
|____|
|____|
cursor 2,3
```

### ALT-15: DECRQM reports the screen, not the mode

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

Input, one step per line:

```text
\e[?1049h
\e[?47$p
\e[?1047$p
\e[?1049$p
```

Expected screen:

```text
|____|
|____|
reply \e[?47;1$y\e[?1047;1$y\e[?1049;1$y
```

### ALT-16: 1048 reports a saved cursor

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

Input, one step per line:

```text
\e7
\e[?1048$p
```

Expected screen:

```text
|____|
|____|
reply \e[?1048;1$y
```

### ALT-17: 1046 answers backwards

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

Input, one step per line:

```text
\e[?1046h # allow switching, as it already is
\e[?1046$p
```

Expected screen:

```text
|____|
|____|
reply \e[?1046;2$y
```

### ALT-18: Switching forbidden

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

Input, one step per line:

```text
AB
\e[?1046l
\e[?47h   # ignored
X
```

Expected screen:

```text
|ABX_|
|____|
```

### ALT-19: RIS goes back to the normal screen

Input, one step per line:

```text
AB
\e[?47h
\ec
\e[?47$p
```

Expected screen:

```text
|____|
|____|
cursor 1,1
reply \e[?47;2$y
```

---

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