^[control-codes live in libghostty-vt

Control sequences

DECSCUSR Set Cursor Style

Choose the cursor's shape, and whether it blinks.

CSI Ps SP q
Defaults
Ps = 1
VT510
DECSCUSR—Set Cursor Style
xterm
Functions using CSI

Set the shape of the cursor, and whether it blinks. SP is a space, 20, used here as an intermediate byte.

PsCursor
0, 1, or left outblinking block
2steady block
3blinking underline
4steady underline
5blinking bar
6steady bar

The VT510 manual defines 0 to 4, and calls the blinking block the default. The bar, 5 and 6, is xterm’s addition. DECSCUSR came with the VT520, so DEC STD 070 does not have it. It changes only how the cursor looks: whether it is shown at all is DECTCEM’s job.

Nothing about the cursor’s style is in the cell grid, so the cases ask the terminal for it with DECRQSS, DCS $ q SP q ST, which both the VT510 manual and xterm list for DECSCUSR (see DCS). The answer is DCS 1 $ r Ps SP q ST, with the style as Ps.

0, or no parameter, is where libghostty-vt departs from the manuals. It reads it as “go back to the host’s default style”, and the host chooses that default with libghostty-vt’s DEFAULT_CURSOR_STYLE and DEFAULT_CURSOR_BLINK options. Left unset, as the cases here leave them, the default is a steady block, so CSI 0 SP q reports 2. Before any DECSCUSR at all, the cursor is the same steady block.

Validation

DECSCUSR-1: Steady block

\e[2\sq
\eP$q\sq\e\\ # ask for the style
|_____|
reply \eP1$r2 q\e\\

DECSCUSR-2: Blinking underline

\e[3\sq
\eP$q\sq\e\\
|_____|
reply \eP1$r3 q\e\\

DECSCUSR-3: Steady bar

\e[6\sq
\eP$q\sq\e\\
|_____|
reply \eP1$r6 q\e\\

DECSCUSR-4: Blinking block

\e[1\sq
\eP$q\sq\e\\
|_____|
reply \eP1$r1 q\e\\

DECSCUSR-5: Zero is a blinking block

\e[2\sq   # steady block
\e[0\sq   # back to the default
\eP$q\sq\e\\
|_____|
reply \eP1$r1 q\e\\

libghostty-vt answers \eP1$r2 q\e\\, a steady block. Its DECSCUSR handler in stream.zig turns 0 and an omitted parameter into .default, and setCursorStyle in Terminal.zig takes the host’s default shape and blink, which unless the host sets them are a block that does not blink. The VT510 manual makes 0 a blinking block, the default, and xterm’s ctlseqs says “Ps = 0 ⇒ blinking block”, which its CASE_DECSCUSR does. A host can make libghostty-vt agree by setting DEFAULT_CURSOR_BLINK.

Every example on these pages runs in your browser, in libghostty-vt compiled to WebAssembly.