^[control-codes
live in libghostty-vt

Private modes

?47, ?1047, ?1048, ?1049 Alternate Screen

View as Markdown

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

?1049 Alternate Screen

CSI ? 1049 h/l

?47 Alternate Screen

CSI ? 47 h/l

?1046 Allow Alternate Screen

CSI ? 1046 h/l

?1047 Alternate Screen, Cleared on Leaving

CSI ? 1047 h/l

?1048 Save Cursor

CSI ? 1048 h/l
xterm
Functions using CSI

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.

ModeSetReset
47switch to the alternate screenswitch to the normal screen
1047switch to the alternate screenclear the alternate screen, then switch to the normal screen
1048save the cursor, as DECSCrestore the cursor, as DECRC
1049save the cursor, switch to the alternate screen, and clear itswitch to the normal screen, and restore the cursor
1046allow the switchforbid 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 and L7906-L7922, ToAlternate, FromAlternate):

  • 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).
  • RIS goes back to the normal screen.

DECRQM is answered with xterm’s own state rather than each mode’s (do_dec_rqm, 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.

Support

TerminalVersionSupportNotes
VT220manual, 2nd ed., 1984Nonot in the manual
VT510manual, 1st ed., 1993Nonot in the manual; not in Table 5-6
xtermpatch 412, xterm-411aYes
libghostty-vt0f171f6Partlyno mode 1046 (ALT-17, ALT-18); DECRQM answers each mode’s own flag (ALT-15, ALT-16)
kitty0.49.1, run in a VMPartlyfails ALT-1, ALT-2, ALT-3, ALT-4 and 5 more of the 11 cases that show it working
foot1.28.0, run in a VMPartlyfails ALT-4, ALT-6 of the 10 cases that show it working
pyte0.8.2, run as a libraryNofails the 10 cases that show it working
xterm.js headless6.0.0, run as a libraryPartlyfails ALT-4, ALT-6 of the 11 cases that show it working
alacritty_terminal0.26.0, run as a libraryPartlyfails ALT-1, ALT-2, ALT-3, ALT-4 and 4 more of the 11 cases that show it working
ConEmubuild 230724, its documentationPartlylists 47, 1047, 1048 and 1049, with 47 the same as 1047; not 1046; ANSI escape codes
rxvt-unicode9.31, run in a VMPartlyfails ALT-1, ALT-2, ALT-3, ALT-4 and 4 more of the 11 cases that show it working
VTE0.84.1, run as a libraryPartlyfails ALT-5, ALT-7, ALT-19 of the 11 cases that show it working

Validation

ALT-1: The alternate screen starts blank

AB
\e[?47h
|____|
|____|
cursor 1,3

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

AB
\e[?47h
X
\e[?47l
|AB__|
|____|
cursor 1,4

ALT-3: The cursor stays where it was

AB
\e[2;2H
\e[?47h
|____|
|____|
cursor 2,2

ALT-4: 47 keeps the alternate screen between visits

AB
\e[?47h
X
\e[?47l
\e[?47h   # back again
|__X_|
|____|
cursor 1,4

ALT-5: 1047 clears it on the way out

AB
\e[?1047h
X
\e[?1047l
\e[?1047h
|____|
|____|
cursor 1,4

ALT-6: But not on the way in

AB
\e[?47h
X
\e[?47l
\e[?1047h
|__X_|
|____|
cursor 1,4

ALT-7: 1049 clears it on the way in

AB
\e[?47h
X
\e[?47l
\e[?1049h
|____|
|____|
cursor 1,4

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

AB
\e[?1049h
\e[2;4H
X
\e[?1049l
|AB__|
|____|
cursor 1,3

ALT-9: 1049 saves the character attributes too

\e[1m     # bold
\e[?1049h
\e[0m
\e[?1049l
B
|B___|
|____|
attr 1,1 bold

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

\e[2;3H
\e7
\e[1;1H
\e[?1049l
|____|
|____|
cursor 2,3

ALT-11: Each screen has its own saved cursor

\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
|____|
|____|
cursor 2,2

ALT-12: Nothing saved on the alternate screen

\e[2;2H
\e7
\e[?47h
\e[1;4H
\e8       # the alternate screen has nothing saved: home
|____|
|____|
cursor 1,1

ALT-13: 1049 set on the alternate screen

AB
\e[?47h
X
\e[?1049h # saves into the alternate screen's slot
\e[?1049l # restores from the normal screen's: nothing there
|AB__|
|____|
cursor 1,1

ALT-14: 1048 is DECSC and DECRC

\e[2;3H
\e[?1048h
\e[1;1H
\e[?1048l
|____|
|____|
cursor 2,3

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

\e[?1049h
\e[?47$p
\e[?1047$p
\e[?1049$p
|____|
|____|
reply \e[?47;1$y\e[?1047;1$y\e[?1049;1$y

ALT-16: 1048 reports a saved cursor

\e7
\e[?1048$p
|____|
|____|
reply \e[?1048;1$y

ALT-17: 1046 answers backwards

\e[?1046h # allow switching, as it already is
\e[?1046$p
|____|
|____|
reply \e[?1046;2$y

ALT-18: Switching forbidden

AB
\e[?1046l
\e[?47h   # ignored
X
|ABX_|
|____|

ALT-19: RIS goes back to the normal screen

AB
\e[?47h
\ec
\e[?47$p
|____|
|____|
cursor 1,1
reply \e[?47;2$y
Every example on these pages runs in your browser, in libghostty-vt compiled to WebAssembly.