termina 0.3.3

A cross-platform VT manipulation library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
//! Terminal input events.
//!
//! [`Event`] is the typed output of [`Parser`], [`EventReader`], and the optional `EventStream`.
//! Termina reports ordinary input events such as keys, mouse input, focus changes, resize events,
//! and bracketed paste. It also keeps terminal responses such as CSI, OSC, and DCS in the same
//! public enum so callers can issue a query and read the response without a second internal event
//! model.
//!
//! Use [`EventReader::read`] or [`Terminal::read`] when reading from a process terminal. Use
//! [`Parser::pop`] when parsing bytes from a PTY, terminal multiplexer, or other caller-owned
//! input source. [`EventReader`] is the main place to look for event-reading examples and filter
//! behavior.
//!
//! # Implementation Notes
//!
//! Most event code is adapted from [crossterm events]. The main difference is intentional: Termina
//! includes escape sequences like [`Csi`] and [`Dcs`] in [`Event`] and does not split the model
//! into separate internal and public events. The key event types otherwise stay close to
//! crossterm's shape.
//!
//! [crossterm events]: https://docs.rs/crossterm/latest/crossterm/event/index.html
//! [`Csi`]: crate::escape::csi::Csi
//! [`Dcs`]: crate::escape::dcs::Dcs
//! [`EventReader`]: crate::EventReader
//! [`EventReader::read`]: crate::EventReader::read
//! [`Parser`]: crate::Parser
//! [`Parser::pop`]: crate::Parser::pop
//! [`Terminal::read`]: crate::Terminal::read

use crate::{
    escape::{csi::Csi, dcs::Dcs, osc::Osc},
    WindowSize,
};

#[cfg(doc)]
use crate::escape::csi::{DecPrivateModeCode, KittyKeyboardFlags};
#[cfg(doc)]
use crate::{EventReader, Parser, Terminal};

pub(crate) mod reader;
pub(crate) mod source;
#[cfg(feature = "event-stream")]
pub(crate) mod stream;

/// A parsed terminal input event or terminal protocol response.
///
/// Values of this type are returned by [`EventReader::read`], [`Terminal::read`], and
/// [`Parser::pop`]. See [`EventReader`] for the normal terminal-reading flow, including how
/// filters skip events without losing them.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
    /// A keyboard event described by [`KeyEvent`].
    ///
    /// Check [`KeyEvent::kind`] when a binding should run once per physical key press. Some input
    /// sources report both press and release for the same key, so handling every `Event::Key`
    /// without checking for [`KeyEventKind::Press`] can run the binding twice.
    Key(KeyEvent),

    /// A mouse event described by [`MouseEvent`].
    ///
    /// Terminals produce these after an application enables mouse tracking with modes such as
    /// [`DecPrivateModeCode::MouseTracking`], [`DecPrivateModeCode::ButtonEventMouse`], or
    /// [`DecPrivateModeCode::AnyEventMouse`].
    Mouse(MouseEvent),

    /// The terminal window was resized to the given [`WindowSize`].
    WindowResized(WindowSize),

    /// Terminal focus entered the application window.
    ///
    /// Terminals send this only after [`DecPrivateModeCode::FocusTracking`] has enabled focus
    /// tracking.
    FocusIn,

    /// Terminal focus left the application window.
    ///
    /// Terminals send this only after [`DecPrivateModeCode::FocusTracking`] has enabled focus
    /// tracking.
    FocusOut,

    /// A "bracketed" paste.
    ///
    /// Normally pasting into a terminal with Ctrl+v (or Super+v) enters the pasted text as if
    /// you had typed the keys individually. [`DecPrivateModeCode::BracketedPaste`] asks compatible
    /// terminals to wrap pasted text in explicit start/end markers so Termina can deliver the
    /// entire pasted content as one event. xterm documents this as [bracketed paste mode].
    ///
    /// [bracketed paste mode]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
    Paste(String),

    /// A parsed CSI response or report described by [`Csi`].
    ///
    /// Applications see this when the terminal sends a Control Sequence Introducer response, such
    /// as a cursor position report, device attributes, mode report, or [`Csi::Keyboard`] protocol
    /// report.
    Csi(Csi),

    /// A parsed OSC response described by [`Osc`].
    ///
    /// Applications see this when the terminal answers an Operating System Command query, such as a
    /// dynamic color query.
    Osc(Osc<'static>),

    /// A parsed DCS response described by [`Dcs`].
    ///
    /// Applications see this when the terminal answers a Device Control String query, such as
    /// DECRQSS.
    Dcs(Dcs),
}

impl Event {
    /// Returns `true` for CSI, OSC, and DCS protocol responses.
    #[inline]
    pub fn is_escape(&self) -> bool {
        matches!(self, Self::Csi(_) | Self::Dcs(_) | Self::Osc(_))
    }
}

/// A key event plus modifiers and protocol state.
///
/// `KeyEvent` appears inside [`Event::Key`], which is normally returned by [`EventReader::read`]
/// or [`Terminal::read`]. See [`EventReader`] for examples of filtering key events while leaving
/// other terminal events buffered.
///
/// `code` identifies the key, `kind` distinguishes press/release/repeat when the terminal reports
/// that detail, `modifiers` carries held modifier keys, and `state` carries protocol state such as
/// keypad-originated input.
///
/// Code that handles shortcuts should usually check `kind == KeyEventKind::Press` before acting.
/// Unix-style terminal input commonly reports only presses unless a keyboard enhancement protocol
/// requests event types, but the Windows legacy console API reports press and release records for
/// many keys. Ignoring `kind` can make a shortcut run twice on backends that expose releases.
///
/// Some key combinations also cannot be represented by some terminals at all. Crossterm's
/// [missing key combinations] issue is a useful catalogue of those terminal-level limitations;
/// report Termina bugs in Termina, not on that upstream issue.
///
/// # Implementation Notes
///
/// This mirrors the layout used by [crossterm key events].
///
/// [crossterm key events]: https://docs.rs/crossterm/latest/crossterm/event/struct.KeyEvent.html
/// [missing key combinations]: https://github.com/crossterm-rs/crossterm/issues/685
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KeyEvent {
    /// The key identity.
    pub code: KeyCode,

    /// Whether this event is a press, release, or repeat.
    ///
    /// Check this before triggering a shortcut or command. If the command should run once per
    /// physical key press, require [`KeyEventKind::Press`] and ignore release events.
    pub kind: KeyEventKind,

    /// Modifier keys active for this key event.
    ///
    /// This is the modifier state Termina could infer from the terminal protocol or platform
    /// backend. Plain terminal input does not report every modifier independently; for example,
    /// Shift may be inferred from an uppercase decoded character, while lock-key and keypad state
    /// require an enhanced keyboard protocol or platform backend that reports them.
    pub modifiers: Modifiers,

    /// Extra key state reported by the terminal protocol.
    ///
    /// This is empty unless the input source reports state outside the ordinary modifier mask,
    /// such as keypad-originated input, Caps Lock, or Num Lock.
    pub state: KeyEventState,
}

impl KeyEvent {
    /// Creates a key-press event with the given key code and modifiers.
    pub const fn new(code: KeyCode, modifiers: Modifiers) -> Self {
        Self {
            code,
            modifiers,
            kind: KeyEventKind::Press,
            state: KeyEventState::NONE,
        }
    }
}

impl From<KeyCode> for KeyEvent {
    fn from(code: KeyCode) -> Self {
        Self {
            code,
            kind: KeyEventKind::Press,
            modifiers: Modifiers::NONE,
            state: KeyEventState::NONE,
        }
    }
}

/// Whether a key was pressed, released, or repeated.
///
/// This controls whether a key event should trigger an action. Unix-style terminal input commonly
/// produces [`Self::Press`] only, while Windows legacy console input and enhanced keyboard
/// protocols can also produce [`Self::Release`] and [`Self::Repeat`]. Code that treats every
/// [`Event::Key`] as an action can therefore run twice for one physical key press. Shortcuts and
/// commands should usually act only on [`Self::Press`]. Some key combinations are also limited by
/// what the terminal can encode; Crossterm's [missing key combinations] issue is useful background
/// for those limitations, but Termina bugs should be reported to Termina.
///
/// [missing key combinations]: https://github.com/crossterm-rs/crossterm/issues/685
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyEventKind {
    /// A key was pressed.
    Press,

    /// A key was released.
    ///
    /// Terminals report releases when a keyboard protocol flag such as
    /// [`KittyKeyboardFlags::REPORT_EVENT_TYPES`] requests event types, or when the platform
    /// backend exposes releases directly.
    Release,

    /// A key press was repeated while held.
    Repeat,
}

bitflags::bitflags! {
    /// Modifier keys active during a key or mouse event.
    ///
    /// Terminals vary in which modifiers they report. Treat these flags as the state Termina
    /// observed, not as proof that every unlisted physical modifier was inactive.
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub struct Modifiers: u8 {
        /// No modifier keys were active.
        const NONE = 0;

        /// Shift was active.
        const SHIFT = 1;

        /// Alt or Option was active.
        const ALT = 1 << 1;

        /// Control was active.
        const CONTROL = 1 << 2;

        /// Super key.
        ///
        /// This is Command on macOS, the Windows key on Windows, and commonly the Super key on
        /// Linux and other Unix-like systems.
        const SUPER = 1 << 3;
        /// Hyper key.
        ///
        /// Hyper is an additional modifier from older keyboard layouts and terminal protocols. It
        /// is uncommon on modern Mac and Windows keyboards, but some terminals can still report it.
        const HYPER = 1 << 4;

        /// Meta was active.
        const META = 1 << 5;

        /// Caps Lock was active.
        const CAPS_LOCK = 1 << 6;

        /// Num Lock was active.
        const NUM_LOCK = 1 << 7;
    }
}

bitflags::bitflags! {
    /// Extra key state reported by the terminal or platform backend.
    ///
    /// These flags are present only when the input source reports them. Ordinary terminal input
    /// often cannot distinguish keypad-originated keys or lock-key state.
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub struct KeyEventState: u8 {
        /// No extra key state was reported.
        const NONE = 0;

        /// The key came from the keypad.
        const KEYPAD = 1 << 1;

        /// Caps Lock was active for this key event.
        const CAPS_LOCK = 1 << 2;

        /// Num Lock was active for this key event.
        const NUM_LOCK = 1 << 3;
    }
}

/// The key identity reported by the terminal.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyCode {
    /// A Unicode character key after terminal decoding.
    ///
    /// Termina stores the character it can identify from the input source rather than normalizing
    /// every alphabetic key to lowercase plus [`Modifiers::SHIFT`]. For ordinary UTF-8 terminal
    /// input, `A` is reported as `Char('A')` and Shift is also recorded in `modifiers` when Termina
    /// can infer it from the decoded character. Control bytes such as Ctrl+A are reported as
    /// `Char('a')` with [`Modifiers::CONTROL`]. Enhanced keyboard protocols and the Windows
    /// backend may use layout-aware or protocol-supplied characters, so callers should compare
    /// against the character they want to handle instead of assuming another terminal library's
    /// capitalization model.
    Char(char),

    /// The Enter or Return key.
    Enter,

    /// The Backspace key.
    Backspace,

    /// The Tab key.
    Tab,

    /// The Escape key.
    Escape,

    /// The left arrow key.
    Left,

    /// The right arrow key.
    Right,

    /// The up arrow key.
    Up,

    /// The down arrow key.
    Down,

    /// The Home key.
    Home,

    /// The End key.
    End,

    /// Shift+Tab or another backwards-tab key sequence.
    BackTab,

    /// The Page Up key.
    PageUp,

    /// The Page Down key.
    PageDown,

    /// The Insert key.
    Insert,

    /// The Delete key.
    Delete,

    /// The keypad begin key.
    KeypadBegin,

    /// The Caps Lock key.
    CapsLock,

    /// The Scroll Lock key.
    ScrollLock,

    /// The Num Lock key.
    NumLock,

    /// The Print Screen key.
    PrintScreen,

    /// The Pause or Break key.
    Pause,

    /// The Menu or Application key.
    Menu,

    /// A null key code.
    ///
    /// This can appear when a platform backend reports a key event without a printable or named
    /// key identity.
    Null,

    /// F1-F35 function keys.
    Function(u8),

    /// A modifier key such as Shift, Control, Alt, Super, Hyper, or Meta.
    Modifier(ModifierKeyCode),

    /// A media control key.
    Media(MediaKeyCode),
}

/// Physical modifier keys reported as key events.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModifierKeyCode {
    /// Left Shift key.
    LeftShift,
    /// Left Control key. (Control on macOS, Ctrl on other platforms)
    LeftControl,
    /// Left Alt key. (Option on macOS, Alt on other platforms)
    LeftAlt,
    /// Left Super key. (Command on macOS, Windows key on Windows, Super on Linux)
    LeftSuper,
    /// Left Hyper key. (An extended modifier used by some keyboards and terminal protocols)
    LeftHyper,
    /// Left Meta key.
    LeftMeta,
    /// Right Shift key.
    RightShift,
    /// Right Control key. (Control on macOS, Ctrl on other platforms)
    RightControl,
    /// Right Alt key. (Option on macOS, Alt on other platforms)
    RightAlt,
    /// Right Super key. (Command on macOS, Windows key on Windows, Super on Linux)
    RightSuper,
    /// Right Hyper key. (An extended modifier used by some keyboards and terminal protocols)
    RightHyper,
    /// Right Meta key.
    RightMeta,
    /// Iso Level3 Shift key.
    IsoLevel3Shift,
    /// Iso Level5 Shift key.
    IsoLevel5Shift,
}

/// Media keys reported as key events.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MediaKeyCode {
    /// Play media key.
    Play,
    /// Pause media key.
    Pause,
    /// Play/Pause media key.
    PlayPause,
    /// Reverse media key.
    Reverse,
    /// Stop media key.
    Stop,
    /// Fast-forward media key.
    FastForward,
    /// Rewind media key.
    Rewind,
    /// Next-track media key.
    TrackNext,
    /// Previous-track media key.
    TrackPrevious,
    /// Record media key.
    Record,
    /// Lower-volume media key.
    LowerVolume,
    /// Raise-volume media key.
    RaiseVolume,
    /// Mute media key.
    MuteVolume,
}

/// Mouse input event with zero-based terminal cell coordinates.
///
/// Terminal mouse protocols encode cell positions as one-based coordinates, but Termina converts
/// them to zero-based `column` and `row` values for consistency with Rust indexing and the parser's
/// existing event model. SGR pixel mouse reports are represented separately as
/// [`crate::escape::csi::MouseReport::Sgr1016`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MouseEvent {
    /// The mouse action.
    pub kind: MouseEventKind,

    /// The zero-based terminal column where the event occurred.
    pub column: u16,

    /// The zero-based terminal row where the event occurred.
    pub row: u16,

    /// The key modifiers active when the event occurred.
    pub modifiers: Modifiers,
}

/// The mouse action reported by the terminal.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseEventKind {
    /// A mouse button was pressed.
    Down(MouseButton),

    /// A mouse button was released.
    Up(MouseButton),

    /// The pointer moved while a mouse button was pressed.
    Drag(MouseButton),

    /// The pointer moved without a pressed mouse button.
    Moved,

    /// The wheel scrolled down, usually toward the user.
    ScrollDown,

    /// The wheel scrolled up, usually away from the user.
    ScrollUp,

    /// The wheel or touchpad scrolled left.
    ScrollLeft,

    /// The wheel or touchpad scrolled right.
    ScrollRight,
}

/// Mouse buttons reported by terminal mouse tracking.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseButton {
    /// Left mouse button.
    Left,
    /// Right mouse button.
    Right,
    /// Middle mouse button.
    Middle,
}