Input
A surface reports what the user does as events, and only the events the program asked for. Most of what the user does stays in the terminal: typing in a field, ticking a box, hovering, scrolling. The program hears the result when it needs it, such as a field's value once it is committed.
host → ESC ] 7279 ; a=ev:s=card:e=click:t=go ; {"area": {"c": 0, "r": 0, "w": 4, "h": 1}} ST
An event arrives on the program's input, among the keys the user types
(§9). s names the surface, e the kind of
event, and t the element, by its id. The detail is msgpack, shown
here decoded.
The id is the program's handle, so give one to each element whose events
you want: a button with no id reports no click. Write ids in ASCII, since
a host reports café as caf_ (§3.2).
Keyboard and focus covers the keys, and focus and blur.
What asks for each event
An element asks for its own events, and a placement asks for those of its whole surface. Nothing is sent that nobody asked for:
| event | asked for by | sent |
|---|---|---|
click, change, input, submit |
the element: its kind, or click or input in its data-on |
each time |
press |
p=1 on the placement |
each press |
dragstart, drag, dragend |
drag in the element's data-on, and drop for a target |
for each element, cell or step crossed |
hover |
v=1 on the placement |
each change, at most one a frame |
view |
view in the element's data-on |
once a scroll rests, else at most one a frame |
fit |
f=1 on the placement |
at most one a frame |
focus, blur |
nothing: the keyboard is always told | each time |
command |
a=edit, and a named item in the program's menu |
each key, each pick |
caps |
the query | each change |
Events are asked for because they reach whatever reads the terminal's input. A document left on the screen after its program exits would otherwise type events into the shell. A program detaches the surfaces it leaves (§5.5), and a detached surface sends nothing.
data-on is a list of words, as in data-on="click drag". p, f and
v belong to the surface: 1 turns one on and 0 off, and it holds
until the program sends it again
(§5.2). fit says the rows a
document needs have changed, and caps that the host's capabilities have.
The pointer's events need a window the host draws. A hidden surface, or
one scrolled out of view, sends no press, drag, hover, fit or
view. Drawn again, it reports what changed since the program last heard.
A click, a press and a drag's events carry mods, the modifier keys held,
as bits: 1 shift, 2 alt, 4 ctrl and 8 super. It is left out when
none is held. Ignore an event whose kind you don't know: later versions
add kinds.
Clicks
A click is sent when the user activates a button, a link, a summary,
or an input of type button, submit or reset. Any other element
asks for clicks with data-on="click". The element that reports is the
nearest of these, from where the user clicked outward.
<button id=save value=draft>Save</button>
<li id=row-7 data-on=click>…</li>
host → ESC ] 7279 ; a=ev:s=editor:e=click:t=save ; {"area": {"c": 2, "r": 5, "w": 6, "h": 1}, "value": "draft"} ST
areais the cells the element covers, counted from 0 at the surface's top left cell. Place what belongs next to the element by it, as a browser places a menu by its button. An element partly scrolled away reports its whole area, socandrmay be negative.valuecomes when the element has avalueattribute.countmarks a repeated click: 2 for the second click of a double click, 3 for the third of a triple. Every click of a run is reported, so a double click is aclick, then aclickwith"count": 2.
A click from the keyboard, space or enter on a button, carries its
area too, and is always the first of its run. Only a host with the
feature count counts clicks, by the user's own settings
(§4). Without it, offer
another way to do what a double click would.
Links
A click on a link is an event like any other click: a surface never
navigates. The link's href is its handle, so t is absent. The detail
carries href as the document has it, and url, the address it resolves
to against the document's base URL
(§7.3):
host → ESC ] 7279 ; a=ev:s=post:e=click ; {"area": {"c": 12, "r": 3, "w": 5, "h": 1}, "href": "../about", "url": "https://example.com/about"} ST
The program decides what the click means: it may show another page, or
ask its platform to open the address. url is absent where it would fall
under https://hotty.invalid/. That is the base of a document with no
<base href>, so a relative link there has only its href.
A link with target="_blank" is a hyperlink, and belongs to the
terminal. The host treats it exactly as an OSC 8 hyperlink in the cells:
the same gesture opens it, with the same feedback and settings.
Its click is not reported, nor a press the host takes for its gesture. A
target="_blank" link with no url is not a hyperlink.
Forms
A form's controls work in the terminal, and the program hears their values as they settle. Toggling a checkbox, picking an option and typing need no round trip, and each control reports by its kind:
| event | when | detail |
|---|---|---|
change |
a checkbox or radio button toggled, or a select's option picked: at once. A text field's value: when focus leaves it, if the value changed | {"checked": bool, "value": str} for a checkbox or radio button, {"value": str} otherwise |
input |
every edit of a control with data-on="input" |
{"value": str} |
submit |
a submit button, or enter in a text field |
{"fields": [[str, str]]}: each named field's name and value, in tree order |
A search box that hears every keystroke, as the user types "ad" and
presses enter:
<form id=search>
<input id=q name=q data-on=input>
</form>
host → ESC ] 7279 ; a=ev:s=find:e=input:t=q ; {"value": "a"} ST
host → ESC ] 7279 ; a=ev:s=find:e=input:t=q ; {"value": "ad"} ST
host → ESC ] 7279 ; a=ev:s=find:e=submit:t=search ; {"fields": [["q", "ad"]]} ST
- A submit's fields are pairs, since two controls may share a name,
as two checkboxes for one setting do. A form with no named field sends
[]. Nothing is sent anywhere else:submitis all that happens. - A text field commits when focus leaves it, including when the
surface loses the keyboard, and sends
changeif its value changed. Withoutdata-on="input", thatchangeis all the program hears of it. - The program sets a value by changing the control's
value,checkedorselectedattribute with a delta. Its value wins, except while the user is editing that control (Updates).
Presses
A placement with p=1 hears every press in its window, whatever it lands
on: a control, plain text or empty space. A program uses it to follow the
user between surfaces, as a dashboard selects the card pressed.
program → ESC ] 7279 ; a=place:s=cpu:c=40:r=6:p=1 ST
host → ESC ] 7279 ; a=ev:s=cpu:e=press:t=cpu-card ; {"area": {"c": 0, "r": 0, "w": 40, "h": 6}} ST
tis the nearest element with an id, from the one pressed outward. Where none has one, there is notand noarea.- A press comes first, before every other event it causes: the
changeandfocusit brings, and theclickits release may make. - A press is the primary button, a tap, or a touch that becomes a drag. It is not reported where the pointer passes through, for a mouse or pen press with Alt held, or where the host takes it for a hyperlink's gesture.
Drags
An element opts in to drags with data-on="drag", and then hears the
pointer from the press to the release
(§9.1). Drags carry what a click cannot: a
range dragged across a grid, a handle let go somewhere, a slider.
dragstartcomes on the press.tis the element that opted in, and it needs an id, or there is no drag.dragcomes each time the element under the pointer changes. Itstis the nearest element there with an id anddragordrop. Where there is none, adragcomes for each cell instead.dragendcomes once, on the release, wherever the pointer is.
Each carries c and r, the pointer's cell counted from the surface's
top left. They go on counting outside it, negative above and to the left.
A drag costs a few events for each element it crosses, not one a move.
Steps say where in an element. With data-steps="20", every event of
its drag carries x, from 0 to 20 across its width. data-steps="0 20"
divides its height instead, for y. A drag also comes each time the
step changes, as for this volume slider:
<div id=volume data-on=drag data-steps=20 style="touch-action: pan-y">…</div>
host → ESC ] 7279 ; a=ev:s=mixer:e=dragstart:t=volume ; {"c": 4, "r": 1, "x": 5} ST
host → ESC ] 7279 ; a=ev:s=mixer:e=drag:t=volume ; {"c": 5, "r": 1, "x": 6} ST
…
host → ESC ] 7279 ; a=ev:s=mixer:e=dragend:t=volume ; {"c": 7, "r": 1, "x": 9} ST
The step is measured against the dragged element wherever the pointer is, and clamped. So the slider follows a finger that slips off its track, as a browser's range input does.
A drop target has drop in its data-on and an id, and a drag can
end on it. One with only drop is never dragged itself. With steps, a
target also tells where in it the pointer is, as tx and ty.
A list row with data-on="drag drop" and data-steps="0 1" is both. It
splits into halves, for an item dropped on it to land before or after it.
A tree's folder with data-steps="0 2" splits into before, into and
after.
- The pointer is held until the release, as
setPointerCaptureholds it in a page. Every move goes to the drag, over the cells, another surface, or outside the terminal's window. - A drag selects no text on an element that opts in.
- A drag is a click only where it began. Released on the element it
started on, it also sends
clickafterdragend, if that element hasclickin itsdata-on. - It can end early, with a
dragendthat has not. That happens when the placement goes away, a new document replaces this one, or the host loses the pointer. A delta or a new placement does not end it.
Touch on a surface
A tap on a surface is a click, and a touch drag scrolls, as a wheel does (§9). Over a document that scrolls, it scrolls the document while that can move. Over any other surface, it does what it would over the cells beneath: scrollback, or on the alternate screen, the program's wheel input.
A touch drags an element only where the element opts out of panning, so a surface never traps the finger:
touch-actiondecides, as the document's CSS sets it. On an element that opts in to drags, a touch drags whentouch-actionallows no pan along its first move.pan-yon a horizontal slider drags it across, and an up or down swipe still scrolls.nonedrags it either way. The default,auto, always scrolls.- The touch presses when it becomes a drag:
press, if asked for, thendragstart, both for where the touch began. - A tap presses at its lift. A long press never becomes a drag.
- A second finger ends a drag, and the gesture is the host's until every finger lifts.
A touch with Alt held never drags, and a touch's moves hover nothing. A host may report what a tap touched, or what the finger was on at the lift that ends a drag.
Presses with Alt
A press with Alt held is the program's, never a surface's
(§9.2). Wherever it lands, the host
takes it as a press on the cells beneath. With mouse reporting on, the
program hears it as any press on the cells, with Alt set: in the SGR
encoding, CSI < 8 ; <col> ; <row> M.
So a program can start a gesture of its own over its surfaces. A multiplexer that draws its panes as surfaces moves them with Alt and a drag, as a window manager moves windows.
- The surface hears nothing of it: no
press,dragstartorclick. It takes no focus, and opens no hyperlink. A surface that had the keyboard gives it back, and sendsblur. - The press decides. The gesture stays the program's until its release, whether or not Alt stays held. A drag begun without Alt stays the surface's when Alt is pressed during it.
- On macOS, Alt is Option, whatever the terminal makes Option type.
- Mouse and pen only. A tap is a click whatever keys are held.
Where the pointer passes through
On a host with the feature passthrough, a surface takes the pointer only
where an element is there for it
(§9.3). The host hit
tests as a browser does, and skips every box whose pointer-events is
none. So a frame drawn around cells can leave the cells the pointer:
<style>
html { pointer-events: none }
#close { pointer-events: auto }
</style>
<div class=frame><button id=close>Close</button></div>
Where a window takes no pointer, the pointer reaches what is below it: another window, or the cells. Over the cells, the program hears presses and motion through its mouse reporting, as it would with no surface there. The surface hears nothing, and a press there gives the keyboard back to the terminal.
A gesture belongs to whoever took its press, until its release. Without
passthrough, every window takes the pointer wherever it is. A program
that needs the cells under a surface checks for the feature, and
otherwise keeps its surfaces off those cells.
Hover
:hover and the pointer's shape need no program. A placement with v=1
also hears which element the pointer is over
(§9.4). A program uses it to show a hint of its
own, or to know that the pointer has left what it lit.
program → ESC ] 7279 ; a=place:s=chart:c=60:r=12:v=1 ST
host → ESC ] 7279 ; a=ev:s=chart:e=hover:t=bar-7 ; {"c": 31, "r": 4} ST
host → ESC ] 7279 ; a=ev:s=chart:e=hover ; {"out": true} ST
- Only changes. A
hovercomes when the element under the pointer changes,tits nearest id, with the pointer's cell. A move within an element sends nothing, and the host sends at most one a frame. - Out comes when the window loses the pointer: to the cells, another window, a part that lets it through, or outside the terminal. A press with Alt sends it too.
- A held button holds hover. From a press to its release, no surface
sends
hover: a drag reports its own targets. At the release, the surface reports where the pointer is now. - Mouse and pen. A touch's moves hover nothing. At a tap's release, or the lift that ends a touch's drag, a host may report what the finger was on.
Hover is a feature, hover. Where a host lacks it, a program never hears
that the pointer has left. It clears what it lit on the next key or press
instead.
What is in view
A document that scrolls tells the program which of its marked elements
are in view (§9.5). Mark an element with
data-on="view" and an id. A table of contents marks the section being
read this way, and a long list fetches what comes into view.
host → ESC ] 7279 ; a=ev:s=guide:e=view ; {"in": {"install": {"c": 0, "r": -3, "w": 60, "h": 8}, "usage": {"c": 0, "r": 5, "w": 60, "h": 20}}, "end": false} ST
innames every element in view, each with its whole area, not only what changed. So the program keeps no state to follow changes.endistruewhen the document's root is scrolled as far down as it goes.- When: once the document is laid out, then each time what it would carry changes. While a scroll moves, the host waits for it to rest.
To take the reader to an element, the program sends the reveal op
(Updates).
Touch in the cells
Note
New in 0.4.
A program that drags with the mouse in its cells can name where a finger
drags too (§9.6). Over the cells a
touch drag scrolls, so a slider drawn in cells moves for a mouse but not
for a finger. a=touch names the cells where a finger drags, and which
way:
program → ESC ] 7279 ; a=touch ; {"areas": [{"c": 10, "r": 20, "w": 30, "h": 1, "drag": "x"}]} ST
Each area has:
candr, its first column and row, counted from 0 at the screen's top left, where a mouse report counts from 1;wandh, the columns and rows it spans;drag, the way a finger drags in it:"x"across,"y"up and down, or"xy"either.
In an area, a touch that moves the area's way becomes the mouse's primary button. It is pressed where the touch began, moves with the finger, and is released where it lifts. The program reads it as it reads the mouse, in its own mode and encoding, so there is no new event to parse.
- A tap in an area is a press and a release at the lift, so a slider jumps to where it was tapped. A swipe the other way still scrolls.
- Only while the program reads the mouse's buttons, and the terminal shows the screen rather than its scrollback.
- Areas last until the program sends others, or
a=touchwith no body. The main and alternate screens keep their own. - They are cells of the screen, not of its text: they stay put when the lines scroll. Send them again when what you drew moves, and clear them when you exit.
- Surfaces come first. A touch that begins where a surface's window takes the pointer is the surface's.
- Only a finger. The mouse and a pen reach the program as they always have.
A host that takes no touch accepts a=touch all the same, so a program
sends its areas without checking. A multiplexer that relays the mouse
passes its panes' areas on, moved to where each pane is. An SDK builds the
command with Touch (SDK design).