Trackpad gesture protocol¶
Added in version 0.50.0.
A trackpad delivers more than scrolling: the system recognizes a pinch, a rotation, a swipe with several fingers, a double tap with two fingers and fingers held still, and hands them to the application as gestures. The trackpad gesture protocol is an open terminal extension that reports them to the program running in the terminal. Like the other extensions here it is small, unobtrusive, easy to implement and changes nothing about what the TTY is.
It is independent of the Tablet pointer protocol, which reports the fingers themselves and leaves recognizing a gesture to the program. Both can be on at the same time, a program takes whichever suits it.
Framing¶
Frames are OSC 777 with the gesture selector:
<OSC> 777 ; gesture ; v=1 : op=query : id=42 <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 currently
1, and op is one of query, cap, mode and event.
Discovery¶
A program asks the terminal whether it supports the protocol:
<OSC> 777 ; gesture ; v=1 : op=query : id=42 <ST>
id is a non-negative 31 bit integer of the program’s choosing, echoed in
the reply so replies can be matched to queries. A supporting terminal
answers:
<OSC> 777 ; gesture ; v=1 : op=cap : id=42 : caps=15 : mode=0 <ST>
caps is a bit set of the gestures the terminal can report: pinch 1,
rotate 2, swipe 4, smart zoom 8, hold 16. It describes what
the terminal can pass on from the window system it is connected to, not
whether a trackpad is attached. 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. A query does not change the mode.
Mode¶
Reports are off by default and turned on per terminal window with:
<OSC> 777 ; gesture ; v=1 : op=mode : mode=1 <ST>
mode is a bit set of the kinds of gesture the program wants: 1 the
gestures made by moving fingers, which are the pinch, the rotation, the swipe
and the smart zoom, 2 holds; 0 turns reporting off. While a bit is
on, the gestures it names are reported with this protocol when they happen
over the window. A terminal drops the bits it does not know, so a program
that depends on a bit reads the mode back. The scroll and touch events the
same fingers produce are unaffected, a two finger pinch still scrolls if the
system scrolls during it, so a program that zooms on pinch should expect
scroll reports too and ignore them while a pinch is in progress.
The mode is local to the window, is reset by RIS, and changing it
cancels every gesture in progress. 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, restore it on reconnect, and turn it off when
detaching. A terminal may let its user refuse the mode, or some of its bits,
for every program or for some. A refused bit reads back as off and the
gestures it names are 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 ; gesture ; 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¶
A report is a single frame per event:
<OSC> 777 ; gesture ; v=1 : op=event : kind=1 : phase=2 : t=408211 : tu=73 : x=25600 : y=51200 : mods=0 : scale=120 : total=1340 <ST>
kind, phase, t, x, y and mods are always present:
|
|
|
|
|
When the system says the event happened, in milliseconds on the clock the terminal uses for every extension, see OSC 777 extension framing. A cancel the terminal makes itself has the time it made it |
|
Microseconds past |
|
Position of the pointer in 1/256 of a physical pixel, signed, zero based, relative to the top left of the cell area, within ±1073741823. A physical pixel is one of the framebuffer the terminal draws into, not a scaled unit of the window system |
|
Shift |
The fields a kind adds:
pinch |
The change in magnification since the previous
report of the gesture, in thousandths: |
pinch |
The magnification since the gesture began, in
thousandths, |
rotate |
The rotation since the previous report, in hundredths of a degree, clockwise positive |
rotate |
The rotation since the gesture began, in hundredths of a degree |
swipe |
The direction the fingers moved, |
A smart zoom and a hold carry no extra fields.
Any kind may also carry fingers, the number of fingers making the
gesture. The system tells it when the gesture begins, and it is then present
in every report of that gesture. It is absent when the system does not say.
Lifecycle¶
A gesture begins with begin, is followed by any number of update
reports and ends with end or cancel. A begin carries a scale
of 0, an angle of 0, and a total of 1000 or 0. A swipe
and a smart zoom are instantaneous: they are reported as a begin
immediately followed by an end, both with the same fields. A pinch and a
rotation are independent gestures that usually run at the same time, since
two fingers that pinch also turn a little, so a program interested in one
must tolerate reports of the other.
A hold begins when fingers come to rest on the trackpad and ends when they
lift, with no update in between. It is cancelled when the fingers start
to move instead, which usually begins a scroll or another gesture. Programs
use it to stop something in motion, a coasting scroll, say, the moment the
user touches the trackpad.
A gesture is captured by the window it began in, and its later reports go
to that window even when the pointer moves out of it, so coordinates can be
negative or exceed the cell area. The terminal cancels gestures in progress
when the mode changes or the terminal is reset. After a cancel the rest of
that gesture is not reported, not even its end. Programs must tolerate a
repeated cancel and a fresh begin for a kind they still have open, which
replaces the earlier gesture.
A terminal or a relay that cannot keep up with the program may merge
consecutive update reports of one gesture into one, and nothing else: a
merged pinch has the product of the changes in magnification as its
scale, a merged rotation the sum of the angles as its angle, and both
have the total, t and tu of the last report merged. A begin,
an end and a cancel are 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, delivers each gesture only to the pane
it began in, and cancels gestures when panes move or resize.
Versions and extension¶
A program ignores a frame whose op it does not know, a report whose
kind it does not know and fields it does not know, so all three are added
without a version change. A new kind is also only sent to a program that
asked for it with a mode bit of its own, as the hold is, so a program that
sends mode=1 sees the four kinds that bit names and nothing else, however
much the protocol grows. A new phase value, or a change in the meaning of
an existing field, needs a new version.
Implementations¶
silkie, this fork of kitty: pinch, rotate, swipe and smart zoom on macOS; pinch, rotate, swipe and hold, with the number of fingers, on Wayland; pinch, rotate and swipe, with the number of fingers, on X11.
kitty --debug-inputlogs the gestures it sees.
Implementation notes for silkie¶
silkie passes on what the window system recognizes:
On macOS, the magnify, rotate, swipe and smart magnify events of AppKit. A swipe with three fingers only reaches applications when System Settings has it set to swipe between pages, and a smart zoom when the trackpad’s smart zoom is enabled. AppKit does not say how many fingers made a gesture, so
fingersis absent. Its events are stamped to the microsecond, sotuis sent on macOS only: Wayland and X11 stamp gestures in whole milliseconds.On Wayland, through version 3 of
pointer-gestures-unstable-v1, andcapsis zero on a compositor without it. A pinch there carries both scale and rotation, so one compositor pinch is reported as a pinch and a rotation. A swipe is reported once it ends, as its dominant direction, and compositors keep most three finger swipes for themselves. A hold also stops kitty’s own coasting scroll.On X11, through the touchpad gestures of XInput 2.4, which needs version 21.1 or later of the X server and a driver that sends them, as the libinput driver does.
capsis zero when the server does not have them. A pinch and a swipe are reported as on Wayland. X11 has no hold and no smart zoom.