Hudson Griffith

Scroll Position Protocol (mode 7502)

Draft 0.1 · October 12, 2026

Draft, not published yet. This is an early proposal shared for feedback, not a finished standard. The sequences, including the number 7502, may still change, so please don't ship it in released software yet. It's prototyped in pi and a macOS terminal, neither released.

A small terminal protocol that lets a full-screen program share its viewport with the terminal: which rows of its content are on screen, and where. The terminal can show its native scrollbar beside those rows, and dragging it asks the program to scroll.

# The program turns the protocol on and reports rows 120–146 of 400,
# in a scrolling area that starts on the second screen row.
CSI ? 7502 h
OSC 7502 ; top=120:rows=27:total=400:y=1 ST

# The user drags the scrollbar to the top. 373 is the last position.
CSI 7502 ; 0 ; 373 v

Note: The protocol isn't tied to any terminal or program, and leaves presentation to the terminal: a scrollbar is the obvious use, but the same reports could drive a minimap or a screen reader. Reports use key=value pairs like kitty's extensions and OSC 7501, and turning it on works like synchronized output (mode 2026).

pi in a prototype macOS terminal. The scrollbar sits beside the transcript, not the input box, and dragging it scrolls pi.

Motivation

On the primary screen, the terminal scrolls its own scrollback, with a native scrollbar, momentum, and search. Full-screen programs such as coding agents, pagers, and editors run on the alternate screen, which has no scrollback, so they scroll themselves and at best draw a scrollbar out of text cells. This protocol lets them report their position, so the terminal can show its own scrollbar and pass drags back as requests.

Conventions

MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are used as in RFC 2119 and RFC 8174, only in capitals.

NameBytes
ESC0x1B
CSIESC [
OSCESC ]
STESC \ or BEL (0x07)

A terminal is one window, tab, or pane on a pseudo-terminal, and a program writes to it. The scrolling area is where the program's content scrolls, such as a transcript above an input box, and a row is one screen row of it. Rows count from 0.

Sequences

DirectionSequenceMeaning
program → terminalCSI ? 7502 $ pQuery (DECRQM): is it supported?
terminal → programCSI ? 7502 ; Ps $ yReply (DECRPM): whether it is.
program → terminalCSI ? 7502 hTurn the protocol on.
program → terminalCSI ? 7502 lTurn it off, clearing the report.
program → terminalOSC 7502 ; pairs STReport: where the scrolling area is.
program → terminalOSC 7502 ; STClear: there is no scrolling area.
terminal → programCSI 7502 ; T ; M vRequest: show row T first. T = M is the end.

Detection and turning it on

program  → CSI ? 7502 $ p
program  → CSI c
terminal → CSI ? 7502 ; 2 $ y
terminal → CSI ? 62 ; 22 c
program  → CSI ? 7502 h

The program asks with DECRQM, then primary device attributes (CSI c), which every terminal answers. A reply of 1, 2, or 3 before the DA1 reply means supported. 0, 4, or the DA1 reply first means not, and the program keeps its own scrollbar.

Report

Pairs are key=value, separated by :, in any order, with decimal integer values.

KeyRequiredMeaning
topyesThe first row shown.
rowsyesThe scrolling area's height. At least 1.
totalyesAll rows of the content.
ynoThe screen row the scrolling area starts on. Default 0.

Request

When the user moves the scrollbar, the terminal sends CSI 7502 ; T ; M v, where M is total - rows of the latest report and T is from 0 to M. Programs ignore any further parameters. Plain CSI … v is unused by key and mouse encodings and by terminals.

Multiplexers

A multiplexer that doesn't know the protocol drops the mode, so programs inside it see it as unsupported. One that supports it implements it per pane, like mouse reporting, instead of passing the sequences through: replies and requests would reach the multiplexer, not the program.

Security

Reports are untrusted, but carry only bounded numbers and only move a scrollbar. Requests carry only numbers from the user's drag, and are ordinary input to whatever program reads the terminal, which is why programs turn the mode off before handing it on.

Relationship to other sequences

Implementation notes

Not requirements: what worked in the prototypes.

Example

# After detection, on entering the alternate screen
program  → CSI ? 7502 h
program  → OSC 7502 ; top=373:rows=27:total=400:y=1 ST

# The user drags to the top, then more output arrives
terminal → CSI 7502 ; 0 ; 373 v
program  → OSC 7502 ; top=0:rows=27:total=400:y=1 ST
program  → OSC 7502 ; top=0:rows=27:total=412:y=1 ST

# The user drags to the bottom while output streams in.
# T = M, so the program follows the end, now at 416 rows.
terminal → CSI 7502 ; 385 ; 385 v
program  → OSC 7502 ; top=389:rows=27:total=416:y=1 ST

# Before leaving the alternate screen
program  → CSI ? 7502 l

Revision history

DateRevisionChanges
2026-10-120.1Initial draft.