class

TermBuf::Input::Decoder

Inherits Reference < Object

Turns the bytes a terminal sends into events.

Three things arrive on the same stream and have to be told apart: replies to queries the application made, text that was pasted rather than typed, and key presses. The first is settled by whoever set #on_sequence, since nothing about the bytes says whether a finger or a terminal produced them. The second is settled by the bracketed paste markers. Everything left is a key.

State is carried between calls, because none of the three respects the boundaries of a read: an escape sequence, a UTF-8 character, and a paste can each be split across as many reads as the kernel feels like.

Constants

ESCAPE_TIMEOUT = 25.milliseconds

How long to wait for the rest of an escape sequence before deciding there is no rest.

The escape key sends one byte and so does the start of every arrow key, so the two are indistinguishable until either more bytes arrive or enough time passes that none will.

KITTY_KEYS = {57358 => Key::Name::CapsLock, 57359 => Key::Name::ScrollLock, 57360 => Key::Name::NumLock, 57361 => Key::Name::PrintScreen, 57362 => Key::Name::Pause, 57363 => Key::Name::Menu, 57376 => Key::Name::F13, 57377 => Key::Name::F14, 57378 => Key::Name::F15, 57379 => Key::Name::F16, 57380 => Key::Name::F17, 57381 => Key::Name::F18, 57382 => Key::Name::F19, 57383 => Key::Name::F20, 57384 => Key::Name::F21, 57385 => Key::Name::F22, 57386 => Key::Name::F23, 57387 => Key::Name::F24, 57388 => Key::Name::F25, 57389 => Key::Name::F26, 57390 => Key::Name::F27, 57391 => Key::Name::F28, 57392 => Key::Name::F29, 57393 => Key::Name::F30, 57394 => Key::Name::F31, 57395 => Key::Name::F32, 57396 => Key::Name::F33, 57397 => Key::Name::F34, 57398 => Key::Name::F35, 57399 => Key::Name::KP0, 57400 => Key::Name::KP1, 57401 => Key::Name::KP2, 57402 => Key::Name::KP3, 57403 => Key::Name::KP4, 57404 => Key::Name::KP5, 57405 => Key::Name::KP6, 57406 => Key::Name::KP7, 57407 => Key::Name::KP8, 57408 => Key::Name::KP9, 57409 => Key::Name::KPDecimal, 57410 => Key::Name::KPDivide, 57411 => Key::Name::KPMultiply, 57412 => Key::Name::KPSubtract, 57413 => Key::Name::KPAdd, 57414 => Key::Name::KPEnter, 57415 => Key::Name::KPEqual, 57416 => Key::Name::KPSeparator, 57417 => Key::Name::KPLeft, 57418 => Key::Name::KPRight, 57419 => Key::Name::KPUp, 57420 => Key::Name::KPDown, 57421 => Key::Name::KPPageUp, 57422 => Key::Name::KPPageDown, 57423 => Key::Name::KPHome, 57424 => Key::Name::KPEnd, 57425 => Key::Name::KPInsert, 57426 => Key::Name::KPDelete, 57427 => Key::Name::KPBegin, 57428 => Key::Name::MediaPlay, 57429 => Key::Name::MediaPause, 57430 => Key::Name::MediaPlayPause, 57431 => Key::Name::MediaReverse, 57432 => Key::Name::MediaStop, 57433 => Key::Name::MediaFastForward, 57434 => Key::Name::MediaRewind, 57435 => Key::Name::MediaTrackNext, 57436 => Key::Name::MediaTrackPrevious, 57437 => Key::Name::MediaRecord, 57438 => Key::Name::LowerVolume, 57439 => Key::Name::RaiseVolume, 57440 => Key::Name::MuteVolume, 57441 => Key::Name::LeftShift, 57442 => Key::Name::LeftControl, 57443 => Key::Name::LeftAlt, 57444 => Key::Name::LeftSuper, 57445 => Key::Name::LeftHyper, 57446 => Key::Name::LeftMeta, 57447 => Key::Name::RightShift, 57448 => Key::Name::RightControl, 57449 => Key::Name::RightAlt, 57450 => Key::Name::RightSuper, 57451 => Key::Name::RightHyper, 57452 => Key::Name::RightMeta, 57453 => Key::Name::IsoLevel3Shift, 57454 => Key::Name::IsoLevel5Shift}

The kitty keyboard protocol's functional keys, which it reports as code points in the Unicode private use area so that they cannot collide with anything a person could type.

Only the keys with no other encoding are here. Escape, enter, tab and backspace arrive as their C0 bytes, CSI 27 u and the rest, and the arrows and the first twelve function keys keep the shapes every terminal has always used, so all of those go through #control_key and the tables above instead. The gap below 57358 is unassigned by the protocol; a code in it is delivered as the character it nominally is.

See https://sw.kovidgoyal.net/kitty/keyboard-protocol/.

LETTER_KEYS = {'A' => Key::Name::Up, 'B' => Key::Name::Down, 'C' => Key::Name::Right, 'D' => Key::Name::Left, 'H' => Key::Name::Home, 'F' => Key::Name::End, 'E' => Key::Name::Begin, 'P' => Key::Name::F1, 'Q' => Key::Name::F2, 'R' => Key::Name::F3, 'S' => Key::Name::F4}

Arrows, and everything sharing their shape, in the ESC [ 1 ; m x form.

MAX_PASTE = (4 * 1024) * 1024

Where a paste stops being a paste and starts being a denial of service. Reached only by a terminal that sent an opening marker and no closing one.

MINIMUM_DEADLINE = 1.millisecond

Never ask for a read deadline shorter than this. A deadline that has already passed would otherwise become a zero or negative timeout.

PASTE_END = "\e[201~".to_slice

The bracketed paste markers. Everything between them is text that was on the clipboard, delivered as one Events::Paste rather than as keys.

PASTE_NOTICE = 300.milliseconds

How long a paste has to have been arriving before it is worth telling the application about. Measured from the opening marker whether or not anything followed it.

PASTE_PROGRESS = 100.milliseconds

How often the byte count is worth resending. A paste large enough to notice arrives in hundreds of reads, and an application draining the channel slowly should not be made to drain hundreds of notices.

PASTE_STALL = 3.seconds

How long a paste may go without a single byte before it is treated as abandoned.

Reset by every byte rather than measured from the opening marker, because the question is whether the paste is slow or stopped and the only evidence either way is whether anything is still arriving. A paste over a link with seconds of latency stays alive as long as it makes progress; one whose closing marker will never come ends here rather than swallowing every keystroke after it.

PASTE_START = "\e[200~".to_slice

The bracketed paste markers. Everything between them is text that was on the clipboard, delivered as one Events::Paste rather than as keys.

SS3_KEYS = {'A' => Key::Name::Up, 'B' => Key::Name::Down, 'C' => Key::Name::Right, 'D' => Key::Name::Left, 'H' => Key::Name::Home, 'F' => Key::Name::End, 'E' => Key::Name::Begin, 'M' => Key::Name::Enter, 'P' => Key::Name::F1, 'Q' => Key::Name::F2, 'R' => Key::Name::F3, 'S' => Key::Name::F4}

The keys ESC O x can name: the arrows, the first four function keys, and the keypad's own enter.

TILDE_KEYS = {1 => Key::Name::Home, 2 => Key::Name::Insert, 3 => Key::Name::Delete, 4 => Key::Name::End, 5 => Key::Name::PageUp, 6 => Key::Name::PageDown, 7 => Key::Name::Home, 8 => Key::Name::End, 11 => Key::Name::F1, 12 => Key::Name::F2, 13 => Key::Name::F3, 14 => Key::Name::F4, 15 => Key::Name::F5, 17 => Key::Name::F6, 18 => Key::Name::F7, 19 => Key::Name::F8, 20 => Key::Name::F9, 21 => Key::Name::F10, 23 => Key::Name::F11, 24 => Key::Name::F12, 25 => Key::Name::F13, 26 => Key::Name::F14, 28 => Key::Name::F15, 29 => Key::Name::F16, 31 => Key::Name::F17, 32 => Key::Name::F18, 33 => Key::Name::F19, 34 => Key::Name::F20}

ESC [ n ~, where n names the key. The gaps are where DEC left room for keys nobody built.

Constructors

Instance methods

decode(bytes : Bytes) : Key

What one complete escape sequence means, as a key.

Anything it cannot name is Key::Name::Unknown, including the reports that are not keys at all: an application that knows better than this reads the bytes on the Events::Key, or registers a Pattern and never gets here.

Source
escape_timeout

See ESCAPE_TIMEOUT. Worth raising over a slow link, where a sequence can take longer than that to arrive in full.

Source
escape_timeout=(escape_timeout : Time::Span)

See ESCAPE_TIMEOUT. Worth raising over a slow link, where a sequence can take longer than that to arrive in full.

Source
feed(bytes : Bytes, &emit : Event -> ) : Nil

Feeds bytes in, yielding whatever they completed.

Whatever they did not complete is held, and the caller owes it a deadline: see #read_deadline and #tick.

Source
flush

Gives up waiting and delivers what is held back for what it is: an escape that begins nothing is the escape key, and a truncated character is a broken one.

Source
kitty_keyboard=(kitty_keyboard : Bool)

Whether the terminal is speaking the kitty keyboard protocol.

Set by the driver when the capability set says the protocol was asked for. It changes nothing about how a sequence is read — a CSI ... u is decoded either way, since a terminal left in that mode by whatever ran before should still work — only about what waiting means.

With the protocol on, the escape key arrives as CSI 27 u, so a lone ESC is always the start of something longer and there is nothing to time out. Held bytes are then held until the rest of them turn up, and the decoder asks for no deadline of its own.

Source
kitty_keyboard?

Whether the terminal is speaking the kitty keyboard protocol.

Set by the driver when the capability set says the protocol was asked for. It changes nothing about how a sequence is read — a CSI ... u is decoded either way, since a terminal left in that mode by whatever ran before should still work — only about what waiting means.

With the protocol on, the escape key arrives as CSI 27 u, so a lone ESC is always the start of something longer and there is nothing to time out. Held bytes are then held until the rest of them turn up, and the decoder asks for no deadline of its own.

Source
on_sequence

Asked what a complete escape sequence means before it is treated as a key press, and answered with nil when it means nothing in particular.

This is the seam the pattern registry hangs off. The decoder deliberately does not own it: which replies an application is waiting for changes while it runs, and the decoder's own state does not.

Source
on_sequence=(on_sequence : Sequence -> Event | Nil | Nil)

Asked what a complete escape sequence means before it is treated as a key press, and answered with nil when it means nothing in particular.

This is the seam the pattern registry hangs off. The decoder deliberately does not own it: which replies an application is waiting for changes while it runs, and the decoder's own state does not.

Source
paste_notice

See PASTE_NOTICE.

Source
paste_notice=(paste_notice : Time::Span)

See PASTE_NOTICE.

Source
paste_progress

See PASTE_PROGRESS.

Source
paste_progress=(paste_progress : Time::Span)

See PASTE_PROGRESS.

Source
paste_stall

See PASTE_STALL.

Source
paste_stall=(paste_stall : Time::Span)

See PASTE_STALL.

Source
pasting?

Whether a paste is open, so that text is being collected rather than delivered as keys.

Source
pending?

Whether anything is being held back for want of more bytes.

What the escape timeout is for: a lone escape looks exactly like the start of an arrow key until enough time passes that no arrow key is coming.

Source
read_deadline

How long the reader may wait before calling #tick, or nil when it may block until something arrives.

Every piece of held state is a bet that more bytes are coming, and each one needs its losing case. With nothing held and no paste open there is nothing to time, so an idle application costs nothing.

Source
tick

Called when a read deadline expires. Works out which one it was.

Source