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 
12 use winapi::shared::minwindef::DWORD;
13 use 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 
20 use super::Coord;
21 use 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)]
25 pub 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 
46 impl 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]
from_winapi(record: &KEY_EVENT_RECORD) -> Self52     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)]
66 pub 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 
77 impl From<MOUSE_EVENT_RECORD> for MouseEvent {
78     #[inline]
from(event: MOUSE_EVENT_RECORD) -> Self79     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)]
120 pub struct ButtonState {
121     state: i32,
122 }
123 
124 impl From<DWORD> for ButtonState {
125     #[inline]
from(event: DWORD) -> Self126     fn from(event: DWORD) -> Self {
127         let state = event as i32;
128         ButtonState { state }
129     }
130 }
131 
132 impl ButtonState {
133     /// Get whether no buttons are being pressed.
release_button(&self) -> bool134     pub fn release_button(&self) -> bool {
135         self.state == 0
136     }
137 
138     /// Returns whether the left button was pressed.
left_button(&self) -> bool139     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.
right_button(&self) -> bool144     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.
middle_button(&self) -> bool153     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.
scroll_down(&self) -> bool158     pub fn scroll_down(&self) -> bool {
159         self.state < 0
160     }
161 
162     /// Returns whether there is a up scroll.
scroll_up(&self) -> bool163     pub fn scroll_up(&self) -> bool {
164         self.state > 0
165     }
166 
167     /// Returns the raw state.
state(&self) -> i32168     pub fn state(&self) -> i32 {
169         self.state
170     }
171 }
172 
173 /// The state of the control keys.
174 ///
175 /// This is a bitmask of the following values.
176 ///
177 /// | Description | Value |
178 /// | --- | --- |
179 /// | The right alt key is pressed | `0x0001` |
180 /// | The left alt key is pressed | `x0002` |
181 /// | The right control key is pressed | `0x0004` |
182 /// | The left control key is pressed | `x0008` |
183 /// | The shift key is pressed | `0x0010` |
184 /// | The num lock light is on | `0x0020` |
185 /// | The scroll lock light is on | `0x0040` |
186 /// | The caps lock light is on | `0x0080` |
187 /// | The key is [enhanced](https://docs.microsoft.com/en-us/windows/console/key-event-record-str#remarks) | `0x0100` |
188 #[derive(PartialEq, Debug, Copy, Clone, Eq)]
189 pub struct ControlKeyState(u32);
190 
191 impl ControlKeyState {
192     /// Whether the control key has a state.
has_state(&self, state: u32) -> bool193     pub fn has_state(&self, state: u32) -> bool {
194         (state & self.0) != 0
195     }
196 }
197 
198 /// The type of mouse event.
199 /// If this value is zero, it indicates a mouse button being pressed or released.
200 /// Otherwise, this member is one of the following values.
201 ///
202 /// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str#members)
203 #[derive(PartialEq, Debug, Copy, Clone, Eq)]
204 pub enum EventFlags {
205     PressOrRelease = 0x0000,
206     /// The second click (button press) of a double-click occurred. The first click is returned as a regular button-press event.
207     DoubleClick = 0x0002,
208     /// The horizontal mouse wheel was moved.
209     MouseHwheeled = 0x0008,
210     /// 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.
211     MouseMoved = 0x0001,
212     /// A change in mouse position occurred.
213     /// 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.
214     /// Otherwise, the wheel was rotated backward, toward the user.
215     MouseWheeled = 0x0004,
216     // This button state is not recognized.
217     Unknown = 0x0021,
218 }
219 
220 // TODO: Replace with TryFrom.
221 impl From<DWORD> for EventFlags {
from(event: DWORD) -> Self222     fn from(event: DWORD) -> Self {
223         match event {
224             0x0000 => EventFlags::PressOrRelease,
225             0x0002 => EventFlags::DoubleClick,
226             0x0008 => EventFlags::MouseHwheeled,
227             0x0001 => EventFlags::MouseMoved,
228             0x0004 => EventFlags::MouseWheeled,
229             _ => EventFlags::Unknown,
230         }
231     }
232 }
233 
234 /// The [size of console screen
235 /// buffer](https://docs.microsoft.com/en-us/windows/console/window-buffer-size-record-str).
236 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
237 pub struct WindowBufferSizeRecord {
238     pub size: Coord,
239 }
240 
241 impl From<WINDOW_BUFFER_SIZE_RECORD> for WindowBufferSizeRecord {
242     #[inline]
from(record: WINDOW_BUFFER_SIZE_RECORD) -> Self243     fn from(record: WINDOW_BUFFER_SIZE_RECORD) -> Self {
244         WindowBufferSizeRecord {
245             size: record.dwSize.into(),
246         }
247     }
248 }
249 
250 /// A [focus event](https://docs.microsoft.com/en-us/windows/console/focus-event-record-str). This
251 /// is used only internally by Windows and should be ignored.
252 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
253 pub struct FocusEventRecord {
254     /// Reserved; do not use.
255     pub set_focus: bool,
256 }
257 
258 impl From<FOCUS_EVENT_RECORD> for FocusEventRecord {
259     #[inline]
from(record: FOCUS_EVENT_RECORD) -> Self260     fn from(record: FOCUS_EVENT_RECORD) -> Self {
261         FocusEventRecord {
262             set_focus: record.bSetFocus != 0,
263         }
264     }
265 }
266 
267 /// A [menu event](https://docs.microsoft.com/en-us/windows/console/menu-event-record-str). This is
268 /// used only internally by Windows and should be ignored.
269 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
270 pub struct MenuEventRecord {
271     /// Reserved; do not use.
272     pub command_id: u32,
273 }
274 
275 impl From<MENU_EVENT_RECORD> for MenuEventRecord {
276     #[inline]
from(record: MENU_EVENT_RECORD) -> Self277     fn from(record: MENU_EVENT_RECORD) -> Self {
278         MenuEventRecord {
279             command_id: record.dwCommandId,
280         }
281     }
282 }
283 
284 /// An [input event](https://docs.microsoft.com/en-us/windows/console/input-record-str).
285 ///
286 /// These records can be read from the input buffer by using the `ReadConsoleInput`
287 /// or `PeekConsoleInput` function, or written to the input buffer by using the
288 /// `WriteConsoleInput` function.
289 #[derive(Clone, Debug, PartialEq, Eq)]
290 pub enum InputRecord {
291     /// A keyboard event occurred.
292     KeyEvent(KeyEventRecord),
293     /// The mouse was moved or a mouse button was pressed.
294     MouseEvent(MouseEvent),
295     /// A console screen buffer was resized.
296     WindowBufferSizeEvent(WindowBufferSizeRecord),
297     /// A focus event occured. This is used only internally by Windows and should be ignored.
298     FocusEvent(FocusEventRecord),
299     /// A menu event occurred. This is used only internally by Windows and should be ignored.
300     MenuEvent(MenuEventRecord),
301 }
302 
303 impl From<INPUT_RECORD> for InputRecord {
304     #[inline]
from(record: INPUT_RECORD) -> Self305     fn from(record: INPUT_RECORD) -> Self {
306         match record.EventType {
307             KEY_EVENT => InputRecord::KeyEvent(KeyEventRecord::from_winapi(unsafe {
308                 record.Event.KeyEvent()
309             })),
310             MOUSE_EVENT => InputRecord::MouseEvent(unsafe { *record.Event.MouseEvent() }.into()),
311             WINDOW_BUFFER_SIZE_EVENT => InputRecord::WindowBufferSizeEvent({
312                 let mut buffer =
313                     unsafe { WindowBufferSizeRecord::from(*record.Event.WindowBufferSizeEvent()) };
314                 let window = ScreenBuffer::current().unwrap().info().unwrap();
315                 let screen_size = window.terminal_size();
316 
317                 buffer.size.y = screen_size.height;
318                 buffer.size.x = screen_size.width;
319 
320                 buffer
321             }),
322             FOCUS_EVENT => InputRecord::FocusEvent(unsafe { *record.Event.FocusEvent() }.into()),
323             MENU_EVENT => InputRecord::MenuEvent(unsafe { *record.Event.MenuEvent() }.into()),
324             code => panic!("Unexpected INPUT_RECORD EventType: {}", code),
325         }
326     }
327 }
328