1 //! This is a WINDOWS specific implementation for input related action.
2 
3 use std::{char, collections::VecDeque, io, sync::Mutex};
4 
5 use crossterm_winapi::{
6     ButtonState, Console, ConsoleMode, EventFlags, Handle, InputEventType, KeyEventRecord,
7     MouseEvent, ScreenBuffer,
8 };
9 use winapi::um::{
10     wincon::{
11         LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, SHIFT_PRESSED,
12     },
13     winnt::INT,
14     winuser::{
15         VK_BACK, VK_CONTROL, VK_DELETE, VK_DOWN, VK_END, VK_ESCAPE, VK_F1, VK_F10, VK_F11, VK_F12,
16         VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_HOME, VK_INSERT, VK_LEFT,
17         VK_MENU, VK_NEXT, VK_PRIOR, VK_RETURN, VK_RIGHT, VK_SHIFT, VK_UP,
18     },
19 };
20 
21 use lazy_static::lazy_static;
22 
23 use crate::input::{input::Input, InputEvent, KeyEvent, MouseButton};
24 use crate::utils::Result;
25 
26 const ENABLE_MOUSE_MODE: u32 = 0x0010 | 0x0080 | 0x0008;
27 
28 lazy_static! {
29     static ref ORIGINAL_CONSOLE_MODE: Mutex<Option<u32>> = Mutex::new(None);
30 }
31 
32 /// Initializes the default console color. It will will be skipped if it has already been initialized.
init_original_console_mode(original_mode: u32)33 fn init_original_console_mode(original_mode: u32) {
34     let mut lock = ORIGINAL_CONSOLE_MODE.lock().unwrap();
35 
36     if lock.is_none() {
37         *lock = Some(original_mode);
38     }
39 }
40 
41 /// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic.
original_console_mode() -> u3242 fn original_console_mode() -> u32 {
43     // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()`
44     ORIGINAL_CONSOLE_MODE
45         .lock()
46         .unwrap()
47         .expect("Original console mode not set")
48 }
49 
50 pub(crate) struct WindowsInput;
51 
52 impl WindowsInput {
new() -> WindowsInput53     pub fn new() -> WindowsInput {
54         WindowsInput
55     }
56 }
57 
58 impl Input for WindowsInput {
read_char(&self) -> Result<char>59     fn read_char(&self) -> Result<char> {
60         // _getwch is without echo and _getwche is with echo
61         let pressed_char = unsafe { _getwche() };
62 
63         // we could return error but maybe option to keep listening until valid character is inputted.
64         if pressed_char == 0 || pressed_char == 0xe0 {
65             Err(io::Error::new(
66                 io::ErrorKind::Other,
67                 "Given input char is not a valid char, mostly occurs when pressing special keys",
68             ))?;
69         }
70 
71         let ch = char::from_u32(pressed_char as u32).ok_or_else(|| {
72             io::Error::new(io::ErrorKind::Other, "Could not parse given input to char")
73         })?;
74 
75         Ok(ch)
76     }
77 
read_async(&self) -> AsyncReader78     fn read_async(&self) -> AsyncReader {
79         let handle = Handle::current_in_handle().expect("failed to create console input handle");
80         let console = Console::from(handle);
81         AsyncReader::new(console, None)
82     }
83 
read_until_async(&self, delimiter: u8) -> AsyncReader84     fn read_until_async(&self, delimiter: u8) -> AsyncReader {
85         let handle = Handle::current_in_handle().expect("failed to create console input handle");
86         let console = Console::from(handle);
87         AsyncReader::new(console, Some(delimiter))
88     }
89 
read_sync(&self) -> SyncReader90     fn read_sync(&self) -> SyncReader {
91         SyncReader
92     }
93 
enable_mouse_mode(&self) -> Result<()>94     fn enable_mouse_mode(&self) -> Result<()> {
95         let mode = ConsoleMode::from(Handle::current_in_handle()?);
96 
97         init_original_console_mode(mode.mode()?);
98         mode.set_mode(ENABLE_MOUSE_MODE)?;
99 
100         Ok(())
101     }
102 
disable_mouse_mode(&self) -> Result<()>103     fn disable_mouse_mode(&self) -> Result<()> {
104         let mode = ConsoleMode::from(Handle::current_in_handle()?);
105         mode.set_mode(original_console_mode())?;
106         Ok(())
107     }
108 }
109 
110 /// A synchronous input reader (blocking).
111 ///
112 /// `SyncReader` implements the [`Iterator`](https://doc.rust-lang.org/std/iter/index.html#iterator)
113 /// trait. Documentation says:
114 ///
115 /// > An iterator has a method, `next`, which when called, returns an `Option<Item>`. `next` will return
116 /// > `Some(Item)` as long as there are elements, and once they've all been exhausted, will return `None`
117 /// > to indicate that iteration is finished. Individual iterators may choose to resume iteration, and
118 /// > so calling `next` again may or may not eventually start returning `Some(Item)` again at some point.
119 ///
120 /// `SyncReader` is an individual iterator and it doesn't use `None` to indicate that the iteration is
121 /// finished. You can expect additional `Some(InputEvent)` after calling `next` even if you have already
122 /// received `None`. Unfortunately, `None` means that an error occurred, but you're free to call `next`
123 /// again. This behavior will be changed in the future to avoid errors consumption.
124 ///
125 /// # Notes
126 ///
127 /// * It requires enabled raw mode (see the
128 ///   [`crossterm_screen`](https://docs.rs/crossterm_screen/) crate documentation to learn more).
129 /// * See the [`AsyncReader`](struct.AsyncReader.html) if you want a non blocking reader.
130 ///
131 /// # Examples
132 ///
133 /// ```no_run
134 /// use std::{thread, time::Duration};
135 ///
136 /// use crossterm::{screen::RawScreen,  input::{input, InputEvent, KeyEvent}};
137 ///
138 /// fn main() {
139 ///     println!("Press 'ESC' to quit.");
140 ///
141 ///     // Enable raw mode and keep the `_raw` around otherwise the raw mode will be disabled
142 ///     let _raw = RawScreen::into_raw_mode();
143 ///
144 ///     // Create an input from our screen
145 ///     let input = input();
146 ///
147 ///     // Create a sync reader
148 ///     let mut reader = input.read_sync();
149 ///
150 ///     loop {
151 ///         if let Some(event) = reader.next() { // Blocking call
152 ///             match event {
153 ///                 InputEvent::Keyboard(KeyEvent::Esc) => {
154 ///                     println!("Program closing ...");
155 ///                     break;
156 ///                  }
157 ///                  InputEvent::Mouse(event) => { /* Mouse event */ }
158 ///                  _ => { /* Other events */ }
159 ///             }
160 ///         }
161 ///         thread::sleep(Duration::from_millis(50));
162 ///     }
163 /// } // `_raw` dropped <- raw mode disabled
164 /// ```
165 pub struct SyncReader;
166 
167 impl Iterator for SyncReader {
168     type Item = InputEvent;
169 
170     /// Tries to read the next input event (blocking).
171     ///
172     /// `None` doesn't mean that the iteration is finished. See the
173     /// [`SyncReader`](struct.SyncReader.html) documentation for more information.
next(&mut self) -> Option<Self::Item>174     fn next(&mut self) -> Option<Self::Item> {
175         // This synces the behaviour with the unix::SyncReader (& documentation) where
176         // None is returned in case of error.
177         read_single_event().unwrap_or(None)
178     }
179 }
180 
181 /// An asynchronous input reader (not blocking).
182 ///
183 /// `AsyncReader` implements the [`Iterator`](https://doc.rust-lang.org/std/iter/index.html#iterator)
184 /// trait. Documentation says:
185 ///
186 /// > An iterator has a method, `next`, which when called, returns an `Option<Item>`. `next` will return
187 /// > `Some(Item)` as long as there are elements, and once they've all been exhausted, will return `None`
188 /// > to indicate that iteration is finished. Individual iterators may choose to resume iteration, and
189 /// > so calling `next` again may or may not eventually start returning `Some(Item)` again at some point.
190 ///
191 /// `AsyncReader` is an individual iterator and it doesn't use `None` to indicate that the iteration is
192 /// finished. You can expect additional `Some(InputEvent)` after calling `next` even if you have already
193 /// received `None`.
194 ///
195 /// # Notes
196 ///
197 /// * It requires enabled raw mode (see the
198 ///   [`crossterm_screen`](https://docs.rs/crossterm_screen/) crate documentation to learn more).
199 /// * A thread is spawned to read the input.
200 /// * The reading thread is cleaned up when you drop the `AsyncReader`.
201 /// * See the [`SyncReader`](struct.SyncReader.html) if you want a blocking,
202 ///   or a less resource hungry reader.
203 ///
204 /// # Examples
205 ///
206 /// ```no_run
207 /// use std::{thread, time::Duration};
208 ///
209 /// use crossterm::{screen::RawScreen,  input::{input, InputEvent, KeyEvent}};
210 ///
211 /// fn main() {
212 ///     println!("Press 'ESC' to quit.");
213 ///
214 ///     // Enable raw mode and keep the `_raw` around otherwise the raw mode will be disabled
215 ///     let _raw = RawScreen::into_raw_mode();
216 ///
217 ///     // Create an input from our screen
218 ///     let input = input();
219 ///
220 ///     // Create an async reader
221 ///     let mut reader = input.read_async();
222 ///
223 ///     loop {
224 ///         if let Some(event) = reader.next() { // Not a blocking call
225 ///             match event {
226 ///                 InputEvent::Keyboard(KeyEvent::Esc) => {
227 ///                     println!("Program closing ...");
228 ///                     break;
229 ///                  }
230 ///                  InputEvent::Mouse(event) => { /* Mouse event */ }
231 ///                  _ => { /* Other events */ }
232 ///             }
233 ///         }
234 ///         thread::sleep(Duration::from_millis(50));
235 ///     }
236 /// } // `reader` dropped <- thread cleaned up, `_raw` dropped <- raw mode disabled
237 /// ```
238 pub struct AsyncReader {
239     console: Console,
240     buffer: VecDeque<InputEvent>,
241     delimiter: Option<u8>,
242 }
243 
244 impl AsyncReader {
245     // TODO Should the new() really be public?
246     /// Creates a new `AsyncReader`.
247     ///
248     /// # Notes
249     ///
250     /// * A thread is spawned to read the input.
251     /// * The reading thread is cleaned up when you drop the `AsyncReader`.
new(console: Console, delimiter: Option<u8>) -> AsyncReader252     pub fn new(console: Console, delimiter: Option<u8>) -> AsyncReader {
253         AsyncReader {
254             console,
255             buffer: VecDeque::new(),
256             delimiter,
257         }
258     }
259 
stop(&mut self)260     pub fn stop(&mut self) {}
261 }
262 
263 impl Iterator for AsyncReader {
264     type Item = InputEvent;
265 
266     /// Tries to read the next input event (not blocking).
267     ///
268     /// `None` doesn't mean that the iteration is finished. See the
269     /// [`AsyncReader`](struct.AsyncReader.html) documentation for more information.
next(&mut self) -> Option<Self::Item>270     fn next(&mut self) -> Option<Self::Item> {
271         loop {
272             if self.buffer.is_empty() {
273                 let (_, events) = read_input_events(&self.console).expect("read failed");
274 
275                 if events.is_empty() {
276                     return None;
277                 }
278 
279                 self.buffer.extend(events);
280             }
281 
282             if let Some(delimiter) = self.delimiter {
283                 while let Some(e) = self.buffer.pop_front() {
284                     if let InputEvent::Keyboard(KeyEvent::Char(key)) = e {
285                         if (key as u8) == delimiter {
286                             return Some(e);
287                         }
288                     }
289                 }
290 
291                 continue;
292             }
293 
294             return self.buffer.pop_front();
295         }
296     }
297 }
298 
299 extern "C" {
_getwche() -> INT300     fn _getwche() -> INT;
301 }
302 
read_single_event() -> Result<Option<InputEvent>>303 fn read_single_event() -> Result<Option<InputEvent>> {
304     let console = Console::from(Handle::current_in_handle()?);
305 
306     let input = console.read_single_input_event()?;
307 
308     match input.event_type {
309         InputEventType::KeyEvent => {
310             handle_key_event(unsafe { KeyEventRecord::from(*input.event.KeyEvent()) })
311         }
312         InputEventType::MouseEvent => {
313             handle_mouse_event(unsafe { MouseEvent::from(*input.event.MouseEvent()) })
314         }
315         // NOTE (@imdaveho): ignore below
316         InputEventType::WindowBufferSizeEvent => return Ok(None), // TODO implement terminal resize event
317         InputEventType::FocusEvent => Ok(None),
318         InputEventType::MenuEvent => Ok(None),
319     }
320 }
321 
322 /// partially inspired by: https://github.com/retep998/wio-rs/blob/master/src/console.rs#L130
read_input_events(console: &Console) -> Result<(u32, Vec<InputEvent>)>323 fn read_input_events(console: &Console) -> Result<(u32, Vec<InputEvent>)> {
324     let result = console.read_console_input()?;
325 
326     let mut input_events = Vec::with_capacity(result.0 as usize);
327 
328     for input in result.1 {
329         match input.event_type {
330             InputEventType::KeyEvent => {
331                 if let Ok(Some(event)) =
332                     handle_key_event(unsafe { KeyEventRecord::from(*input.event.KeyEvent()) })
333                 {
334                     input_events.push(event)
335                 }
336             }
337             InputEventType::MouseEvent => {
338                 if let Ok(Some(event)) =
339                     handle_mouse_event(unsafe { MouseEvent::from(*input.event.MouseEvent()) })
340                 {
341                     input_events.push(event)
342                 }
343             }
344             // NOTE (@imdaveho): ignore below
345             InputEventType::WindowBufferSizeEvent => (), // TODO implement terminal resize event
346             InputEventType::FocusEvent => (),
347             InputEventType::MenuEvent => (),
348         }
349     }
350 
351     return Ok((result.0, input_events));
352 }
353 
handle_mouse_event(mouse_event: MouseEvent) -> Result<Option<InputEvent>>354 fn handle_mouse_event(mouse_event: MouseEvent) -> Result<Option<InputEvent>> {
355     if let Ok(Some(event)) = parse_mouse_event_record(&mouse_event) {
356         return Ok(Some(InputEvent::Mouse(event)));
357     }
358     Ok(None)
359 }
360 
handle_key_event(key_event: KeyEventRecord) -> Result<Option<InputEvent>>361 fn handle_key_event(key_event: KeyEventRecord) -> Result<Option<InputEvent>> {
362     if key_event.key_down {
363         if let Some(event) = parse_key_event_record(&key_event) {
364             return Ok(Some(InputEvent::Keyboard(event)));
365         }
366     }
367 
368     return Ok(None);
369 }
370 
parse_key_event_record(key_event: &KeyEventRecord) -> Option<KeyEvent>371 fn parse_key_event_record(key_event: &KeyEventRecord) -> Option<KeyEvent> {
372     let key_code = key_event.virtual_key_code as i32;
373     match key_code {
374         VK_SHIFT | VK_CONTROL | VK_MENU => None,
375         VK_BACK => Some(KeyEvent::Backspace),
376         VK_ESCAPE => Some(KeyEvent::Esc),
377         VK_RETURN => Some(KeyEvent::Enter),
378         VK_F1 | VK_F2 | VK_F3 | VK_F4 | VK_F5 | VK_F6 | VK_F7 | VK_F8 | VK_F9 | VK_F10 | VK_F11
379         | VK_F12 => Some(KeyEvent::F((key_event.virtual_key_code - 111) as u8)),
380         VK_LEFT | VK_UP | VK_RIGHT | VK_DOWN => {
381             // Modifier Keys (Ctrl, Shift) Support
382             let key_state = &key_event.control_key_state;
383             let ctrl_pressed = key_state.has_state(RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED);
384             let shift_pressed = key_state.has_state(SHIFT_PRESSED);
385 
386             let event = match key_code {
387                 VK_LEFT => {
388                     if ctrl_pressed {
389                         Some(KeyEvent::CtrlLeft)
390                     } else if shift_pressed {
391                         Some(KeyEvent::ShiftLeft)
392                     } else {
393                         Some(KeyEvent::Left)
394                     }
395                 }
396                 VK_UP => {
397                     if ctrl_pressed {
398                         Some(KeyEvent::CtrlUp)
399                     } else if shift_pressed {
400                         Some(KeyEvent::ShiftUp)
401                     } else {
402                         Some(KeyEvent::Up)
403                     }
404                 }
405                 VK_RIGHT => {
406                     if ctrl_pressed {
407                         Some(KeyEvent::CtrlRight)
408                     } else if shift_pressed {
409                         Some(KeyEvent::ShiftRight)
410                     } else {
411                         Some(KeyEvent::Right)
412                     }
413                 }
414                 VK_DOWN => {
415                     if ctrl_pressed {
416                         Some(KeyEvent::CtrlDown)
417                     } else if shift_pressed {
418                         Some(KeyEvent::ShiftDown)
419                     } else {
420                         Some(KeyEvent::Down)
421                     }
422                 }
423                 _ => None,
424             };
425 
426             event
427         }
428         VK_PRIOR | VK_NEXT => {
429             if key_code == VK_PRIOR {
430                 Some(KeyEvent::PageUp)
431             } else if key_code == VK_NEXT {
432                 Some(KeyEvent::PageDown)
433             } else {
434                 None
435             }
436         }
437         VK_END | VK_HOME => {
438             if key_code == VK_HOME {
439                 Some(KeyEvent::Home)
440             } else if key_code == VK_END {
441                 Some(KeyEvent::End)
442             } else {
443                 None
444             }
445         }
446         VK_DELETE => Some(KeyEvent::Delete),
447         VK_INSERT => Some(KeyEvent::Insert),
448         _ => {
449             // Modifier Keys (Ctrl, Alt, Shift) Support
450             let character_raw = { (unsafe { *key_event.u_char.UnicodeChar() } as u16) };
451 
452             if character_raw < 255 {
453                 let character = character_raw as u8 as char;
454 
455                 let key_state = &key_event.control_key_state;
456 
457                 if key_state.has_state(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) {
458                     // If the ALT key is held down, pressing the A key produces ALT+A, which the system does not treat as a character at all, but rather as a system command.
459                     // The pressed command is stored in `virtual_key_code`.
460                     let command = key_event.virtual_key_code as u8 as char;
461 
462                     if (command).is_alphabetic() {
463                         Some(KeyEvent::Alt(command))
464                     } else {
465                         None
466                     }
467                 } else if key_state.has_state(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) {
468                     match character_raw as u8 {
469                         c @ b'\x01'..=b'\x1A' => {
470                             Some(KeyEvent::Ctrl((c as u8 - 0x1 + b'a') as char))
471                         }
472                         c @ b'\x1C'..=b'\x1F' => {
473                             Some(KeyEvent::Ctrl((c as u8 - 0x1C + b'4') as char))
474                         }
475                         _ => None,
476                     }
477                 } else if key_state.has_state(SHIFT_PRESSED) && character == '\t' {
478                     Some(KeyEvent::BackTab)
479                 } else {
480                     if character == '\t' {
481                         Some(KeyEvent::Tab)
482                     } else {
483                         // Shift + key press, essentially the same as single key press
484                         // Separating to be explicit about the Shift press.
485                         Some(KeyEvent::Char(character))
486                     }
487                 }
488             } else {
489                 None
490             }
491         }
492     }
493 }
494 
parse_mouse_event_record(event: &MouseEvent) -> Result<Option<crate::input::MouseEvent>>495 fn parse_mouse_event_record(event: &MouseEvent) -> Result<Option<crate::input::MouseEvent>> {
496     // NOTE (@imdaveho): xterm emulation takes the digits of the coords and passes them
497     // individually as bytes into a buffer; the below cxbs and cybs replicates that and
498     // mimicks the behavior; additionally, in xterm, mouse move is only handled when a
499     // mouse button is held down (ie. mouse drag)
500 
501     let window_size = ScreenBuffer::current()?.info()?.terminal_window();
502 
503     let xpos = event.mouse_position.x;
504     let mut ypos = event.mouse_position.y;
505 
506     // The 'y' position of a mouse event is not relative to the window but absolute to screen buffer.
507     // This means that when the mouse cursor is at the top left it will be x: 0, y: 2295 (e.g. y = number of cells counting from the absolute buffer height) instead of relative x: 0, y: 0 to the window.
508 
509     ypos = ypos - window_size.top;
510 
511     Ok(match event.event_flags {
512         EventFlags::PressOrRelease => {
513             // Single click
514             match event.button_state {
515                 ButtonState::Release => {
516                     Some(crate::input::MouseEvent::Release(xpos as u16, ypos as u16))
517                 }
518                 ButtonState::FromLeft1stButtonPressed => {
519                     // left click
520                     Some(crate::input::MouseEvent::Press(
521                         MouseButton::Left,
522                         xpos as u16,
523                         ypos as u16,
524                     ))
525                 }
526                 ButtonState::RightmostButtonPressed => {
527                     // right click
528                     Some(crate::input::MouseEvent::Press(
529                         MouseButton::Right,
530                         xpos as u16,
531                         ypos as u16,
532                     ))
533                 }
534                 ButtonState::FromLeft2ndButtonPressed => {
535                     // middle click
536                     Some(crate::input::MouseEvent::Press(
537                         MouseButton::Middle,
538                         xpos as u16,
539                         ypos as u16,
540                     ))
541                 }
542                 _ => None,
543             }
544         }
545         EventFlags::MouseMoved => {
546             // Click + Move
547             // NOTE (@imdaveho) only register when mouse is not released
548             if event.button_state != ButtonState::Release {
549                 Some(crate::input::MouseEvent::Hold(xpos as u16, ypos as u16))
550             } else {
551                 None
552             }
553         }
554         EventFlags::MouseWheeled => {
555             // Vertical scroll
556             // NOTE (@imdaveho) from https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str
557             // if `button_state` is negative then the wheel was rotated backward, toward the user.
558             if event.button_state != ButtonState::Negative {
559                 Some(crate::input::MouseEvent::Press(
560                     MouseButton::WheelUp,
561                     xpos as u16,
562                     ypos as u16,
563                 ))
564             } else {
565                 Some(crate::input::MouseEvent::Press(
566                     MouseButton::WheelDown,
567                     xpos as u16,
568                     ypos as u16,
569                 ))
570             }
571         }
572         EventFlags::DoubleClick => None, // NOTE (@imdaveho): double click not supported by unix terminals
573         EventFlags::MouseHwheeled => None, // NOTE (@imdaveho): horizontal scroll not supported by unix terminals
574                                            // TODO: Handle Ctrl + Mouse, Alt + Mouse, etc.
575     })
576 }
577