//! # 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 //! use crossterm::event::{read, Event}; //! //! 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}; //! //! use crossterm::event::{poll, read, Event}; //! //! 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> = 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> { 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}; /// use crossterm::{event::poll}; /// /// fn is_event_available() -> io::Result { /// // 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}; /// /// use crossterm::event::poll; /// /// fn is_event_available() -> io::Result { /// // 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 { 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 /// use crossterm::event::read; /// use std::io; /// /// fn print_events() -> io::Result { /// loop { /// // Blocks until an `Event` is available /// println!("{:?}", read()?); /// } /// } /// ``` /// /// Non-blocking read: /// /// ```no_run /// use std::time::Duration; /// use std::io; /// /// use crossterm::event::{read, poll}; /// /// fn print_events() -> io::Result { /// 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 { 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(timeout: Option, filter: &F) -> std::io::Result 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(filter: &F) -> std::io::Result 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 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}; /// use crossterm::execute; /// use crossterm::event::{ /// 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 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 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(&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); } }