Scroll protocol¶
Added in version 0.50.0.
The mouse protocol reports a scroll as presses of imaginary wheel buttons, one per line, so a program sees a trackpad as a stuttering wheel and never learns when a gesture begins, ends or coasts. The scroll protocol is an open terminal extension that reports scrolling as it arrives from the system: distances in pixels, the device that produced them, and the phase of the gesture. It is framed like the Tablet pointer protocol and shares its goals: small, unobtrusive, easy to implement, and no change to what the TTY is.
Framing¶
Frames are OSC 777 with the scroll selector:
<OSC> 777 ; scroll ; v=1 : op=query : id=7 <ST>
They follow the rules every OSC 777 extension shares, which
OSC 777 extension framing gives: the syntax of the fields, which frames are
rejected, the units and the clock. v, the protocol version, is 1, and
op is one of query, cap, mode and event.
Discovery¶
A program asks the terminal whether it supports the protocol:
<OSC> 777 ; scroll ; v=1 : op=query : id=7 <ST>
id is a non-negative 31 bit integer of the program’s choosing, echoed in
the reply. A supporting terminal answers:
<OSC> 777 ; scroll ; v=1 : op=cap : id=7 : caps=1 : mode=0 <ST>
caps is a bit set, of which only 1 is defined: the terminal reports
pixel distances. mode is the current state of the mode described below.
A terminal that does not support the protocol ignores the
query, so a program must time out and fall back to mouse reporting. A query
does not change the mode.
Mode¶
Reports are off by default and turned on per terminal window with:
<OSC> 777 ; scroll ; v=1 : op=mode : mode=1 <ST>
and off with mode=0. While the mode is on, scrolling over the window is
reported with this protocol instead of as wheel button presses, while clicks
and motion keep using whatever mouse reporting the program asked for. The
terminal does not scroll its own scrollback either; the program owns the
scroll. The mode is local to the window and reset by RIS. Programs must
turn it off when they exit, suspend or fail, and turn it back on when
resumed. Multiplexers keep it with the rest of the terminal state. A
terminal may let its user refuse the mode, for every program or for some. A
refused mode reads back as off and scrolling is not reported, so a program
reads the mode back rather than assume it was granted.
A mode request without a mode field reads the state instead of
changing it, and is answered with:
<OSC> 777 ; scroll ; v=1 : op=mode : mode=1 <ST>
A program resuming after a reconnection reads the mode to learn whether it survived, rather than assuming it did.
Reports¶
One frame per scroll event:
<OSC> 777 ; scroll ; v=1 : op=event : t=408211 : tu=73 : dx=0 : dy=250 : x=8448 : y=3072 : mods=1 : src=1 : phase=2 <ST>
All fields but tu are required:
|
When the system says the scroll happened, in milliseconds on the clock the terminal uses for every extension, see OSC 777 extension framing. A report the terminal makes itself, of its own coasting for one, has the time it made it |
|
Microseconds past |
|
The distance the view should move, in hundredths of a physical pixel, positive to the right and down, signed. A trackpad gives the distance the system measured; a notched wheel gives its lines converted with the terminal’s cell size and scroll multiplier, so a program applies one unit either way |
|
The pointer position in 1/256 of a physical pixel, signed, zero based, relative to the top left of the cell area |
|
Shift |
|
|
|
|
A report may carry a distance of zero, in particular with phases 1,
3, 4 and 6. A program that animates its own coasting should stop
at phase 4, since the terminal is already reporting the system’s. A
wheel reports phase 0 only.
A terminal or a relay that cannot keep up with the program may merge
consecutive reports of one device in one phase into one, and nothing else:
the merged report has the sum of their distances and the other fields of the
last report merged, its t and tu among them. A change of phase is
never merged or dropped, see OSC 777 extension framing.
A multiplexer translates x,y by the pane’s origin (borders included),
forwards every other field unchanged and delivers each report to the pane
under the pointer.
Implementation notes for silkie¶
silkie reports what the window system gives it:
On macOS, pixel distances and all the phases above, from the trackpad events of AppKit. Distances are in physical pixels, as
x,yare, so on a Retina display they are twice what the system gives in its own units. AppKit stamps its events to the microsecond, sotuis sent on macOS only: Wayland and X11 stamp them in whole milliseconds.On Wayland, pixel distances from
wl_pointeraxis events. The finger phases1to3come from the axis source and the axis stop, which the compositor sends when the fingers lift, on a seat of version 5 or later. The coasting phases4to6come from the terminal’s own momentum scroller, so phase4follows phase3at once. Phase7is sent when the coasting is interrupted, by fingers coming back down for example, or when the pointer leaves the window while the fingers are still scrolling.On X11, pixel distances from XInput 2 smooth scrolling where available, otherwise lines converted to pixels with
src=0, and the coasting phases4to7from the terminal’s own momentum scroller. The X server does not say when the fingers lift, which the scroller guesses from a pause, so the finger phases1to3are not reported.