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}