2024-01-11 02:04:43 +00:00
//! # Event
//!
//! The `event` module provides the functionality to read keyboard, mouse and terminal resize events.
//!
//! * The [`read`](fn.read.html) function returns an [`Event`](enum.Event.html) immediately
//! (if available) or blocks until an [`Event`](enum.Event.html) is available.
//!
//! * The [`poll`](fn.poll.html) function allows you to check if there is or isn't an [`Event`](enum.Event.html) available
//! within the given period of time. In other words - if subsequent call to the [`read`](fn.read.html)
//! function will block or not.
//!
//! It's **not allowed** to call these functions from different threads or combine them with the
//! [`EventStream`](struct.EventStream.html). You're allowed to either:
//!
//! * use the [`read`](fn.read.html) & [`poll`](fn.poll.html) functions on any, but same, thread
//! * or the [`EventStream`](struct.EventStream.html).
//!
//! **Make sure to enable [raw mode](../terminal/index.html#raw-mode) in order for keyboard events to work properly**
//!
//! ## Mouse Events
//!
//! Mouse events are not enabled by default. You have to enable them with the
//! [`EnableMouseCapture`](struct.EnableMouseCapture.html) command. See [Command API](../index.html#command-api)
//! for more information.
//!
//! ## Examples
//!
//! Blocking read:
//!
//! ```no_run
2024-01-11 02:21:34 +00:00
//! use keyfork_crossterm::event::{read, Event};
2024-01-11 02:04:43 +00:00
//!
//! fn print_events() -> std::io::Result<()> {
//! loop {
//! // `read()` blocks until an `Event` is available
//! match read()? {
//! Event::FocusGained => println!("FocusGained"),
//! Event::FocusLost => println!("FocusLost"),
//! Event::Key(event) => println!("{:?}", event),
//! Event::Mouse(event) => println!("{:?}", event),
//! #[cfg(feature = "bracketed-paste")]
//! Event::Paste(data) => println!("{:?}", data),
//! Event::Resize(width, height) => println!("New size {}x{}", width, height),
//! }
//! }
//! Ok(())
//! }
//! ```
//!
//! Non-blocking read:
//!
//! ```no_run
//! use std::{time::Duration, io};
//!
2024-01-11 02:21:34 +00:00
//! use keyfork_crossterm::event::{poll, read, Event};
2024-01-11 02:04:43 +00:00
//!
//! fn print_events() -> io::Result<()> {
//! loop {
//! // `poll()` waits for an `Event` for a given time period
//! if poll(Duration::from_millis(500))? {
//! // It's guaranteed that the `read()` won't block when the `poll()`
//! // function returns `true`
//! match read()? {
//! Event::FocusGained => println!("FocusGained"),
//! Event::FocusLost => println!("FocusLost"),
//! Event::Key(event) => println!("{:?}", event),
//! Event::Mouse(event) => println!("{:?}", event),
//! #[cfg(feature = "bracketed-paste")]
//! Event::Paste(data) => println!("Pasted {:?}", data),
//! Event::Resize(width, height) => println!("New size {}x{}", width, height),
//! }
//! } else {
//! // Timeout expired and no `Event` is available
//! }
//! }
//! Ok(())
//! }
//! ```
//!
//! Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder for more of
//! them (`event-*`).
pub ( crate ) mod filter ;
pub ( crate ) mod read ;
pub ( crate ) mod source ;
#[ cfg(feature = " event-stream " ) ]
pub ( crate ) mod stream ;
pub ( crate ) mod sys ;
pub ( crate ) mod timeout ;
#[ cfg(feature = " event-stream " ) ]
pub use stream ::EventStream ;
use crate ::event ::{
filter ::{ EventFilter , Filter } ,
read ::InternalEventReader ,
timeout ::PollTimeout ,
} ;
use crate ::{ csi , Command } ;
use parking_lot ::{ MappedMutexGuard , Mutex , MutexGuard } ;
use std ::fmt ;
use std ::time ::Duration ;
use bitflags ::bitflags ;
use std ::hash ::{ Hash , Hasher } ;
/// Static instance of `InternalEventReader`.
/// This needs to be static because there can be one event reader.
static INTERNAL_EVENT_READER : Mutex < Option < InternalEventReader > > = parking_lot ::const_mutex ( None ) ;
pub ( crate ) fn lock_internal_event_reader ( ) -> MappedMutexGuard < 'static , InternalEventReader > {
MutexGuard ::map ( INTERNAL_EVENT_READER . lock ( ) , | reader | {
reader . get_or_insert_with ( InternalEventReader ::default )
} )
}
fn try_lock_internal_event_reader_for (
duration : Duration ,
) -> Option < MappedMutexGuard < 'static , InternalEventReader > > {
Some ( MutexGuard ::map (
INTERNAL_EVENT_READER . try_lock_for ( duration ) ? ,
| reader | reader . get_or_insert_with ( InternalEventReader ::default ) ,
) )
}
/// Checks if there is an [`Event`](enum.Event.html) available.
///
/// Returns `Ok(true)` if an [`Event`](enum.Event.html) is available otherwise it returns `Ok(false)`.
///
/// `Ok(true)` guarantees that subsequent call to the [`read`](fn.read.html) function
/// won't block.
///
/// # Arguments
///
/// * `timeout` - maximum waiting time for event availability
///
/// # Examples
///
/// Return immediately:
///
/// ```no_run
/// use std::{time::Duration, io};
2024-01-11 02:21:34 +00:00
/// use keyfork_crossterm::{event::poll};
2024-01-11 02:04:43 +00:00
///
/// fn is_event_available() -> io::Result<bool> {
/// // Zero duration says that the `poll` function must return immediately
/// // with an `Event` availability information
/// poll(Duration::from_secs(0))
/// }
/// ```
///
/// Wait up to 100ms:
///
/// ```no_run
/// use std::{time::Duration, io};
///
2024-01-11 02:21:34 +00:00
/// use keyfork_crossterm::event::poll;
2024-01-11 02:04:43 +00:00
///
/// fn is_event_available() -> io::Result<bool> {
/// // Wait for an `Event` availability for 100ms. It returns immediately
/// // if an `Event` is/becomes available.
/// poll(Duration::from_millis(100))
/// }
/// ```
pub fn poll ( timeout : Duration ) -> std ::io ::Result < bool > {
poll_internal ( Some ( timeout ) , & EventFilter )
}
/// Reads a single [`Event`](enum.Event.html).
///
/// This function blocks until an [`Event`](enum.Event.html) is available. Combine it with the
/// [`poll`](fn.poll.html) function to get non-blocking reads.
///
/// # Examples
///
/// Blocking read:
///
/// ```no_run
2024-01-11 02:21:34 +00:00
/// use keyfork_crossterm::event::read;
2024-01-11 02:04:43 +00:00
/// use std::io;
///
/// fn print_events() -> io::Result<bool> {
/// loop {
/// // Blocks until an `Event` is available
/// println!("{:?}", read()?);
/// }
/// }
/// ```
///
/// Non-blocking read:
///
/// ```no_run
/// use std::time::Duration;
/// use std::io;
///
2024-01-11 02:21:34 +00:00
/// use keyfork_crossterm::event::{read, poll};
2024-01-11 02:04:43 +00:00
///
/// fn print_events() -> io::Result<bool> {
/// loop {
/// if poll(Duration::from_millis(100))? {
/// // It's guaranteed that `read` won't block, because `poll` returned
/// // `Ok(true)`.
/// println!("{:?}", read()?);
/// } else {
/// // Timeout expired, no `Event` is available
/// }
/// }
/// }
/// ```
pub fn read ( ) -> std ::io ::Result < Event > {
match read_internal ( & EventFilter ) ? {
InternalEvent ::Event ( event ) = > Ok ( event ) ,
#[ cfg(unix) ]
_ = > unreachable! ( ) ,
}
}
/// Polls to check if there are any `InternalEvent`s that can be read within the given duration.
pub ( crate ) fn poll_internal < F > ( timeout : Option < Duration > , filter : & F ) -> std ::io ::Result < bool >
where
F : Filter ,
{
let ( mut reader , timeout ) = if let Some ( timeout ) = timeout {
let poll_timeout = PollTimeout ::new ( Some ( timeout ) ) ;
if let Some ( reader ) = try_lock_internal_event_reader_for ( timeout ) {
( reader , poll_timeout . leftover ( ) )
} else {
return Ok ( false ) ;
}
} else {
( lock_internal_event_reader ( ) , None )
} ;
reader . poll ( timeout , filter )
}
/// Reads a single `InternalEvent`.
pub ( crate ) fn read_internal < F > ( filter : & F ) -> std ::io ::Result < InternalEvent >
where
F : Filter ,
{
let mut reader = lock_internal_event_reader ( ) ;
reader . read ( filter )
}
bitflags! {
/// Represents special flags that tell compatible terminals to add extra information to keyboard events.
///
/// See <https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement> for more information.
///
/// Alternate keys and Unicode codepoints are not yet supported by crossterm.
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize), serde(transparent)) ]
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
pub struct KeyboardEnhancementFlags : u8 {
/// Represent Escape and modified keys using CSI-u sequences, so they can be unambiguously
/// read.
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.
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
// resulting `KeyEvent`s.
const REPORT_ALTERNATE_KEYS = 0b0000_0100 ;
/// Represent all keyboard events as CSI-u sequences. This is required to get repeat/release
/// events for plain-text keys.
const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 0b0000_1000 ;
// Send the Unicode codepoint as well as the keycode.
//
// *Note*: this is not yet supported by crossterm.
// const REPORT_ASSOCIATED_TEXT = 0b0001_0000;
}
}
/// A command that enables mouse event capturing.
///
/// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
#[ cfg(feature = " events " ) ]
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct EnableMouseCapture ;
#[ cfg(feature = " events " ) ]
impl Command for EnableMouseCapture {
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
f . write_str ( concat! (
// Normal tracking: Send mouse X & Y on button press and release
csi! ( " ?1000h " ) ,
// Button-event tracking: Report button motion events (dragging)
csi! ( " ?1002h " ) ,
// Any-event tracking: Report all motion events
csi! ( " ?1003h " ) ,
// RXVT mouse mode: Allows mouse coordinates of >223
csi! ( " ?1015h " ) ,
// SGR mouse mode: Allows mouse coordinates of >223, preferred over RXVT mode
csi! ( " ?1006h " ) ,
) )
}
#[ cfg(windows) ]
fn execute_winapi ( & self ) -> std ::io ::Result < ( ) > {
sys ::windows ::enable_mouse_capture ( )
}
#[ cfg(windows) ]
fn is_ansi_code_supported ( & self ) -> bool {
false
}
}
/// A command that disables mouse event capturing.
///
/// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct DisableMouseCapture ;
impl Command for DisableMouseCapture {
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
f . write_str ( concat! (
// The inverse commands of EnableMouseCapture, in reverse order.
csi! ( " ?1006l " ) ,
csi! ( " ?1015l " ) ,
csi! ( " ?1003l " ) ,
csi! ( " ?1002l " ) ,
csi! ( " ?1000l " ) ,
) )
}
#[ cfg(windows) ]
fn execute_winapi ( & self ) -> std ::io ::Result < ( ) > {
sys ::windows ::disable_mouse_capture ( )
}
#[ cfg(windows) ]
fn is_ansi_code_supported ( & self ) -> bool {
false
}
}
/// A command that enables focus event emission.
///
/// It should be paired with [`DisableFocusChange`] at the end of execution.
///
/// Focus events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct EnableFocusChange ;
impl Command for EnableFocusChange {
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
f . write_str ( csi! ( " ?1004h " ) )
}
#[ cfg(windows) ]
fn execute_winapi ( & self ) -> std ::io ::Result < ( ) > {
// Focus events are always enabled on Windows
Ok ( ( ) )
}
}
/// A command that disables focus event emission.
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct DisableFocusChange ;
impl Command for DisableFocusChange {
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
f . write_str ( csi! ( " ?1004l " ) )
}
#[ cfg(windows) ]
fn execute_winapi ( & self ) -> std ::io ::Result < ( ) > {
// Focus events can't be disabled on Windows
Ok ( ( ) )
}
}
/// A command that enables [bracketed paste mode](https://en.wikipedia.org/wiki/Bracketed-paste).
///
/// It should be paired with [`DisableBracketedPaste`] at the end of execution.
///
/// This is not supported in older Windows terminals without
/// [virtual terminal sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences).
#[ cfg(feature = " bracketed-paste " ) ]
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct EnableBracketedPaste ;
#[ cfg(feature = " bracketed-paste " ) ]
impl Command for EnableBracketedPaste {
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
f . write_str ( csi! ( " ?2004h " ) )
}
#[ cfg(windows) ]
fn execute_winapi ( & self ) -> std ::io ::Result < ( ) > {
Err ( std ::io ::Error ::new (
std ::io ::ErrorKind ::Unsupported ,
" Bracketed paste not implemented in the legacy Windows API. " ,
) )
}
}
/// A command that disables bracketed paste mode.
#[ cfg(feature = " bracketed-paste " ) ]
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct DisableBracketedPaste ;
#[ cfg(feature = " bracketed-paste " ) ]
impl Command for DisableBracketedPaste {
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
f . write_str ( csi! ( " ?2004l " ) )
}
#[ cfg(windows) ]
fn execute_winapi ( & self ) -> std ::io ::Result < ( ) > {
Ok ( ( ) )
}
}
/// A command that enables the [kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/), which adds extra information to keyboard events and removes ambiguity for modifier keys.
///
/// It should be paired with [`PopKeyboardEnhancementFlags`] at the end of execution.
///
/// Example usage:
/// ```no_run
/// use std::io::{Write, stdout};
2024-01-11 02:21:34 +00:00
/// use keyfork_crossterm::execute;
/// use keyfork_crossterm::event::{
2024-01-11 02:04:43 +00:00
/// KeyboardEnhancementFlags,
/// PushKeyboardEnhancementFlags,
/// PopKeyboardEnhancementFlags
/// };
///
/// let mut stdout = stdout();
///
/// execute!(
/// stdout,
/// PushKeyboardEnhancementFlags(
/// KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
/// )
/// );
///
/// // ...
///
/// execute!(stdout, PopKeyboardEnhancementFlags);
/// ```
///
/// Note that, currently, only the following support this protocol:
/// * [kitty terminal](https://sw.kovidgoyal.net/kitty/)
/// * [foot terminal](https://codeberg.org/dnkl/foot/issues/319)
/// * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html)
/// * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131)
/// * [neovim text editor](https://github.com/neovim/neovim/pull/18181)
/// * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103)
/// * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138)
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct PushKeyboardEnhancementFlags ( pub KeyboardEnhancementFlags ) ;
impl Command for PushKeyboardEnhancementFlags {
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
write! ( f , " {}{}u " , csi! ( " > " ) , self . 0. bits ( ) )
}
#[ cfg(windows) ]
fn execute_winapi ( & self ) -> std ::io ::Result < ( ) > {
use std ::io ;
Err ( io ::Error ::new (
io ::ErrorKind ::Unsupported ,
" Keyboard progressive enhancement not implemented for the legacy Windows API. " ,
) )
}
#[ cfg(windows) ]
fn is_ansi_code_supported ( & self ) -> bool {
false
}
}
/// A command that disables extra kinds of keyboard events.
///
/// Specifically, it pops one level of keyboard enhancement flags.
///
/// See [`PushKeyboardEnhancementFlags`] and <https://sw.kovidgoyal.net/kitty/keyboard-protocol/> for more information.
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct PopKeyboardEnhancementFlags ;
impl Command for PopKeyboardEnhancementFlags {
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
f . write_str ( csi! ( " <1u " ) )
}
#[ cfg(windows) ]
fn execute_winapi ( & self ) -> std ::io ::Result < ( ) > {
use std ::io ;
Err ( io ::Error ::new (
io ::ErrorKind ::Unsupported ,
" Keyboard progressive enhancement not implemented for the legacy Windows API. " ,
) )
}
#[ cfg(windows) ]
fn is_ansi_code_supported ( & self ) -> bool {
false
}
}
/// Represents an event.
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize)) ]
#[ cfg_attr(not(feature = " bracketed-paste " ), derive(Copy)) ]
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash) ]
pub enum Event {
/// The terminal gained focus
FocusGained ,
/// The terminal lost focus
FocusLost ,
/// A single key event with additional pressed modifiers.
Key ( KeyEvent ) ,
/// A single mouse event with additional pressed modifiers.
Mouse ( MouseEvent ) ,
/// A string that was pasted into the terminal. Only emitted if bracketed paste has been
/// enabled.
#[ cfg(feature = " bracketed-paste " ) ]
Paste ( String ) ,
/// An resize event with new dimensions after resize (columns, rows).
/// **Note** that resize events can occur in batches.
Resize ( u16 , u16 ) ,
}
/// Represents a mouse event.
///
/// # Platform-specific Notes
///
/// ## Mouse Buttons
///
/// Some platforms/terminals do not report mouse button for the
/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left`
/// is returned if we don't know which button was used.
///
/// ## Key Modifiers
///
/// Some platforms/terminals does not report all key modifiers
/// combinations for all mouse event types. For example - macOS reports
/// `Ctrl` + left mouse button click as a right mouse button click.
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize)) ]
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
pub struct MouseEvent {
/// The kind of mouse event that was caused.
pub kind : MouseEventKind ,
/// The column that the event occurred on.
pub column : u16 ,
/// The row that the event occurred on.
pub row : u16 ,
/// The key modifiers active when the event occurred.
pub modifiers : KeyModifiers ,
}
/// A mouse event kind.
///
/// # Platform-specific Notes
///
/// ## Mouse Buttons
///
/// Some platforms/terminals do not report mouse button for the
/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left`
/// is returned if we don't know which button was used.
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize)) ]
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
pub enum MouseEventKind {
/// Pressed mouse button. Contains the button that was pressed.
Down ( MouseButton ) ,
/// Released mouse button. Contains the button that was released.
Up ( MouseButton ) ,
/// Moved the mouse cursor while pressing the contained mouse button.
Drag ( MouseButton ) ,
/// Moved the mouse cursor while not pressing a mouse button.
Moved ,
/// Scrolled mouse wheel downwards (towards the user).
ScrollDown ,
/// Scrolled mouse wheel upwards (away from the user).
ScrollUp ,
/// Scrolled mouse wheel left (mostly on a laptop touchpad).
ScrollLeft ,
/// Scrolled mouse wheel right (mostly on a laptop touchpad).
ScrollRight ,
}
/// Represents a mouse button.
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize)) ]
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
pub enum MouseButton {
/// Left mouse button.
Left ,
/// Right mouse button.
Right ,
/// Middle mouse button.
Middle ,
}
bitflags! {
/// Represents key modifiers (shift, control, alt, etc.).
///
/// **Note:** `SUPER`, `HYPER`, and `META` can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize), serde(transparent)) ]
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
pub struct KeyModifiers : u8 {
const SHIFT = 0b0000_0001 ;
const CONTROL = 0b0000_0010 ;
const ALT = 0b0000_0100 ;
const SUPER = 0b0000_1000 ;
const HYPER = 0b0001_0000 ;
const META = 0b0010_0000 ;
const NONE = 0b0000_0000 ;
}
}
/// Represents a keyboard event kind.
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize)) ]
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
pub enum KeyEventKind {
Press ,
Repeat ,
Release ,
}
bitflags! {
/// Represents extra state about the key event.
///
/// **Note:** This state can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize), serde(transparent)) ]
pub struct KeyEventState : u8 {
/// The key event origins from the keypad.
const KEYPAD = 0b0000_0001 ;
/// Caps Lock was enabled for this key event.
///
/// **Note:** this is set for the initial press of Caps Lock itself.
const CAPS_LOCK = 0b0000_1000 ;
/// Num Lock was enabled for this key event.
///
/// **Note:** this is set for the initial press of Num Lock itself.
const NUM_LOCK = 0b0000_1000 ;
const NONE = 0b0000_0000 ;
}
}
/// Represents a key event.
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize)) ]
#[ derive(Debug, PartialOrd, Clone, Copy) ]
pub struct KeyEvent {
/// The key itself.
pub code : KeyCode ,
/// Additional key modifiers.
pub modifiers : KeyModifiers ,
/// Kind of event.
///
/// Only set if:
/// - Unix: [`KeyboardEnhancementFlags::REPORT_EVENT_TYPES`] has been enabled with [`PushKeyboardEnhancementFlags`].
/// - Windows: always
pub kind : KeyEventKind ,
/// Keyboard state.
///
/// Only set if [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
pub state : KeyEventState ,
}
impl KeyEvent {
pub const fn new ( code : KeyCode , modifiers : KeyModifiers ) -> KeyEvent {
KeyEvent {
code ,
modifiers ,
kind : KeyEventKind ::Press ,
state : KeyEventState ::empty ( ) ,
}
}
pub const fn new_with_kind (
code : KeyCode ,
modifiers : KeyModifiers ,
kind : KeyEventKind ,
) -> KeyEvent {
KeyEvent {
code ,
modifiers ,
kind ,
state : KeyEventState ::empty ( ) ,
}
}
pub const fn new_with_kind_and_state (
code : KeyCode ,
modifiers : KeyModifiers ,
kind : KeyEventKind ,
state : KeyEventState ,
) -> KeyEvent {
KeyEvent {
code ,
modifiers ,
kind ,
state ,
}
}
// modifies the KeyEvent,
// so that KeyModifiers::SHIFT is present iff
// an uppercase char is present.
fn normalize_case ( mut self ) -> KeyEvent {
let c = match self . code {
KeyCode ::Char ( c ) = > c ,
_ = > return self ,
} ;
if c . is_ascii_uppercase ( ) {
self . modifiers . insert ( KeyModifiers ::SHIFT ) ;
} else if self . modifiers . contains ( KeyModifiers ::SHIFT ) {
self . code = KeyCode ::Char ( c . to_ascii_uppercase ( ) )
}
self
}
}
impl From < KeyCode > for KeyEvent {
fn from ( code : KeyCode ) -> Self {
KeyEvent {
code ,
modifiers : KeyModifiers ::empty ( ) ,
kind : KeyEventKind ::Press ,
state : KeyEventState ::empty ( ) ,
}
}
}
impl PartialEq for KeyEvent {
fn eq ( & self , other : & KeyEvent ) -> bool {
let KeyEvent {
code : lhs_code ,
modifiers : lhs_modifiers ,
kind : lhs_kind ,
state : lhs_state ,
} = self . normalize_case ( ) ;
let KeyEvent {
code : rhs_code ,
modifiers : rhs_modifiers ,
kind : rhs_kind ,
state : rhs_state ,
} = other . normalize_case ( ) ;
( lhs_code = = rhs_code )
& & ( lhs_modifiers = = rhs_modifiers )
& & ( lhs_kind = = rhs_kind )
& & ( lhs_state = = rhs_state )
}
}
impl Eq for KeyEvent { }
impl Hash for KeyEvent {
fn hash < H : Hasher > ( & self , hash_state : & mut H ) {
let KeyEvent {
code ,
modifiers ,
kind ,
state ,
} = self . normalize_case ( ) ;
code . hash ( hash_state ) ;
modifiers . hash ( hash_state ) ;
kind . hash ( hash_state ) ;
state . hash ( hash_state ) ;
}
}
/// Represents a media key (as part of [`KeyCode::Media`]).
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize)) ]
pub enum MediaKeyCode {
/// Play media key.
Play ,
/// Pause media key.
Pause ,
/// Play/Pause media key.
PlayPause ,
/// Reverse media key.
Reverse ,
/// Stop media key.
Stop ,
/// Fast-forward media key.
FastForward ,
/// Rewind media key.
Rewind ,
/// Next-track media key.
TrackNext ,
/// Previous-track media key.
TrackPrevious ,
/// Record media key.
Record ,
/// Lower-volume media key.
LowerVolume ,
/// Raise-volume media key.
RaiseVolume ,
/// Mute media key.
MuteVolume ,
}
/// Represents a modifier key (as part of [`KeyCode::Modifier`]).
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize)) ]
pub enum ModifierKeyCode {
/// Left Shift key.
LeftShift ,
/// Left Control key.
LeftControl ,
/// Left Alt key.
LeftAlt ,
/// Left Super key.
LeftSuper ,
/// Left Hyper key.
LeftHyper ,
/// Left Meta key.
LeftMeta ,
/// Right Shift key.
RightShift ,
/// Right Control key.
RightControl ,
/// Right Alt key.
RightAlt ,
/// Right Super key.
RightSuper ,
/// Right Hyper key.
RightHyper ,
/// Right Meta key.
RightMeta ,
/// Iso Level3 Shift key.
IsoLevel3Shift ,
/// Iso Level5 Shift key.
IsoLevel5Shift ,
}
/// Represents a key.
#[ derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash) ]
#[ cfg_attr(feature = " serde " , derive(serde::Serialize, serde::Deserialize)) ]
pub enum KeyCode {
/// Backspace key.
Backspace ,
/// Enter key.
Enter ,
/// Left arrow key.
Left ,
/// Right arrow key.
Right ,
/// Up arrow key.
Up ,
/// Down arrow key.
Down ,
/// Home key.
Home ,
/// End key.
End ,
/// Page up key.
PageUp ,
/// Page down key.
PageDown ,
/// Tab key.
Tab ,
/// Shift + Tab key.
BackTab ,
/// Delete key.
Delete ,
/// Insert key.
Insert ,
/// F key.
///
/// `KeyCode::F(1)` represents F1 key, etc.
F ( u8 ) ,
/// A character.
///
/// `KeyCode::Char('c')` represents `c` character, etc.
Char ( char ) ,
/// Null.
Null ,
/// Escape key.
Esc ,
/// Caps Lock key.
///
/// **Note:** this key can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
CapsLock ,
/// Scroll Lock key.
///
/// **Note:** this key can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
ScrollLock ,
/// Num Lock key.
///
/// **Note:** this key can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
NumLock ,
/// Print Screen key.
///
/// **Note:** this key can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
PrintScreen ,
/// Pause key.
///
/// **Note:** this key can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
Pause ,
/// Menu key.
///
/// **Note:** this key can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
Menu ,
/// The "Begin" key (often mapped to the 5 key when Num Lock is turned on).
///
/// **Note:** this key can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
KeypadBegin ,
/// A media key.
///
/// **Note:** these keys can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
Media ( MediaKeyCode ) ,
/// A modifier key.
///
/// **Note:** these keys can only be read if **both**
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] and
/// [`KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES`] have been enabled with
/// [`PushKeyboardEnhancementFlags`].
Modifier ( ModifierKeyCode ) ,
}
/// An internal event.
///
/// Encapsulates publicly available `Event` with additional internal
/// events that shouldn't be publicly available to the crate users.
#[ derive(Debug, PartialOrd, PartialEq, Hash, Clone, Eq) ]
pub ( crate ) enum InternalEvent {
/// An event.
Event ( Event ) ,
/// A cursor position (`col`, `row`).
#[ cfg(unix) ]
CursorPosition ( u16 , u16 ) ,
/// The progressive keyboard enhancement flags enabled by the terminal.
#[ cfg(unix) ]
KeyboardEnhancementFlags ( KeyboardEnhancementFlags ) ,
/// Attributes and architectural class of the terminal.
#[ cfg(unix) ]
PrimaryDeviceAttributes ,
}
#[ cfg(test) ]
mod tests {
use std ::collections ::hash_map ::DefaultHasher ;
use std ::hash ::{ Hash , Hasher } ;
use super ::{ KeyCode , KeyEvent , KeyModifiers } ;
#[ test ]
fn test_equality ( ) {
let lowercase_d_with_shift = KeyEvent ::new ( KeyCode ::Char ( 'd' ) , KeyModifiers ::SHIFT ) ;
let uppercase_d_with_shift = KeyEvent ::new ( KeyCode ::Char ( 'D' ) , KeyModifiers ::SHIFT ) ;
let uppercase_d = KeyEvent ::new ( KeyCode ::Char ( 'D' ) , KeyModifiers ::NONE ) ;
assert_eq! ( lowercase_d_with_shift , uppercase_d_with_shift ) ;
assert_eq! ( uppercase_d , uppercase_d_with_shift ) ;
}
#[ test ]
fn test_hash ( ) {
let lowercase_d_with_shift_hash = {
let mut hasher = DefaultHasher ::new ( ) ;
KeyEvent ::new ( KeyCode ::Char ( 'd' ) , KeyModifiers ::SHIFT ) . hash ( & mut hasher ) ;
hasher . finish ( )
} ;
let uppercase_d_with_shift_hash = {
let mut hasher = DefaultHasher ::new ( ) ;
KeyEvent ::new ( KeyCode ::Char ( 'D' ) , KeyModifiers ::SHIFT ) . hash ( & mut hasher ) ;
hasher . finish ( )
} ;
let uppercase_d_hash = {
let mut hasher = DefaultHasher ::new ( ) ;
KeyEvent ::new ( KeyCode ::Char ( 'D' ) , KeyModifiers ::NONE ) . hash ( & mut hasher ) ;
hasher . finish ( )
} ;
assert_eq! ( lowercase_d_with_shift_hash , uppercase_d_with_shift_hash ) ;
assert_eq! ( uppercase_d_hash , uppercase_d_with_shift_hash ) ;
}
}