# Select Number of Lines per Screen (DECSNLS)

> Choose how many lines the screen shows at once.

- **Sequence:** `CSI Pn * |`
- **DEC STD 070:** [§5.5 Select Number of Lines per Screen, p. 5-89](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n343/mode/1up)
- **VT510:** [DECSNLS—Set Lines Per Screen](https://vt100.net/docs/vt510-rm/DECSNLS.html)

Choose how many lines, `Pn` from 1 to 255, the screen shows at once. On a
DEC terminal this picks a font height: DEC STD 070 says it "selects the
maximum number of lines which can be displayed on the screen by choosing a
corresponding font size", that a value it does not support picks the next
larger one it does, and that a value past the largest picks the largest. The
VT420, it says, offers 24, 39 or 49 lines. DEC STD 070 lists DECSNLS among
its documented exceptions, part of the windowing extension.

xterm, a VT420 by default, implements it at level 4
([`CASE_DECSNLS`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L5747-L5756)). It ignores `Pn` outside 1
to 255, and otherwise asks the window system to make the window `Pn` lines
tall ([`RequestResize`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L9741)). Window operations like this
one are allowed by default: xterm's `allowWindowOps` is false
([`main.h`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/main.h#L119)), but an operation is still allowed unless it is
named in `disallowedWindowOps`
([`AllowWindowOps`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/ptyx.h#L3997-L3999)), and the default list
([`main.h`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/main.h#L150-L175)) does not name this one. Nothing on the
screen changes until the window does, which is up to the window manager, so
there is nothing a case here can check for a valid `Pn`.

The same request comes from xterm's `CSI Pn t` with `Pn` of 24 or more,
DECSLPP ([`window_ops`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L9312-L9316); the VT510 manual's
[DECSLPP](https://vt100.net/docs/vt510-rm/DECSLPP.html) sets lines per page).
libghostty-vt passes some of xterm's `CSI Ps t` window operations to its host,
but not that one.

libghostty-vt has no handler for `CSI Pn * |`: its control sequence dispatch
in `stream.zig` has no case for the final byte `|`. Since a valid DECSNLS
changes nothing on the screen in xterm either, the cases agree.

## Validation

### DECSNLS-1: Nothing changes at once

Input, one step per line:

```text
AB
\e[2;3H
\e[2*|    # two lines per screen
X
```

Expected screen:

```text
|AB____|
|__X___|
|______|
```

### DECSNLS-2: Zero or 256 is ignored

Input, one step per line:

```text
\e[2;3H
\e[*|     # left out, so zero
\e[256*|
X
```

Expected screen:

```text
|______|
|__X___|
|______|
cursor 2,4
```

---

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