^[control-codes
live in libghostty-vt

Control sequences

SU Scroll Up

View as Markdown

Move the contents of the scrolling region up n lines.

CSI Pn S
Defaults
Pn = 1
ECMA-48
§8.3.147 SU – Scroll Up, p. 71
DEC STD 070
§5.5 Pan Down (Scroll Up), p. 5-91
VT510
SU—Pan Down

Move the lines of the scrolling region up Pn lines. The top Pn lines of the region are lost, and Pn blank lines appear at the bottom margin. A 0 is taken as 1. The cursor does not move.

Unlike DL, SU does not depend on where the cursor is. It scrolls the whole scrolling region, from the top margin, even when the cursor is outside it. With left and right margins set, only the part of each line between them moves.

The two DEC documents call this Pan Down, and describe something different. In DEC’s windowing extension the screen is a window onto a page of display memory that can be taller than the screen, and SU moves that window down the page: a line at the top of the window goes out of view, a new one comes into view at the bottom, and DEC STD 070 says that “no movement of data within the Logical Display occurs”. ECMA-48’s name describes the same thing from the other side, as data that “appear to move up”. A terminal whose page is no bigger than its screen, as xterm’s and libghostty-vt’s are, has nothing to pan across, so it moves the lines of the scrolling region instead. xterm documents CSI Ps S as “Scroll up Ps lines”.

With a ? before the parameters, CSI ? … S is a different sequence in xterm, XTSMGRAPHICS.

SD scrolls the other way.

Support

TerminalVersionSupportNotes
VT220manual, 2nd ed., 1984Nonot in the manual
VT510manual, 1st ed., 1993YesSU—Pan Down: pans the window rather than moving lines
xtermpatch 412, xterm-411aYes
libghostty-vt0f171f6Yes
kitty0.49.1, run in a VMPartlyfails SU-4 of the 4 cases that show it working
foot1.28.0, run in a VMPartlyfails SU-4 of the 4 cases that show it working
pyte0.8.2, run as a libraryNofails the 4 cases that show it working
xterm.js headless6.0.0, run as a libraryPartlyfails SU-4 of the 4 cases that show it working
alacritty_terminal0.26.0, run as a libraryPartlyfails SU-4 of the 4 cases that show it working
ConEmubuild 230724, its documentationPartlyscrolls the whole buffer up, not the scrolling region; ANSI escape codes
rxvt-unicode9.31, run in a VMPartlyfails SU-4 of the 4 cases that show it working
VTE0.84.1, run as a libraryYespasses the 4 cases that show it working

Validation

SU-1: Scroll one line

1\r\n2\r\n3\r\n4
\e[2;3H
\e[S
|2____|
|3____|
|4____|
|_____|
cursor 2,3

SU-2: Scroll two lines

1\r\n2\r\n3\r\n4
\e[2S
|3____|
|4____|
|_____|
|_____|

SU-3: The scrolling region, with the cursor outside it

1\r\n2\r\n3\r\n4
\e[2;3r   # region is lines 2 and 3; homes the cursor, above it
\e[S
|1____|
|3____|
|_____|
|4____|
cursor 1,1

SU-4: Inside left and right margins

abcde\r\nfghij\r\nklmno\r\npqrst\r\nuvwxy
\e[?69h   # allow left and right margins
\e[2;4s   # margins at columns 2 and 4
\e[2;3H
\e[2S
|almne|
|fqrsj|
|kvwxo|
|p___t|
|u___y|
Every example on these pages runs in your browser, in libghostty-vt compiled to WebAssembly.