diff options
-rw-r--r-- | src/display/src/crossterm.rs | 17 | ||||
-rw-r--r-- | src/input/src/event_provider.rs | 220 |
2 files changed, 232 insertions, 5 deletions
diff --git a/src/display/src/crossterm.rs b/src/display/src/crossterm.rs index 31aac25..291a7e8 100644 --- a/src/display/src/crossterm.rs +++ b/src/display/src/crossterm.rs @@ -2,7 +2,13 @@ use std::io::{stdout, BufWriter, Stdout, Write}; use crossterm::{ cursor::{Hide, MoveTo, MoveToColumn, MoveToNextLine, Show}, - event::{DisableMouseCapture, EnableMouseCapture}, + event::{ + DisableMouseCapture, + EnableMouseCapture, + KeyboardEnhancementFlags, + PopKeyboardEnhancementFlags, + PushKeyboardEnhancementFlags, + }, style::{available_color_count, Attribute, Colors, Print, ResetColor, SetAttribute, SetColors}, terminal::{ disable_raw_mode, @@ -118,12 +124,21 @@ impl Tui for CrossTerm { self.queue_command(DisableLineWrap)?; self.queue_command(Hide)?; self.queue_command(EnableMouseCapture)?; + // this will fail on terminals without support, so ignore any errors + let _command_result = self.queue_command(PushKeyboardEnhancementFlags( + KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_EVENT_TYPES + | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS + | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES, + )); enable_raw_mode().map_err(DisplayError::Unexpected)?; self.flush() } #[inline] fn end(&mut self) -> Result<(), DisplayError> { + // this will fail on terminals without support, so ignore any errors + let _command_result = self.queue_command(PopKeyboardEnhancementFlags); self.queue_command(DisableMouseCapture)?; self.queue_command(Show)?; self.queue_command(EnableLineWrap)?; diff --git a/src/input/src/event_provider.rs b/src/input/src/event_provider.rs index f1a0440..7dd1f41 100644 --- a/src/input/src/event_provider.rs +++ b/src/input/src/event_provider.rs @@ -1,9 +1,9 @@ use std::time::Duration; use anyhow::{anyhow, Result}; -use crossterm::event::Event; #[cfg(not(test))] use crossterm::event::{poll, read}; +use crossterm::event::{Event, KeyEvent, KeyEventKind, MouseEvent, MouseEventKind}; #[cfg(test)] use read_event_mocks::{poll, read}; @@ -23,7 +23,20 @@ impl<FN: Fn() -> Result<Option<Event>> + Send + Sync + 'static> EventReaderFn fo pub fn read_event() -> Result<Option<Event>> { if poll(Duration::from_millis(20)).unwrap_or(false) { read() - .map(Some) + .map(|event| { + match event { + e @ (Event::Key(KeyEvent { + kind: KeyEventKind::Press | KeyEventKind::Repeat, + .. + }) + | Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(_) | MouseEventKind::ScrollDown | MouseEventKind::ScrollUp, + .. + }) + | Event::Resize(..)) => Some(e), + Event::Key(_) | Event::Mouse(_) | Event::Paste(_) | Event::FocusGained | Event::FocusLost => None, + } + }) .map_err(|err| anyhow!("{:#}", err).context("Unexpected Error")) } else { @@ -62,7 +75,7 @@ mod read_event_mocks { mod tests { use std::{io, io::ErrorKind}; - use crossterm::event::{KeyCode, KeyEvent}; + use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton}; use super::*; @@ -102,7 +115,7 @@ mod tests { #[test] #[serial_test::serial] - fn read_event_read_success() { + fn read_event_read_key_press() { let mut lock = read_event_mocks::NEXT_EVENT.lock(); *lock = Ok(Event::Key(KeyEvent::from(KeyCode::Enter))); drop(lock); @@ -113,4 +126,203 @@ mod tests { assert_eq!(read_event().unwrap(), Some(Event::Key(KeyEvent::from(KeyCode::Enter)))); } + + #[test] + #[serial_test::serial] + fn read_event_read_key_repeat() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::Key(KeyEvent::new_with_kind( + KeyCode::Enter, + KeyModifiers::NONE, + KeyEventKind::Repeat, + ))); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!( + read_event().unwrap(), + Some(Event::Key(KeyEvent::new_with_kind( + KeyCode::Enter, + KeyModifiers::NONE, + KeyEventKind::Repeat, + ))) + ); + } + + #[test] + #[serial_test::serial] + fn read_event_read_mouse_down() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Right), + column: 0, + row: 0, + modifiers: KeyModifiers::NONE, + })); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!( + read_event().unwrap(), + Some(Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Right), + column: 0, + row: 0, + modifiers: KeyModifiers::NONE + })) + ); + } + + #[test] + #[serial_test::serial] + fn read_event_read_mouse_scroll_down() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::Mouse(MouseEvent { + kind: MouseEventKind::ScrollDown, + column: 0, + row: 0, + modifiers: KeyModifiers::NONE, + })); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!( + read_event().unwrap(), + Some(Event::Mouse(MouseEvent { + kind: MouseEventKind::ScrollDown, + column: 0, + row: 0, + modifiers: KeyModifiers::NONE + })) + ); + } + + #[test] + #[serial_test::serial] + fn read_event_read_mouse_scroll_up() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::Mouse(MouseEvent { + kind: MouseEventKind::ScrollDown, + column: 0, + row: 0, + modifiers: KeyModifiers::NONE, + })); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!( + read_event().unwrap(), + Some(Event::Mouse(MouseEvent { + kind: MouseEventKind::ScrollDown, + column: 0, + row: 0, + modifiers: KeyModifiers::NONE + })) + ); + } + + #[test] + #[serial_test::serial] + fn read_event_read_resize() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::Resize(1, 1)); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!(read_event().unwrap(), Some(Event::Resize(1, 1))); + } + + #[test] + #[serial_test::serial] + fn read_event_read_key_other() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::Key(KeyEvent::new_with_kind( + KeyCode::Enter, + KeyModifiers::NONE, + KeyEventKind::Release, + ))); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!(read_event().unwrap(), None); + } + + #[test] + #[serial_test::serial] + fn read_event_read_mouse_other() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::Mouse(MouseEvent { + kind: MouseEventKind::Moved, + column: 0, + row: 0, + modifiers: KeyModifiers::NONE, + })); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!(read_event().unwrap(), None); + } + + #[test] + #[serial_test::serial] + fn read_event_read_paste() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::Paste(String::from("Foo"))); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!(read_event().unwrap(), None); + } + + #[test] + #[serial_test::serial] + fn read_event_read_focus_gained() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::FocusGained); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!(read_event().unwrap(), None); + } + + #[test] + #[serial_test::serial] + fn read_event_read_focus_lost() { + let mut lock = read_event_mocks::NEXT_EVENT.lock(); + *lock = Ok(Event::FocusLost); + drop(lock); + + let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock(); + *lock = Ok(true); + drop(lock); + + assert_eq!(read_event().unwrap(), None); + } } |