Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make release event an opt-in feature #778

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
1 change: 0 additions & 1 deletion examples/interactive-demo/src/test/color.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#![allow(clippy::cognitive_complexity)]

use crate::Result;
use crossterm::{cursor, queue, style, style::Color};
use std::io::Write;

Expand Down
7 changes: 7 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -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`].
Expand Down
13 changes: 11 additions & 2 deletions src/event/sys/unix/parse.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -271,6 +272,7 @@ fn parse_csi_keyboard_enhancement_flags(buffer: &[u8]) -> io::Result<Option<Inte
if bits & 1 != 0 {
flags |= KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES;
}
#[cfg(feature = "event-kind")]
if bits & 2 != 0 {
flags |= KeyboardEnhancementFlags::REPORT_EVENT_TYPES;
}
Expand Down Expand Up @@ -339,7 +341,9 @@ fn parse_modifiers_to_state(mask: u8) -> 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,
}
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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(),
Expand All @@ -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(),
Expand Down
26 changes: 24 additions & 2 deletions src/event/sys/windows/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,13 @@ fn parse_key_event_record(key_event: &KeyEventRecord) -> Option<WindowsKeyEvent>
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));
Expand All @@ -235,10 +241,20 @@ fn parse_key_event_record(key_event: &KeyEventRecord) -> Option<WindowsKeyEvent>
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),
Expand Down Expand Up @@ -286,7 +302,13 @@ fn parse_key_event_record(key_event: &KeyEventRecord) -> Option<WindowsKeyEvent>
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));
Expand Down