diff --git a/Cargo.toml b/Cargo.toml index aa23a87c..f0555e5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ event-stream = ["dep:futures-core", "events"] # Enables async events use-dev-tty = ["filedescriptor"] # Enables raw file descriptor polling / selecting instead of mio. events = ["dep:mio", "dep:signal-hook", "dep:signal-hook-mio"] # Enables reading input/events from the system. serde = ["dep:serde", "bitflags/serde"] # Enables 'serde' for various types. +event-kind = ["events"] # Enables a 'release' event for input for windows and kitty protocol. # # Shared dependencies @@ -81,7 +82,7 @@ serde_json = "1.0" # [[example]] name = "event-read" -required-features = ["bracketed-paste", "events"] +required-features = ["bracketed-paste", "events", "event-kind"] [[example]] name = "event-match-modifiers" diff --git a/examples/interactive-demo/src/test/color.rs b/examples/interactive-demo/src/test/color.rs index dc37d3ba..d34ac457 100644 --- a/examples/interactive-demo/src/test/color.rs +++ b/examples/interactive-demo/src/test/color.rs @@ -1,6 +1,5 @@ #![allow(clippy::cognitive_complexity)] -use crate::Result; use crossterm::{cursor, queue, style, style::Color}; use std::io::Write; diff --git a/src/event.rs b/src/event.rs index b93c8acd..f4671227 100644 --- a/src/event.rs +++ b/src/event.rs @@ -255,6 +255,8 @@ bitflags! { const DISAMBIGUATE_ESCAPE_CODES = 0b0000_0001; /// Add extra events with [`KeyEvent.kind`] set to [`KeyEventKind::Repeat`] or /// [`KeyEventKind::Release`] when keys are autorepeated or released. + /// IMPORTANT: Requires feature `event-kind` to be enabled. + #[cfg(feature="event-kind")] const REPORT_EVENT_TYPES = 0b0000_0010; // Send [alternate keycodes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#key-codes) // in addition to the base keycode. The alternate keycode overrides the base keycode in @@ -609,11 +611,15 @@ bitflags! { } /// Represents a keyboard event kind. +/// +/// Enable `event-kind` feature to get release events on windows, and on unix when kitty-protocol is enabled and `KeyboardEnhancementFlags::REPORT_EVENT_TYPES` is set. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] pub enum KeyEventKind { Press, + #[cfg(feature = "event-kind")] Repeat, + #[cfg(feature = "event-kind")] Release, } @@ -649,6 +655,7 @@ pub struct KeyEvent { /// Additional key modifiers. pub modifiers: KeyModifiers, /// Kind of event. + /// By default `KeyEventKind::Press`. /// /// Only set if: /// - Unix: [`KeyboardEnhancementFlags::REPORT_EVENT_TYPES`] has been enabled with [`PushKeyboardEnhancementFlags`]. diff --git a/src/event/sys/unix/parse.rs b/src/event/sys/unix/parse.rs index c40cebc5..4da34d51 100644 --- a/src/event/sys/unix/parse.rs +++ b/src/event/sys/unix/parse.rs @@ -1,8 +1,9 @@ use std::io; use crate::event::{ - Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers, KeyboardEnhancementFlags, - MediaKeyCode, ModifierKeyCode, MouseButton, MouseEvent, MouseEventKind, + Event, KeyCode, KeyEvent, KeyEventKind::Release, KeyEventState, KeyModifiers, + KeyboardEnhancementFlags, MediaKeyCode, ModifierKeyCode, MouseButton, MouseEvent, + MouseEventKind, }; use super::super::super::InternalEvent; @@ -271,6 +272,7 @@ fn parse_csi_keyboard_enhancement_flags(buffer: &[u8]) -> io::Result KeyEventState { fn parse_key_event_kind(kind: u8) -> KeyEventKind { match kind { 1 => KeyEventKind::Press, + #[cfg(feature = "event-kind")] 2 => KeyEventKind::Repeat, + #[cfg(feature = "event-kind")] 3 => KeyEventKind::Release, _ => KeyEventKind::Press, } @@ -1332,6 +1336,7 @@ mod tests { KeyEventKind::Press, )))), ); + #[cfg(feature = "event-kind")] assert_eq!( parse_csi_u_encoded_key_code(b"\x1B[97;1:2u").unwrap(), Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( @@ -1340,6 +1345,7 @@ mod tests { KeyEventKind::Repeat, )))), ); + #[cfg(feature = "event-kind")] assert_eq!( parse_csi_u_encoded_key_code(b"\x1B[97;1:3u").unwrap(), Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( @@ -1360,6 +1366,7 @@ mod tests { KeyEventKind::Press, )))), ); + #[cfg(feature = "event-kind")] assert_eq!( parse_csi_u_encoded_key_code(b"\x1B[57449;3:3u").unwrap(), Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( @@ -1463,6 +1470,7 @@ mod tests { } #[test] + #[cfg(feature = "event-kind")] fn test_parse_csi_special_key_code_with_types() { assert_eq!( parse_event(b"\x1B[;1:3B", false).unwrap(), @@ -1483,6 +1491,7 @@ mod tests { } #[test] + #[cfg(feature = "event-kind")] fn test_parse_csi_numbered_escape_code_with_types() { assert_eq!( parse_event(b"\x1B[5;1:3~", false).unwrap(), diff --git a/src/event/sys/windows/parse.rs b/src/event/sys/windows/parse.rs index 288b7479..6c2d9cdf 100644 --- a/src/event/sys/windows/parse.rs +++ b/src/event/sys/windows/parse.rs @@ -223,7 +223,13 @@ fn parse_key_event_record(key_event: &KeyEventRecord) -> Option let kind = if key_event.key_down { KeyEventKind::Press } else { - KeyEventKind::Release + #[cfg(feature = "event-kind")] + { + KeyEventKind::Release + } + // Dont register key up event. + #[cfg(not(feature = "event-kind"))] + return None; }; let key_event = KeyEvent::new_with_kind(key_code, modifiers, kind); return Some(WindowsKeyEvent::KeyEvent(key_event)); @@ -235,10 +241,20 @@ fn parse_key_event_record(key_event: &KeyEventRecord) -> Option let is_numpad_numeric_key = (VK_NUMPAD0..=VK_NUMPAD9).contains(&virtual_key_code); let is_only_alt_modifier = modifiers.contains(KeyModifiers::ALT) && !modifiers.contains(KeyModifiers::SHIFT | KeyModifiers::CONTROL); + if is_only_alt_modifier && is_numpad_numeric_key { return None; } + if !key_event.key_down && virtual_key_code == VK_RETURN { + // For some reason in some cases we receive a release ENTER event here at the application start. + // This might have to do with the initial enter when running a CLI command. + // We early exit here to prevent confusion. + // + // https://github.com/crossterm-rs/crossterm/issues/752 + return None; + } + let parse_result = match virtual_key_code { VK_SHIFT | VK_CONTROL | VK_MENU => None, VK_BACK => Some(KeyCode::Backspace), @@ -286,7 +302,13 @@ fn parse_key_event_record(key_event: &KeyEventRecord) -> Option let kind = if key_event.key_down { KeyEventKind::Press } else { - KeyEventKind::Release + #[cfg(feature = "event-kind")] + { + KeyEventKind::Release + } + // Dont register key up event. + #[cfg(not(feature = "event-kind"))] + return None; }; let key_event = KeyEvent::new_with_kind(key_code, modifiers, kind); return Some(WindowsKeyEvent::KeyEvent(key_event));