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