# Set Columns Per Page (DECSCPP)

> Make the page 80 or 132 columns wide, without clearing it.

- **Sequence:** `CSI Pn $ |`
- **Defaults:** Pn = 80
- **DEC STD 070:** [§5.4.6 Set Columns Per Page, p. 5-73](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n327/mode/1up)
- **VT510:** [DECSCPP—Select 80 or 132 Columns per Page](https://vt100.net/docs/vt510-rm/DECSCPP.html)

Set the width of the page: `Pn` is `80` or `132`, and left out or `0`
means 80. It does what [DECCOLM](https://control-codes.page/modes/deccolm/index.html.md) does to the width, but
DEC STD 070 says it "does not clear page memory": what is in columns 1 to
80 is kept. The cursor does not move, unless it is beyond the new width,
in which case it goes to the last column.

DEC STD 070 makes DECSCPP a level 4 function, or level 3 with the windowing
extension. It also says a value it does not support picks the next larger
width it does, that the right margin moves in to the new edge if it was
beyond it, and that going from 132 columns to 80 clears columns 81 to 132.

xterm, a VT420 by default, implements it at level 3 and up
([`CASE_DECSCPP`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L5719-L5745)). It accepts only `80` and
`132` and ignores any other value, where DEC STD 070 would round up. If the
cursor is beyond the new width it moves it to the last column straight away,
then asks the window system to resize the window
([`RequestResize`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L9741)). The screen only changes size when
the window does, which depends on the window manager, so the cases here
check only what xterm does at once: where the cursor goes.

libghostty-vt has no handler for `CSI Pn $ |`. Its control sequence
dispatch in `stream.zig` has no case for the final byte `|`, so DECSCPP is
ignored and the cursor stays where it was.

## Validation

### DECSCPP-1: The cursor moves in to column 80

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

Input, one step per line:

```text
\e[1;82H  # column 82 of 82
\e[80$|   # 80 columns per page
```

Expected screen:

```text
|__________________________________________________________________________________|
cursor 1,80
```

libghostty-vt ignores DECSCPP, so the cursor stays in column 82. xterm
moves it to column 80 before it asks for the narrower window.

### DECSCPP-2: Left out means 80

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

Input, one step per line:

```text
\e[1;82H
\e[$|
```

Expected screen:

```text
|__________________________________________________________________________________|
cursor 1,80
```

### DECSCPP-3: A cursor inside the new width does not move

Input, one step per line:

```text
\e[1;5H
\e[80$|
```

Expected screen:

```text
|__________________________________________________________________________________|
cursor 1,5
```

### DECSCPP-4: Any width but 80 or 132 is ignored

Input, one step per line:

```text
\e[1;82H
\e[100$|  # neither 80 nor 132
```

Expected screen:

```text
|__________________________________________________________________________________|
cursor 1,82
```

---

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