crossterm_winapi/structs/
input.rs

1//! This module provides a few structs to wrap common input struts to a rusty interface
2//!
3//! Types like:
4//! - `KEY_EVENT_RECORD`
5//! - `MOUSE_EVENT_RECORD`
6//! - `ControlKeyState`
7//! - `ButtonState`
8//! - `EventFlags`
9//! - `InputEventType`
10//! - `INPUT_RECORD`
11
12use winapi::shared::minwindef::DWORD;
13use winapi::um::wincon::{
14    FOCUS_EVENT, FOCUS_EVENT_RECORD, FROM_LEFT_1ST_BUTTON_PRESSED, FROM_LEFT_2ND_BUTTON_PRESSED,
15    FROM_LEFT_3RD_BUTTON_PRESSED, FROM_LEFT_4TH_BUTTON_PRESSED, INPUT_RECORD, KEY_EVENT,
16    KEY_EVENT_RECORD, MENU_EVENT, MENU_EVENT_RECORD, MOUSE_EVENT, MOUSE_EVENT_RECORD,
17    RIGHTMOST_BUTTON_PRESSED, WINDOW_BUFFER_SIZE_EVENT, WINDOW_BUFFER_SIZE_RECORD,
18};
19
20use super::Coord;
21use crate::ScreenBuffer;
22
23/// A [keyboard input event](https://docs.microsoft.com/en-us/windows/console/key-event-record-str).
24#[derive(Clone, Debug, Eq, PartialEq)]
25pub struct KeyEventRecord {
26    /// If the key is pressed, this member is true. Otherwise, this member is
27    /// false (the key is released).
28    pub key_down: bool,
29    /// The repeat count, which indicates that a key is being held down.
30    /// For example, when a key is held down, you might get five events with
31    /// this member equal to 1, one event with this member equal to 5, or
32    /// multiple events with this member greater than or equal to 1.
33    pub repeat_count: u16,
34    /// A virtual-key code that identifies the given key in a
35    /// device-independent manner.
36    pub virtual_key_code: u16,
37    /// The virtual scan code of the given key that represents the
38    /// device-dependent value generated by the keyboard hardware.
39    pub virtual_scan_code: u16,
40    /// The translated Unicode character (as a WCHAR, or utf-16 value)
41    pub u_char: u16,
42    /// The state of the control keys.
43    pub control_key_state: ControlKeyState,
44}
45
46impl KeyEventRecord {
47    /// Convert a `KEY_EVENT_RECORD` to KeyEventRecord. This function is private
48    /// because the `KEY_EVENT_RECORD` has several union fields for characters
49    /// (u8 vs u16) that we always interpret as u16. We always use the wide
50    /// versions of windows API calls to support this.
51    #[inline]
52    fn from_winapi(record: &KEY_EVENT_RECORD) -> Self {
53        KeyEventRecord {
54            key_down: record.bKeyDown != 0,
55            repeat_count: record.wRepeatCount,
56            virtual_key_code: record.wVirtualKeyCode,
57            virtual_scan_code: record.wVirtualScanCode,
58            u_char: unsafe { *record.uChar.UnicodeChar() },
59            control_key_state: ControlKeyState(record.dwControlKeyState),
60        }
61    }
62}
63
64/// A [mouse input event](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
65#[derive(PartialEq, Debug, Copy, Clone, Eq)]
66pub struct MouseEvent {
67    /// The position of the mouse when the event occurred in cell coordinates.
68    pub mouse_position: Coord,
69    /// The state of the mouse's buttons.
70    pub button_state: ButtonState,
71    /// The state of the control keys.
72    pub control_key_state: ControlKeyState,
73    /// What type of mouse event it is.
74    pub event_flags: EventFlags,
75}
76
77impl From<MOUSE_EVENT_RECORD> for MouseEvent {
78    #[inline]
79    fn from(event: MOUSE_EVENT_RECORD) -> Self {
80        MouseEvent {
81            mouse_position: event.dwMousePosition.into(),
82            button_state: event.dwButtonState.into(),
83            control_key_state: ControlKeyState(event.dwControlKeyState),
84            event_flags: event.dwEventFlags.into(),
85        }
86    }
87}
88
89/// The status of the mouse buttons.
90/// The least significant bit corresponds to the leftmost mouse button.
91/// The next least significant bit corresponds to the rightmost mouse button.
92/// The next bit indicates the next-to-leftmost mouse button.
93/// The bits then correspond left to right to the mouse buttons.
94/// A bit is 1 if the button was pressed.
95///
96/// The state can be one of the following:
97///
98/// ```
99/// # enum __ {
100/// Release = 0x0000,
101/// /// The leftmost mouse button.
102/// FromLeft1stButtonPressed = 0x0001,
103/// /// The second button from the left.
104/// FromLeft2ndButtonPressed = 0x0004,
105/// /// The third button from the left.
106/// FromLeft3rdButtonPressed = 0x0008,
107/// /// The fourth button from the left.
108/// FromLeft4thButtonPressed = 0x0010,
109/// /// The rightmost mouse button.
110/// RightmostButtonPressed = 0x0002,
111/// /// This button state is not recognized.
112/// Unknown = 0x0021,
113/// /// The wheel was rotated backward, toward the user; this will only be activated for `MOUSE_WHEELED ` from `dwEventFlags`
114/// Negative = 0x0020,
115/// # }
116/// ```
117///
118/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str#members)
119#[derive(PartialEq, Debug, Copy, Clone, Eq)]
120pub struct ButtonState {
121    state: i32,
122}
123
124impl From<DWORD> for ButtonState {
125    #[inline]
126    fn from(event: DWORD) -> Self {
127        let state = event as i32;
128        ButtonState { state }
129    }
130}
131
132impl ButtonState {
133    /// Get whether no buttons are being pressed.
134    pub fn release_button(&self) -> bool {
135        self.state == 0
136    }
137
138    /// Returns whether the left button was pressed.
139    pub fn left_button(&self) -> bool {
140        self.state as u32 & FROM_LEFT_1ST_BUTTON_PRESSED != 0
141    }
142
143    /// Returns whether the right button was pressed.
144    pub fn right_button(&self) -> bool {
145        self.state as u32
146            & (RIGHTMOST_BUTTON_PRESSED
147                | FROM_LEFT_3RD_BUTTON_PRESSED
148                | FROM_LEFT_4TH_BUTTON_PRESSED)
149            != 0
150    }
151
152    /// Returns whether the right button was pressed.
153    pub fn middle_button(&self) -> bool {
154        self.state as u32 & FROM_LEFT_2ND_BUTTON_PRESSED != 0
155    }
156
157    /// Returns whether there is a down scroll.
158    pub fn scroll_down(&self) -> bool {
159        self.state < 0
160    }
161
162    /// Returns whether there is a up scroll.
163    pub fn scroll_up(&self) -> bool {
164        self.state > 0
165    }
166
167    /// Returns whether there is a horizontal scroll to the right.
168    pub fn scroll_right(&self) -> bool {
169        self.state > 0
170    }
171
172    /// Returns whether there is a horizontal scroll to the left.
173    pub fn scroll_left(&self) -> bool {
174        self.state < 0
175    }
176
177    /// Returns the raw state.
178    pub fn state(&self) -> i32 {
179        self.state
180    }
181}
182
183/// The state of the control keys.
184///
185/// This is a bitmask of the following values.
186///
187/// | Description | Value |
188/// | --- | --- |
189/// | The right alt key is pressed | `0x0001` |
190/// | The left alt key is pressed | `x0002` |
191/// | The right control key is pressed | `0x0004` |
192/// | The left control key is pressed | `x0008` |
193/// | The shift key is pressed | `0x0010` |
194/// | The num lock light is on | `0x0020` |
195/// | The scroll lock light is on | `0x0040` |
196/// | The caps lock light is on | `0x0080` |
197/// | The key is [enhanced](https://docs.microsoft.com/en-us/windows/console/key-event-record-str#remarks) | `0x0100` |
198#[derive(PartialEq, Debug, Copy, Clone, Eq)]
199pub struct ControlKeyState(u32);
200
201impl ControlKeyState {
202    /// Whether the control key has a state.
203    pub fn has_state(&self, state: u32) -> bool {
204        (state & self.0) != 0
205    }
206}
207
208/// The type of mouse event.
209/// If this value is zero, it indicates a mouse button being pressed or released.
210/// Otherwise, this member is one of the following values.
211///
212/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str#members)
213#[derive(PartialEq, Debug, Copy, Clone, Eq)]
214pub enum EventFlags {
215    PressOrRelease = 0x0000,
216    /// The second click (button press) of a double-click occurred. The first click is returned as a regular button-press event.
217    DoubleClick = 0x0002,
218    /// The horizontal mouse wheel was moved.
219    MouseHwheeled = 0x0008,
220    /// If the high word of the dwButtonState member contains a positive value, the wheel was rotated to the right. Otherwise, the wheel was rotated to the left.
221    MouseMoved = 0x0001,
222    /// A change in mouse position occurred.
223    /// The vertical mouse wheel was moved, if the high word of the dwButtonState member contains a positive value, the wheel was rotated forward, away from the user.
224    /// Otherwise, the wheel was rotated backward, toward the user.
225    MouseWheeled = 0x0004,
226    // This button state is not recognized.
227    Unknown = 0x0021,
228}
229
230// TODO: Replace with TryFrom.
231impl From<DWORD> for EventFlags {
232    fn from(event: DWORD) -> Self {
233        match event {
234            0x0000 => EventFlags::PressOrRelease,
235            0x0002 => EventFlags::DoubleClick,
236            0x0008 => EventFlags::MouseHwheeled,
237            0x0001 => EventFlags::MouseMoved,
238            0x0004 => EventFlags::MouseWheeled,
239            _ => EventFlags::Unknown,
240        }
241    }
242}
243
244/// The [size of console screen
245/// buffer](https://docs.microsoft.com/en-us/windows/console/window-buffer-size-record-str).
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub struct WindowBufferSizeRecord {
248    pub size: Coord,
249}
250
251impl From<WINDOW_BUFFER_SIZE_RECORD> for WindowBufferSizeRecord {
252    #[inline]
253    fn from(record: WINDOW_BUFFER_SIZE_RECORD) -> Self {
254        WindowBufferSizeRecord {
255            size: record.dwSize.into(),
256        }
257    }
258}
259
260/// A [focus event](https://docs.microsoft.com/en-us/windows/console/focus-event-record-str). This
261/// is used only internally by Windows and should be ignored.
262#[derive(Debug, Clone, Copy, PartialEq, Eq)]
263pub struct FocusEventRecord {
264    /// Reserved; do not use.
265    pub set_focus: bool,
266}
267
268impl From<FOCUS_EVENT_RECORD> for FocusEventRecord {
269    #[inline]
270    fn from(record: FOCUS_EVENT_RECORD) -> Self {
271        FocusEventRecord {
272            set_focus: record.bSetFocus != 0,
273        }
274    }
275}
276
277/// A [menu event](https://docs.microsoft.com/en-us/windows/console/menu-event-record-str). This is
278/// used only internally by Windows and should be ignored.
279#[derive(Debug, Clone, Copy, PartialEq, Eq)]
280pub struct MenuEventRecord {
281    /// Reserved; do not use.
282    pub command_id: u32,
283}
284
285impl From<MENU_EVENT_RECORD> for MenuEventRecord {
286    #[inline]
287    fn from(record: MENU_EVENT_RECORD) -> Self {
288        MenuEventRecord {
289            command_id: record.dwCommandId,
290        }
291    }
292}
293
294/// An [input event](https://docs.microsoft.com/en-us/windows/console/input-record-str).
295///
296/// These records can be read from the input buffer by using the `ReadConsoleInput`
297/// or `PeekConsoleInput` function, or written to the input buffer by using the
298/// `WriteConsoleInput` function.
299#[derive(Clone, Debug, PartialEq, Eq)]
300pub enum InputRecord {
301    /// A keyboard event occurred.
302    KeyEvent(KeyEventRecord),
303    /// The mouse was moved or a mouse button was pressed.
304    MouseEvent(MouseEvent),
305    /// A console screen buffer was resized.
306    WindowBufferSizeEvent(WindowBufferSizeRecord),
307    /// A focus event occured. This is used only internally by Windows and should be ignored.
308    FocusEvent(FocusEventRecord),
309    /// A menu event occurred. This is used only internally by Windows and should be ignored.
310    MenuEvent(MenuEventRecord),
311}
312
313impl From<INPUT_RECORD> for InputRecord {
314    #[inline]
315    fn from(record: INPUT_RECORD) -> Self {
316        match record.EventType {
317            KEY_EVENT => InputRecord::KeyEvent(KeyEventRecord::from_winapi(unsafe {
318                record.Event.KeyEvent()
319            })),
320            MOUSE_EVENT => InputRecord::MouseEvent(unsafe { *record.Event.MouseEvent() }.into()),
321            WINDOW_BUFFER_SIZE_EVENT => InputRecord::WindowBufferSizeEvent({
322                let mut buffer =
323                    unsafe { WindowBufferSizeRecord::from(*record.Event.WindowBufferSizeEvent()) };
324                let window = ScreenBuffer::current().unwrap().info().unwrap();
325                let screen_size = window.terminal_size();
326
327                buffer.size.y = screen_size.height;
328                buffer.size.x = screen_size.width;
329
330                buffer
331            }),
332            FOCUS_EVENT => InputRecord::FocusEvent(unsafe { *record.Event.FocusEvent() }.into()),
333            MENU_EVENT => InputRecord::MenuEvent(unsafe { *record.Event.MenuEvent() }.into()),
334            code => panic!("Unexpected INPUT_RECORD EventType: {}", code),
335        }
336    }
337}