Skip to main content

urushi_terminal/
event.rs

1//! Terminal events normalized independently of an input backend.
2
3use std::{fmt, io, time::Duration};
4
5use crate::{Position, TerminalSize};
6
7/// Extra information requested from terminals implementing an enhanced
8/// keyboard protocol.
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
10pub struct KeyboardEnhancementFlags(u8);
11
12impl KeyboardEnhancementFlags {
13    pub const DISAMBIGUATE_ESCAPE_CODES: Self = Self(1 << 0);
14    pub const REPORT_EVENT_TYPES: Self = Self(1 << 1);
15    pub const REPORT_ALTERNATE_KEYS: Self = Self(1 << 2);
16    pub const REPORT_ALL_KEYS_AS_ESCAPE_CODES: Self = Self(1 << 3);
17
18    pub const fn empty() -> Self {
19        Self(0)
20    }
21
22    pub const fn union(self, other: Self) -> Self {
23        Self(self.0 | other.0)
24    }
25
26    pub const fn contains(self, other: Self) -> bool {
27        self.0 & other.0 == other.0
28    }
29
30    /// The Kitty keyboard protocol bit field written on the wire.
31    pub const fn bits(self) -> u8 {
32        self.0
33    }
34}
35
36impl std::ops::BitOr for KeyboardEnhancementFlags {
37    type Output = Self;
38
39    fn bitor(self, rhs: Self) -> Self::Output {
40        self.union(rhs)
41    }
42}
43
44/// One event read from an interactive terminal.
45#[derive(Clone, Debug, PartialEq, Eq)]
46#[non_exhaustive]
47pub enum Event {
48    Key(KeyEvent),
49    /// Text pasted while bracketed-paste reporting is active.
50    Paste(String),
51    Focus(FocusChange),
52    Mouse(MouseEvent),
53    /// A new terminal surface size in cells.
54    Resize(TerminalSize),
55}
56
57/// One normalized key event.
58#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
59#[non_exhaustive]
60pub struct KeyEvent {
61    pub code: KeyCode,
62    pub modifiers: Modifiers,
63    pub kind: KeyKind,
64    pub state: KeyEventState,
65}
66
67impl KeyEvent {
68    /// Creates an unmodified key-press event.
69    pub const fn new(code: KeyCode) -> Self {
70        Self {
71            code,
72            modifiers: Modifiers::NONE,
73            kind: KeyKind::Press,
74            state: KeyEventState::NONE,
75        }
76    }
77
78    pub const fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
79        self.modifiers = modifiers;
80        self
81    }
82
83    pub const fn with_kind(mut self, kind: KeyKind) -> Self {
84        self.kind = kind;
85        self
86    }
87
88    pub const fn with_state(mut self, state: KeyEventState) -> Self {
89        self.state = state;
90        self
91    }
92}
93
94/// Additional state reported with an enhanced keyboard event.
95#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
96pub struct KeyEventState(u8);
97
98impl KeyEventState {
99    pub const NONE: Self = Self(0);
100    pub const KEYPAD: Self = Self(1 << 0);
101    pub const CAPS_LOCK: Self = Self(1 << 1);
102    pub const NUM_LOCK: Self = Self(1 << 2);
103
104    pub const fn union(self, other: Self) -> Self {
105        Self(self.0 | other.0)
106    }
107
108    pub const fn contains(self, other: Self) -> bool {
109        self.0 & other.0 == other.0
110    }
111}
112
113impl std::ops::BitOr for KeyEventState {
114    type Output = Self;
115
116    fn bitor(self, rhs: Self) -> Self::Output {
117        self.union(rhs)
118    }
119}
120
121/// The transition represented by a key event.
122#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
123pub enum KeyKind {
124    #[default]
125    Press,
126    Repeat,
127    Release,
128}
129
130/// A backend-independent terminal key.
131#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
132#[non_exhaustive]
133pub enum KeyCode {
134    Char(char),
135    Enter,
136    Tab,
137    BackTab,
138    Backspace,
139    Delete,
140    Escape,
141    Insert,
142    Home,
143    End,
144    PageUp,
145    PageDown,
146    Up,
147    Down,
148    Left,
149    Right,
150    Function(u8),
151    Null,
152    CapsLock,
153    ScrollLock,
154    NumLock,
155    PrintScreen,
156    Pause,
157    Menu,
158    KeypadBegin,
159    Media(MediaKeyCode),
160    Modifier(ModifierKeyCode),
161}
162
163#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
164pub enum MediaKeyCode {
165    Play,
166    Pause,
167    PlayPause,
168    Reverse,
169    Stop,
170    FastForward,
171    Rewind,
172    TrackNext,
173    TrackPrevious,
174    Record,
175    LowerVolume,
176    RaiseVolume,
177    MuteVolume,
178}
179
180#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
181pub enum ModifierKeyCode {
182    LeftShift,
183    LeftControl,
184    LeftAlt,
185    LeftSuper,
186    LeftHyper,
187    LeftMeta,
188    RightShift,
189    RightControl,
190    RightAlt,
191    RightSuper,
192    RightHyper,
193    RightMeta,
194    IsoLevel3Shift,
195    IsoLevel5Shift,
196}
197
198/// The keyboard modifiers held for one input event.
199#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
200pub struct Modifiers(u8);
201
202impl Modifiers {
203    pub const NONE: Self = Self(0);
204    pub const SHIFT: Self = Self(1 << 0);
205    pub const CONTROL: Self = Self(1 << 1);
206    pub const ALT: Self = Self(1 << 2);
207    pub const SUPER: Self = Self(1 << 3);
208    pub const HYPER: Self = Self(1 << 4);
209    pub const META: Self = Self(1 << 5);
210
211    pub const fn union(self, other: Self) -> Self {
212        Self(self.0 | other.0)
213    }
214
215    pub const fn contains(self, other: Self) -> bool {
216        self.0 & other.0 == other.0
217    }
218}
219
220impl std::ops::BitOr for Modifiers {
221    type Output = Self;
222
223    fn bitor(self, rhs: Self) -> Self::Output {
224        self.union(rhs)
225    }
226}
227
228impl fmt::Debug for Modifiers {
229    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
230        let mut first = true;
231        formatter.write_str("Modifiers(")?;
232        for (name, held) in [
233            ("shift", self.contains(Self::SHIFT)),
234            ("control", self.contains(Self::CONTROL)),
235            ("alt", self.contains(Self::ALT)),
236            ("super", self.contains(Self::SUPER)),
237            ("hyper", self.contains(Self::HYPER)),
238            ("meta", self.contains(Self::META)),
239        ] {
240            if held {
241                if !first {
242                    formatter.write_str("+")?;
243                }
244                formatter.write_str(name)?;
245                first = false;
246            }
247        }
248        if first {
249            formatter.write_str("none")?;
250        }
251        formatter.write_str(")")
252    }
253}
254
255#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
256pub enum FocusChange {
257    Gained,
258    Lost,
259}
260
261/// One normalized mouse event at a zero-based cell position.
262#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
263pub struct MouseEvent {
264    pub kind: MouseKind,
265    pub position: Position,
266    pub modifiers: Modifiers,
267}
268
269/// A normalized mouse action.
270#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
271#[non_exhaustive]
272pub enum MouseKind {
273    Down(MouseButton),
274    Up(MouseButton),
275    Drag(MouseButton),
276    Moved,
277    ScrollDown,
278    ScrollUp,
279    ScrollLeft,
280    ScrollRight,
281}
282
283#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
284pub enum MouseButton {
285    Left,
286    Right,
287    Middle,
288}
289
290/// Blocking and non-blocking reads from one normalized event source.
291pub trait EventSource {
292    /// Blocks until one supported terminal event is available.
293    fn read_event(&mut self) -> io::Result<Event>;
294
295    /// Returns one already-waiting event without blocking.
296    fn poll_event(&mut self) -> io::Result<Option<Event>>;
297
298    /// Waits up to `timeout` for one event.
299    fn poll_event_timeout(&mut self, timeout: Duration) -> io::Result<Option<Event>>;
300}
301
302impl<T: EventSource + ?Sized> EventSource for &mut T {
303    fn read_event(&mut self) -> io::Result<Event> {
304        T::read_event(self)
305    }
306
307    fn poll_event(&mut self) -> io::Result<Option<Event>> {
308        T::poll_event(self)
309    }
310
311    fn poll_event_timeout(&mut self, timeout: Duration) -> io::Result<Option<Event>> {
312        T::poll_event_timeout(self, timeout)
313    }
314}