Skip to main content

gpui_cn/terminal/
input.rs

1//! Input the UI sends to the engine.
2//!
3//! The engine owns the Ghostty encoders and the terminal modes, so the UI
4//! sends typed requests and the owner thread encodes them.
5//!
6//! Derived from Herdr (Apache-2.0).
7
8/// Modifier keys held during an input event.
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
10#[non_exhaustive]
11pub struct Modifiers {
12    /// Shift.
13    pub shift: bool,
14    /// Control.
15    pub control: bool,
16    /// Alt or Option.
17    pub alt: bool,
18    /// Super, Command or the Windows key.
19    pub super_key: bool,
20}
21
22/// What happened to a key.
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24pub enum KeyAction {
25    /// Pressed.
26    #[default]
27    Press,
28    /// Held and repeating.
29    Repeat,
30    /// Released. Only encoded under the Kitty keyboard protocol.
31    Release,
32}
33
34/// A key event in GPUI terms.
35#[derive(Clone, Debug, Default, PartialEq, Eq)]
36#[non_exhaustive]
37pub struct KeyInput {
38    /// GPUI's key name, such as `enter`, `left`, `f1` or `a`.
39    pub key: String,
40    /// The text the key produces on the current layout, if any.
41    pub text: Option<String>,
42    /// Modifiers held.
43    pub modifiers: Modifiers,
44    /// Press, repeat or release.
45    pub action: KeyAction,
46}
47
48impl KeyInput {
49    /// A key press with no modifiers and no text.
50    #[must_use]
51    pub fn new(key: impl Into<String>) -> Self {
52        Self {
53            key: key.into(),
54            ..Self::default()
55        }
56    }
57
58    /// Set the produced text.
59    #[must_use]
60    pub fn with_text(mut self, text: impl Into<String>) -> Self {
61        self.text = Some(text.into());
62        self
63    }
64
65    /// Set the modifiers.
66    #[must_use]
67    pub fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
68        self.modifiers = modifiers;
69        self
70    }
71
72    /// Set the action.
73    #[must_use]
74    pub fn with_action(mut self, action: KeyAction) -> Self {
75        self.action = action;
76        self
77    }
78}
79
80/// A pointer action.
81#[derive(Clone, Copy, Debug, PartialEq, Eq)]
82pub enum MouseAction {
83    /// A button went down.
84    Press,
85    /// A button went up.
86    Release,
87    /// The pointer moved.
88    Move,
89    /// The wheel scrolled up.
90    ScrollUp,
91    /// The wheel scrolled down.
92    ScrollDown,
93}
94
95/// A pointer button.
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
97pub enum MouseButton {
98    /// Left.
99    Left,
100    /// Middle.
101    Middle,
102    /// Right.
103    Right,
104}
105
106/// A pointer event for a program that reports the mouse.
107#[derive(Clone, Copy, Debug, PartialEq)]
108#[non_exhaustive]
109pub struct MouseInput {
110    /// What happened.
111    pub action: MouseAction,
112    /// The button, when one is involved.
113    pub button: Option<MouseButton>,
114    /// Modifiers held.
115    pub modifiers: Modifiers,
116    /// X in device pixels from the grid origin.
117    pub x: f32,
118    /// Y in device pixels from the grid origin.
119    pub y: f32,
120}
121
122impl MouseInput {
123    /// A pointer event at a grid position.
124    #[must_use]
125    pub fn new(action: MouseAction, x: f32, y: f32) -> Self {
126        Self {
127            action,
128            button: None,
129            modifiers: Modifiers::default(),
130            x,
131            y,
132        }
133    }
134
135    /// Set the button.
136    #[must_use]
137    pub fn with_button(mut self, button: MouseButton) -> Self {
138        self.button = Some(button);
139        self
140    }
141
142    /// Set the modifiers.
143    #[must_use]
144    pub fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
145        self.modifiers = modifiers;
146        self
147    }
148}
149
150/// A viewport scroll request.
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152pub enum ScrollRequest {
153    /// Scroll by rows; negative is toward older output.
154    Lines(isize),
155    /// Scroll to the top of the scrollback.
156    Top,
157    /// Scroll to the bottom, the active area.
158    Bottom,
159}
160
161/// Everything the UI can send to the engine.
162#[derive(Clone, Debug, PartialEq)]
163#[non_exhaustive]
164pub enum TerminalInput {
165    /// Raw text for the program, such as an IME commit.
166    Text(String),
167    /// Pasted text, bracketed when the program asked for it.
168    Paste(String),
169    /// A key.
170    Key(KeyInput),
171    /// A pointer event.
172    Mouse(MouseInput),
173    /// A viewport scroll.
174    Scroll(ScrollRequest),
175    /// Focus gained or lost.
176    Focus(bool),
177}
178
179impl TerminalInput {
180    /// Whether this input interrupts the program (Ctrl+C), which jumps the
181    /// queue and discards pending input.
182    #[must_use]
183    pub fn is_interrupt(&self) -> bool {
184        match self {
185            Self::Key(key) => {
186                key.key.eq_ignore_ascii_case("c")
187                    && key.modifiers.control
188                    && !key.modifiers.alt
189                    && !key.modifiers.super_key
190                    && key.action != KeyAction::Release
191            }
192            Self::Text(text) => text == "\x03",
193            _ => false,
194        }
195    }
196
197    /// The bytes this input accounts for in the bounded queue.
198    #[must_use]
199    pub fn budget(&self) -> usize {
200        match self {
201            Self::Text(s) | Self::Paste(s) => s.len(),
202            Self::Key(k) => k.key.len() + k.text.as_ref().map_or(0, String::len),
203            _ => 0,
204        }
205    }
206}