# Set Cursor Style (DECSCUSR)

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

- **Sequence:** `CSI Ps SP q`
- **Defaults:** Ps = 1
- **VT510:** [DECSCUSR—Set Cursor Style](https://vt100.net/docs/vt510-rm/DECSCUSR.html)
- **xterm:** [Functions using CSI](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)

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

| `Ps` | Cursor |
| --- | --- |
| `0`, `1`, or left out | blinking block |
| `2` | steady block |
| `3` | blinking underline |
| `4` | steady underline |
| `5` | blinking bar |
| `6` | steady 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](https://control-codes.page/modes/dectcem/index.html.md)'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](https://control-codes.page/esc/dcs/index.html.md)). 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

Input, one step per line:

```text
\e[2\sq
\eP$q\sq\e\\ # ask for the style
```

Expected screen:

```text
|_____|
reply \eP1$r2 q\e\\
```

### DECSCUSR-2: Blinking underline

Input, one step per line:

```text
\e[3\sq
\eP$q\sq\e\\
```

Expected screen:

```text
|_____|
reply \eP1$r3 q\e\\
```

### DECSCUSR-3: Steady bar

Input, one step per line:

```text
\e[6\sq
\eP$q\sq\e\\
```

Expected screen:

```text
|_____|
reply \eP1$r6 q\e\\
```

### DECSCUSR-4: Blinking block

Input, one step per line:

```text
\e[1\sq
\eP$q\sq\e\\
```

Expected screen:

```text
|_____|
reply \eP1$r1 q\e\\
```

### DECSCUSR-5: Zero is a blinking block

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

Input, one step per line:

```text
\e[2\sq   # steady block
\e[0\sq   # back to the default
\eP$q\sq\e\\
```

Expected screen:

```text
|_____|
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`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L4949-L4969)
does. A host can make libghostty-vt agree by setting `DEFAULT_CURSOR_BLINK`.

---

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