crossterm/
event.rs

1//! # Event
2//!
3//! The `event` module provides the functionality to read keyboard, mouse and terminal resize events.
4//!
5//! * The [`read`](fn.read.html) function returns an [`Event`](enum.Event.html) immediately
6//! (if available) or blocks until an [`Event`](enum.Event.html) is available.
7//!
8//! * The [`poll`](fn.poll.html) function allows you to check if there is or isn't an [`Event`](enum.Event.html) available
9//! within the given period of time. In other words - if subsequent call to the [`read`](fn.read.html)
10//! function will block or not.
11//!
12//! It's **not allowed** to call these functions from different threads or combine them with the
13//! [`EventStream`](struct.EventStream.html). You're allowed to either:
14//!
15//! * use the [`read`](fn.read.html) & [`poll`](fn.poll.html) functions on any, but same, thread
16//! * or the [`EventStream`](struct.EventStream.html).
17//!
18//! **Make sure to enable [raw mode](../terminal/index.html#raw-mode) in order for keyboard events to work properly**
19//!
20//! ## Mouse and Focus Events
21//!
22//! Mouse and focus events are not enabled by default. You have to enable them with the
23//! [`EnableMouseCapture`](struct.EnableMouseCapture.html) / [`EnableFocusChange`](struct.EnableFocusChange.html) command.
24//! See [Command API](../index.html#command-api) for more information.
25//!
26//! ## Examples
27//!
28//! Blocking read:
29//!
30//! ```no_run
31//! #![cfg(feature = "bracketed-paste")]
32//! use crossterm::{
33//!     event::{
34//!         read, DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste,
35//!         EnableFocusChange, EnableMouseCapture, Event,
36//!     },
37//!     execute,
38//! };
39//!
40//! fn print_events() -> std::io::Result<()> {
41//!     execute!(
42//!          std::io::stdout(),
43//!          EnableBracketedPaste,
44//!          EnableFocusChange,
45//!          EnableMouseCapture
46//!     )?;
47//!     loop {
48//!         // `read()` blocks until an `Event` is available
49//!         match read()? {
50//!             Event::FocusGained => println!("FocusGained"),
51//!             Event::FocusLost => println!("FocusLost"),
52//!             Event::Key(event) => println!("{:?}", event),
53//!             Event::Mouse(event) => println!("{:?}", event),
54//!             #[cfg(feature = "bracketed-paste")]
55//!             Event::Paste(data) => println!("{:?}", data),
56//!             Event::Resize(width, height) => println!("New size {}x{}", width, height),
57//!         }
58//!     }
59//!     execute!(
60//!         std::io::stdout(),
61//!         DisableBracketedPaste,
62//!         DisableFocusChange,
63//!         DisableMouseCapture
64//!     )?;
65//!     Ok(())
66//! }
67//! ```
68//!
69//! Non-blocking read:
70//!
71//! ```no_run
72//! #![cfg(feature = "bracketed-paste")]
73//! use std::{time::Duration, io};
74//!
75//! use crossterm::{
76//!     event::{
77//!         poll, read, DisableBracketedPaste, DisableFocusChange, DisableMouseCapture,
78//!         EnableBracketedPaste, EnableFocusChange, EnableMouseCapture, Event,
79//!     },
80//!     execute,
81//! };
82//!
83//! fn print_events() -> io::Result<()> {
84//!     execute!(
85//!          std::io::stdout(),
86//!          EnableBracketedPaste,
87//!          EnableFocusChange,
88//!          EnableMouseCapture
89//!     )?;
90//!     loop {
91//!         // `poll()` waits for an `Event` for a given time period
92//!         if poll(Duration::from_millis(500))? {
93//!             // It's guaranteed that the `read()` won't block when the `poll()`
94//!             // function returns `true`
95//!             match read()? {
96//!                 Event::FocusGained => println!("FocusGained"),
97//!                 Event::FocusLost => println!("FocusLost"),
98//!                 Event::Key(event) => println!("{:?}", event),
99//!                 Event::Mouse(event) => println!("{:?}", event),
100//!                 #[cfg(feature = "bracketed-paste")]
101//!                 Event::Paste(data) => println!("Pasted {:?}", data),
102//!                 Event::Resize(width, height) => println!("New size {}x{}", width, height),
103//!             }
104//!         } else {
105//!             // Timeout expired and no `Event` is available
106//!         }
107//!     }
108//!     execute!(
109//!         std::io::stdout(),
110//!         DisableBracketedPaste,
111//!         DisableFocusChange,
112//!         DisableMouseCapture
113//!     )?;
114//!     Ok(())
115//! }
116//! ```
117//!
118//! Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder for more of
119//! them (`event-*`).
120
121pub(crate) mod filter;
122pub(crate) mod read;
123pub(crate) mod source;
124#[cfg(feature = "event-stream")]
125pub(crate) mod stream;
126pub(crate) mod sys;
127pub(crate) mod timeout;
128
129#[cfg(feature = "event-stream")]
130pub use stream::EventStream;
131
132use crate::event::{
133    filter::{EventFilter, Filter},
134    read::InternalEventReader,
135    timeout::PollTimeout,
136};
137use crate::{csi, Command};
138use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
139use std::fmt::{self, Display};
140use std::time::Duration;
141
142use bitflags::bitflags;
143use std::hash::{Hash, Hasher};
144
145/// Static instance of `InternalEventReader`.
146/// This needs to be static because there can be one event reader.
147static INTERNAL_EVENT_READER: Mutex<Option<InternalEventReader>> = parking_lot::const_mutex(None);
148
149pub(crate) fn lock_internal_event_reader() -> MappedMutexGuard<'static, InternalEventReader> {
150    MutexGuard::map(INTERNAL_EVENT_READER.lock(), |reader| {
151        reader.get_or_insert_with(InternalEventReader::default)
152    })
153}
154fn try_lock_internal_event_reader_for(
155    duration: Duration,
156) -> Option<MappedMutexGuard<'static, InternalEventReader>> {
157    Some(MutexGuard::map(
158        INTERNAL_EVENT_READER.try_lock_for(duration)?,
159        |reader| reader.get_or_insert_with(InternalEventReader::default),
160    ))
161}
162
163/// Checks if there is an [`Event`](enum.Event.html) available.
164///
165/// Returns `Ok(true)` if an [`Event`](enum.Event.html) is available otherwise it returns `Ok(false)`.
166///
167/// `Ok(true)` guarantees that subsequent call to the [`read`](fn.read.html) function
168/// won't block.
169///
170/// # Arguments
171///
172/// * `timeout` - maximum waiting time for event availability
173///
174/// # Examples
175///
176/// Return immediately:
177///
178/// ```no_run
179/// use std::{time::Duration, io};
180/// use crossterm::{event::poll};
181///
182/// fn is_event_available() -> io::Result<bool> {
183///     // Zero duration says that the `poll` function must return immediately
184///     // with an `Event` availability information
185///     poll(Duration::from_secs(0))
186/// }
187/// ```
188///
189/// Wait up to 100ms:
190///
191/// ```no_run
192/// use std::{time::Duration, io};
193///
194/// use crossterm::event::poll;
195///
196/// fn is_event_available() -> io::Result<bool> {
197///     // Wait for an `Event` availability for 100ms. It returns immediately
198///     // if an `Event` is/becomes available.
199///     poll(Duration::from_millis(100))
200/// }
201/// ```
202pub fn poll(timeout: Duration) -> std::io::Result<bool> {
203    poll_internal(Some(timeout), &EventFilter)
204}
205
206/// Reads a single [`Event`](enum.Event.html).
207///
208/// This function blocks until an [`Event`](enum.Event.html) is available. Combine it with the
209/// [`poll`](fn.poll.html) function to get non-blocking reads.
210///
211/// # Examples
212///
213/// Blocking read:
214///
215/// ```no_run
216/// use crossterm::event::read;
217/// use std::io;
218///
219/// fn print_events() -> io::Result<bool> {
220///     loop {
221///         // Blocks until an `Event` is available
222///         println!("{:?}", read()?);
223///     }
224/// }
225/// ```
226///
227/// Non-blocking read:
228///
229/// ```no_run
230/// use std::time::Duration;
231/// use std::io;
232///
233/// use crossterm::event::{read, poll};
234///
235/// fn print_events() -> io::Result<bool> {
236///     loop {
237///         if poll(Duration::from_millis(100))? {
238///             // It's guaranteed that `read` won't block, because `poll` returned
239///             // `Ok(true)`.
240///             println!("{:?}", read()?);
241///         } else {
242///             // Timeout expired, no `Event` is available
243///         }
244///     }
245/// }
246/// ```
247pub fn read() -> std::io::Result<Event> {
248    match read_internal(&EventFilter)? {
249        InternalEvent::Event(event) => Ok(event),
250        #[cfg(unix)]
251        _ => unreachable!(),
252    }
253}
254
255/// Polls to check if there are any `InternalEvent`s that can be read within the given duration.
256pub(crate) fn poll_internal<F>(timeout: Option<Duration>, filter: &F) -> std::io::Result<bool>
257where
258    F: Filter,
259{
260    let (mut reader, timeout) = if let Some(timeout) = timeout {
261        let poll_timeout = PollTimeout::new(Some(timeout));
262        if let Some(reader) = try_lock_internal_event_reader_for(timeout) {
263            (reader, poll_timeout.leftover())
264        } else {
265            return Ok(false);
266        }
267    } else {
268        (lock_internal_event_reader(), None)
269    };
270    reader.poll(timeout, filter)
271}
272
273/// Reads a single `InternalEvent`.
274pub(crate) fn read_internal<F>(filter: &F) -> std::io::Result<InternalEvent>
275where
276    F: Filter,
277{
278    let mut reader = lock_internal_event_reader();
279    reader.read(filter)
280}
281
282bitflags! {
283    /// Represents special flags that tell compatible terminals to add extra information to keyboard events.
284    ///
285    /// See <https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement> for more information.
286    ///
287    /// Alternate keys and Unicode codepoints are not yet supported by crossterm.
288    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
289    #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
290    pub struct KeyboardEnhancementFlags: u8 {
291        /// Represent Escape and modified keys using CSI-u sequences, so they can be unambiguously
292        /// read.
293        const DISAMBIGUATE_ESCAPE_CODES = 0b0000_0001;
294        /// Add extra events with [`KeyEvent.kind`] set to [`KeyEventKind::Repeat`] or
295        /// [`KeyEventKind::Release`] when keys are autorepeated or released.
296        const REPORT_EVENT_TYPES = 0b0000_0010;
297        /// Send [alternate keycodes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#key-codes)
298        /// in addition to the base keycode. The alternate keycode overrides the base keycode in
299        /// resulting `KeyEvent`s.
300        const REPORT_ALTERNATE_KEYS = 0b0000_0100;
301        /// Represent all keyboard events as CSI-u sequences. This is required to get repeat/release
302        /// events for plain-text keys.
303        const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 0b0000_1000;
304        // Send the Unicode codepoint as well as the keycode.
305        //
306        // *Note*: this is not yet supported by crossterm.
307        // const REPORT_ASSOCIATED_TEXT = 0b0001_0000;
308    }
309}
310
311/// A command that enables mouse event capturing.
312///
313/// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
314#[cfg(feature = "events")]
315#[derive(Debug, Clone, Copy, PartialEq, Eq)]
316pub struct EnableMouseCapture;
317
318#[cfg(feature = "events")]
319impl Command for EnableMouseCapture {
320    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
321        f.write_str(concat!(
322            // Normal tracking: Send mouse X & Y on button press and release
323            csi!("?1000h"),
324            // Button-event tracking: Report button motion events (dragging)
325            csi!("?1002h"),
326            // Any-event tracking: Report all motion events
327            csi!("?1003h"),
328            // RXVT mouse mode: Allows mouse coordinates of >223
329            csi!("?1015h"),
330            // SGR mouse mode: Allows mouse coordinates of >223, preferred over RXVT mode
331            csi!("?1006h"),
332        ))
333    }
334
335    #[cfg(windows)]
336    fn execute_winapi(&self) -> std::io::Result<()> {
337        sys::windows::enable_mouse_capture()
338    }
339
340    #[cfg(windows)]
341    fn is_ansi_code_supported(&self) -> bool {
342        false
343    }
344}
345
346/// A command that disables mouse event capturing.
347///
348/// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
349#[derive(Debug, Clone, Copy, PartialEq, Eq)]
350pub struct DisableMouseCapture;
351
352impl Command for DisableMouseCapture {
353    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
354        f.write_str(concat!(
355            // The inverse commands of EnableMouseCapture, in reverse order.
356            csi!("?1006l"),
357            csi!("?1015l"),
358            csi!("?1003l"),
359            csi!("?1002l"),
360            csi!("?1000l"),
361        ))
362    }
363
364    #[cfg(windows)]
365    fn execute_winapi(&self) -> std::io::Result<()> {
366        sys::windows::disable_mouse_capture()
367    }
368
369    #[cfg(windows)]
370    fn is_ansi_code_supported(&self) -> bool {
371        false
372    }
373}
374
375/// A command that enables focus event emission.
376///
377/// It should be paired with [`DisableFocusChange`] at the end of execution.
378///
379/// Focus events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
380#[derive(Debug, Clone, Copy, PartialEq, Eq)]
381pub struct EnableFocusChange;
382
383impl Command for EnableFocusChange {
384    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
385        f.write_str(csi!("?1004h"))
386    }
387
388    #[cfg(windows)]
389    fn execute_winapi(&self) -> std::io::Result<()> {
390        // Focus events are always enabled on Windows
391        Ok(())
392    }
393}
394
395/// A command that disables focus event emission.
396#[derive(Debug, Clone, Copy, PartialEq, Eq)]
397pub struct DisableFocusChange;
398
399impl Command for DisableFocusChange {
400    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
401        f.write_str(csi!("?1004l"))
402    }
403
404    #[cfg(windows)]
405    fn execute_winapi(&self) -> std::io::Result<()> {
406        // Focus events can't be disabled on Windows
407        Ok(())
408    }
409}
410
411/// A command that enables [bracketed paste mode](https://en.wikipedia.org/wiki/Bracketed-paste).
412///
413/// It should be paired with [`DisableBracketedPaste`] at the end of execution.
414///
415/// This is not supported in older Windows terminals without
416/// [virtual terminal sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences).
417#[cfg(feature = "bracketed-paste")]
418#[derive(Debug, Clone, Copy, PartialEq, Eq)]
419pub struct EnableBracketedPaste;
420
421#[cfg(feature = "bracketed-paste")]
422impl Command for EnableBracketedPaste {
423    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
424        f.write_str(csi!("?2004h"))
425    }
426
427    #[cfg(windows)]
428    fn execute_winapi(&self) -> std::io::Result<()> {
429        Err(std::io::Error::new(
430            std::io::ErrorKind::Unsupported,
431            "Bracketed paste not implemented in the legacy Windows API.",
432        ))
433    }
434}
435
436/// A command that disables bracketed paste mode.
437#[cfg(feature = "bracketed-paste")]
438#[derive(Debug, Clone, Copy, PartialEq, Eq)]
439pub struct DisableBracketedPaste;
440
441#[cfg(feature = "bracketed-paste")]
442impl Command for DisableBracketedPaste {
443    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
444        f.write_str(csi!("?2004l"))
445    }
446
447    #[cfg(windows)]
448    fn execute_winapi(&self) -> std::io::Result<()> {
449        Ok(())
450    }
451}
452
453/// 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.
454///
455/// It should be paired with [`PopKeyboardEnhancementFlags`] at the end of execution.
456///
457/// Example usage:
458/// ```no_run
459/// use std::io::{Write, stdout};
460/// use crossterm::execute;
461/// use crossterm::event::{
462///     KeyboardEnhancementFlags,
463///     PushKeyboardEnhancementFlags,
464///     PopKeyboardEnhancementFlags
465/// };
466///
467/// let mut stdout = stdout();
468///
469/// execute!(
470///     stdout,
471///     PushKeyboardEnhancementFlags(
472///         KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
473///     )
474/// );
475///
476/// // ...
477///
478/// execute!(stdout, PopKeyboardEnhancementFlags);
479/// ```
480///
481/// Note that, currently, only the following support this protocol:
482/// * [kitty terminal](https://sw.kovidgoyal.net/kitty/)
483/// * [foot terminal](https://codeberg.org/dnkl/foot/issues/319)
484/// * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html)
485/// * [alacritty terminal](https://github.com/alacritty/alacritty/issues/6378)
486/// * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131)
487/// * [neovim text editor](https://github.com/neovim/neovim/pull/18181)
488/// * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103)
489/// * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138)
490#[derive(Debug, Clone, Copy, PartialEq, Eq)]
491pub struct PushKeyboardEnhancementFlags(pub KeyboardEnhancementFlags);
492
493impl Command for PushKeyboardEnhancementFlags {
494    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
495        write!(f, "{}{}u", csi!(">"), self.0.bits())
496    }
497
498    #[cfg(windows)]
499    fn execute_winapi(&self) -> std::io::Result<()> {
500        use std::io;
501
502        Err(io::Error::new(
503            io::ErrorKind::Unsupported,
504            "Keyboard progressive enhancement not implemented for the legacy Windows API.",
505        ))
506    }
507
508    #[cfg(windows)]
509    fn is_ansi_code_supported(&self) -> bool {
510        false
511    }
512}
513
514/// A command that disables extra kinds of keyboard events.
515///
516/// Specifically, it pops one level of keyboard enhancement flags.
517///
518/// See [`PushKeyboardEnhancementFlags`] and <https://sw.kovidgoyal.net/kitty/keyboard-protocol/> for more information.
519#[derive(Debug, Clone, Copy, PartialEq, Eq)]
520pub struct PopKeyboardEnhancementFlags;
521
522impl Command for PopKeyboardEnhancementFlags {
523    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
524        f.write_str(csi!("<1u"))
525    }
526
527    #[cfg(windows)]
528    fn execute_winapi(&self) -> std::io::Result<()> {
529        use std::io;
530
531        Err(io::Error::new(
532            io::ErrorKind::Unsupported,
533            "Keyboard progressive enhancement not implemented for the legacy Windows API.",
534        ))
535    }
536
537    #[cfg(windows)]
538    fn is_ansi_code_supported(&self) -> bool {
539        false
540    }
541}
542
543/// Represents an event.
544#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
545#[cfg_attr(not(feature = "bracketed-paste"), derive(Copy))]
546#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)]
547pub enum Event {
548    /// The terminal gained focus
549    FocusGained,
550    /// The terminal lost focus
551    FocusLost,
552    /// A single key event with additional pressed modifiers.
553    Key(KeyEvent),
554    /// A single mouse event with additional pressed modifiers.
555    Mouse(MouseEvent),
556    /// A string that was pasted into the terminal. Only emitted if bracketed paste has been
557    /// enabled.
558    #[cfg(feature = "bracketed-paste")]
559    Paste(String),
560    /// An resize event with new dimensions after resize (columns, rows).
561    /// **Note** that resize events can occur in batches.
562    Resize(u16, u16),
563}
564
565/// Represents a mouse event.
566///
567/// # Platform-specific Notes
568///
569/// ## Mouse Buttons
570///
571/// Some platforms/terminals do not report mouse button for the
572/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left`
573/// is returned if we don't know which button was used.
574///
575/// ## Key Modifiers
576///
577/// Some platforms/terminals does not report all key modifiers
578/// combinations for all mouse event types. For example - macOS reports
579/// `Ctrl` + left mouse button click as a right mouse button click.
580#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
581#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
582pub struct MouseEvent {
583    /// The kind of mouse event that was caused.
584    pub kind: MouseEventKind,
585    /// The column that the event occurred on.
586    pub column: u16,
587    /// The row that the event occurred on.
588    pub row: u16,
589    /// The key modifiers active when the event occurred.
590    pub modifiers: KeyModifiers,
591}
592
593/// A mouse event kind.
594///
595/// # Platform-specific Notes
596///
597/// ## Mouse Buttons
598///
599/// Some platforms/terminals do not report mouse button for the
600/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left`
601/// is returned if we don't know which button was used.
602#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
603#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
604pub enum MouseEventKind {
605    /// Pressed mouse button. Contains the button that was pressed.
606    Down(MouseButton),
607    /// Released mouse button. Contains the button that was released.
608    Up(MouseButton),
609    /// Moved the mouse cursor while pressing the contained mouse button.
610    Drag(MouseButton),
611    /// Moved the mouse cursor while not pressing a mouse button.
612    Moved,
613    /// Scrolled mouse wheel downwards (towards the user).
614    ScrollDown,
615    /// Scrolled mouse wheel upwards (away from the user).
616    ScrollUp,
617    /// Scrolled mouse wheel left (mostly on a laptop touchpad).
618    ScrollLeft,
619    /// Scrolled mouse wheel right (mostly on a laptop touchpad).
620    ScrollRight,
621}
622
623/// Represents a mouse button.
624#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
625#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
626pub enum MouseButton {
627    /// Left mouse button.
628    Left,
629    /// Right mouse button.
630    Right,
631    /// Middle mouse button.
632    Middle,
633}
634
635bitflags! {
636    /// Represents key modifiers (shift, control, alt, etc.).
637    ///
638    /// **Note:** `SUPER`, `HYPER`, and `META` can only be read if
639    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
640    /// [`PushKeyboardEnhancementFlags`].
641    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
642    #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
643    pub struct KeyModifiers: u8 {
644        const SHIFT = 0b0000_0001;
645        const CONTROL = 0b0000_0010;
646        const ALT = 0b0000_0100;
647        const SUPER = 0b0000_1000;
648        const HYPER = 0b0001_0000;
649        const META = 0b0010_0000;
650        const NONE = 0b0000_0000;
651    }
652}
653
654impl Display for KeyModifiers {
655    /// Formats the key modifiers using the given formatter.
656    ///
657    /// The key modifiers are joined by a `+` character.
658    ///
659    /// # Platform-specific Notes
660    ///
661    /// On macOS, the control, alt, and super keys is displayed as "Control", "Option", and
662    /// "Command" respectively. See
663    /// <https://support.apple.com/guide/applestyleguide/welcome/1.0/web>.
664    ///
665    /// On Windows, the super key is displayed as "Windows" and the control key is displayed as
666    /// "Ctrl". See
667    /// <https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/keys-keyboard-shortcuts>.
668    ///
669    /// On other platforms, the super key is referred to as "Super" and the control key is
670    /// displayed as "Ctrl".
671    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672        let mut first = true;
673        for modifier in self.iter() {
674            if !first {
675                f.write_str("+")?;
676                first = false;
677            }
678            match modifier {
679                KeyModifiers::SHIFT => f.write_str("Shift")?,
680                #[cfg(unix)]
681                KeyModifiers::CONTROL => f.write_str("Control")?,
682                #[cfg(windows)]
683                KeyModifiers::CONTROL => f.write_str("Ctrl")?,
684                #[cfg(target_os = "macos")]
685                KeyModifiers::ALT => f.write_str("Option")?,
686                #[cfg(not(target_os = "macos"))]
687                KeyModifiers::ALT => f.write_str("Alt")?,
688                #[cfg(target_os = "macos")]
689                KeyModifiers::SUPER => f.write_str("Command")?,
690                #[cfg(target_os = "windows")]
691                KeyModifiers::SUPER => f.write_str("Windows")?,
692                #[cfg(not(any(target_os = "macos", target_os = "windows")))]
693                KeyModifiers::SUPER => f.write_str("Super")?,
694                KeyModifiers::HYPER => f.write_str("Hyper")?,
695                KeyModifiers::META => f.write_str("Meta")?,
696                _ => unreachable!(),
697            }
698        }
699        Ok(())
700    }
701}
702
703/// Represents a keyboard event kind.
704#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
705#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
706pub enum KeyEventKind {
707    Press,
708    Repeat,
709    Release,
710}
711
712bitflags! {
713    /// Represents extra state about the key event.
714    ///
715    /// **Note:** This state can only be read if
716    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
717    /// [`PushKeyboardEnhancementFlags`].
718    #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
719    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
720    pub struct KeyEventState: u8 {
721        /// The key event origins from the keypad.
722        const KEYPAD = 0b0000_0001;
723        /// Caps Lock was enabled for this key event.
724        ///
725        /// **Note:** this is set for the initial press of Caps Lock itself.
726        const CAPS_LOCK = 0b0000_0010;
727        /// Num Lock was enabled for this key event.
728        ///
729        /// **Note:** this is set for the initial press of Num Lock itself.
730        const NUM_LOCK = 0b0000_0100;
731        const NONE = 0b0000_0000;
732    }
733}
734
735/// Represents a key event.
736#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
737#[derive(Debug, PartialOrd, Clone, Copy)]
738pub struct KeyEvent {
739    /// The key itself.
740    pub code: KeyCode,
741    /// Additional key modifiers.
742    pub modifiers: KeyModifiers,
743    /// Kind of event.
744    ///
745    /// Only set if:
746    /// - Unix: [`KeyboardEnhancementFlags::REPORT_EVENT_TYPES`] has been enabled with [`PushKeyboardEnhancementFlags`].
747    /// - Windows: always
748    pub kind: KeyEventKind,
749    /// Keyboard state.
750    ///
751    /// Only set if [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
752    /// [`PushKeyboardEnhancementFlags`].
753    pub state: KeyEventState,
754}
755
756impl KeyEvent {
757    pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent {
758        KeyEvent {
759            code,
760            modifiers,
761            kind: KeyEventKind::Press,
762            state: KeyEventState::empty(),
763        }
764    }
765
766    pub const fn new_with_kind(
767        code: KeyCode,
768        modifiers: KeyModifiers,
769        kind: KeyEventKind,
770    ) -> KeyEvent {
771        KeyEvent {
772            code,
773            modifiers,
774            kind,
775            state: KeyEventState::empty(),
776        }
777    }
778
779    pub const fn new_with_kind_and_state(
780        code: KeyCode,
781        modifiers: KeyModifiers,
782        kind: KeyEventKind,
783        state: KeyEventState,
784    ) -> KeyEvent {
785        KeyEvent {
786            code,
787            modifiers,
788            kind,
789            state,
790        }
791    }
792
793    // modifies the KeyEvent,
794    // so that KeyModifiers::SHIFT is present iff
795    // an uppercase char is present.
796    fn normalize_case(mut self) -> KeyEvent {
797        let c = match self.code {
798            KeyCode::Char(c) => c,
799            _ => return self,
800        };
801
802        if c.is_ascii_uppercase() {
803            self.modifiers.insert(KeyModifiers::SHIFT);
804        } else if self.modifiers.contains(KeyModifiers::SHIFT) {
805            self.code = KeyCode::Char(c.to_ascii_uppercase())
806        }
807        self
808    }
809}
810
811impl From<KeyCode> for KeyEvent {
812    fn from(code: KeyCode) -> Self {
813        KeyEvent {
814            code,
815            modifiers: KeyModifiers::empty(),
816            kind: KeyEventKind::Press,
817            state: KeyEventState::empty(),
818        }
819    }
820}
821
822impl PartialEq for KeyEvent {
823    fn eq(&self, other: &KeyEvent) -> bool {
824        let KeyEvent {
825            code: lhs_code,
826            modifiers: lhs_modifiers,
827            kind: lhs_kind,
828            state: lhs_state,
829        } = self.normalize_case();
830        let KeyEvent {
831            code: rhs_code,
832            modifiers: rhs_modifiers,
833            kind: rhs_kind,
834            state: rhs_state,
835        } = other.normalize_case();
836        (lhs_code == rhs_code)
837            && (lhs_modifiers == rhs_modifiers)
838            && (lhs_kind == rhs_kind)
839            && (lhs_state == rhs_state)
840    }
841}
842
843impl Eq for KeyEvent {}
844
845impl Hash for KeyEvent {
846    fn hash<H: Hasher>(&self, hash_state: &mut H) {
847        let KeyEvent {
848            code,
849            modifiers,
850            kind,
851            state,
852        } = self.normalize_case();
853        code.hash(hash_state);
854        modifiers.hash(hash_state);
855        kind.hash(hash_state);
856        state.hash(hash_state);
857    }
858}
859
860/// Represents a media key (as part of [`KeyCode::Media`]).
861#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
862#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
863pub enum MediaKeyCode {
864    /// Play media key.
865    Play,
866    /// Pause media key.
867    Pause,
868    /// Play/Pause media key.
869    PlayPause,
870    /// Reverse media key.
871    Reverse,
872    /// Stop media key.
873    Stop,
874    /// Fast-forward media key.
875    FastForward,
876    /// Rewind media key.
877    Rewind,
878    /// Next-track media key.
879    TrackNext,
880    /// Previous-track media key.
881    TrackPrevious,
882    /// Record media key.
883    Record,
884    /// Lower-volume media key.
885    LowerVolume,
886    /// Raise-volume media key.
887    RaiseVolume,
888    /// Mute media key.
889    MuteVolume,
890}
891
892impl Display for MediaKeyCode {
893    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
894        match self {
895            MediaKeyCode::Play => write!(f, "Play"),
896            MediaKeyCode::Pause => write!(f, "Pause"),
897            MediaKeyCode::PlayPause => write!(f, "Play/Pause"),
898            MediaKeyCode::Reverse => write!(f, "Reverse"),
899            MediaKeyCode::Stop => write!(f, "Stop"),
900            MediaKeyCode::FastForward => write!(f, "Fast Forward"),
901            MediaKeyCode::Rewind => write!(f, "Rewind"),
902            MediaKeyCode::TrackNext => write!(f, "Next Track"),
903            MediaKeyCode::TrackPrevious => write!(f, "Previous Track"),
904            MediaKeyCode::Record => write!(f, "Record"),
905            MediaKeyCode::LowerVolume => write!(f, "Lower Volume"),
906            MediaKeyCode::RaiseVolume => write!(f, "Raise Volume"),
907            MediaKeyCode::MuteVolume => write!(f, "Mute Volume"),
908        }
909    }
910}
911
912/// Represents a modifier key (as part of [`KeyCode::Modifier`]).
913#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
914#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
915pub enum ModifierKeyCode {
916    /// Left Shift key.
917    LeftShift,
918    /// Left Control key. (Control on macOS, Ctrl on other platforms)
919    LeftControl,
920    /// Left Alt key. (Option on macOS, Alt on other platforms)
921    LeftAlt,
922    /// Left Super key. (Command on macOS, Windows on Windows, Super on other platforms)
923    LeftSuper,
924    /// Left Hyper key.
925    LeftHyper,
926    /// Left Meta key.
927    LeftMeta,
928    /// Right Shift key.
929    RightShift,
930    /// Right Control key. (Control on macOS, Ctrl on other platforms)
931    RightControl,
932    /// Right Alt key. (Option on macOS, Alt on other platforms)
933    RightAlt,
934    /// Right Super key. (Command on macOS, Windows on Windows, Super on other platforms)
935    RightSuper,
936    /// Right Hyper key.
937    RightHyper,
938    /// Right Meta key.
939    RightMeta,
940    /// Iso Level3 Shift key.
941    IsoLevel3Shift,
942    /// Iso Level5 Shift key.
943    IsoLevel5Shift,
944}
945
946impl Display for ModifierKeyCode {
947    /// Formats the modifier key using the given formatter.
948    ///
949    /// # Platform-specific Notes
950    ///
951    /// On macOS, the control, alt, and super keys are displayed as "Control", "Option", and
952    /// "Command" respectively. See
953    /// <https://support.apple.com/guide/applestyleguide/welcome/1.0/web>.
954    ///
955    /// On Windows, the super key is displayed as "Windows" and the control key is displayed as
956    /// "Ctrl". See
957    /// <https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/keys-keyboard-shortcuts>.
958    ///
959    /// On other platforms, the super key is referred to as "Super".
960    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
961        match self {
962            ModifierKeyCode::LeftShift => write!(f, "Left Shift"),
963            ModifierKeyCode::LeftHyper => write!(f, "Left Hyper"),
964            ModifierKeyCode::LeftMeta => write!(f, "Left Meta"),
965            ModifierKeyCode::RightShift => write!(f, "Right Shift"),
966            ModifierKeyCode::RightHyper => write!(f, "Right Hyper"),
967            ModifierKeyCode::RightMeta => write!(f, "Right Meta"),
968            ModifierKeyCode::IsoLevel3Shift => write!(f, "Iso Level 3 Shift"),
969            ModifierKeyCode::IsoLevel5Shift => write!(f, "Iso Level 5 Shift"),
970
971            #[cfg(target_os = "macos")]
972            ModifierKeyCode::LeftControl => write!(f, "Left Control"),
973            #[cfg(not(target_os = "macos"))]
974            ModifierKeyCode::LeftControl => write!(f, "Left Ctrl"),
975
976            #[cfg(target_os = "macos")]
977            ModifierKeyCode::LeftAlt => write!(f, "Left Option"),
978            #[cfg(not(target_os = "macos"))]
979            ModifierKeyCode::LeftAlt => write!(f, "Left Alt"),
980
981            #[cfg(target_os = "macos")]
982            ModifierKeyCode::LeftSuper => write!(f, "Left Command"),
983            #[cfg(target_os = "windows")]
984            ModifierKeyCode::LeftSuper => write!(f, "Left Windows"),
985            #[cfg(not(any(target_os = "macos", target_os = "windows")))]
986            ModifierKeyCode::LeftSuper => write!(f, "Left Super"),
987
988            #[cfg(target_os = "macos")]
989            ModifierKeyCode::RightControl => write!(f, "Right Control"),
990            #[cfg(not(target_os = "macos"))]
991            ModifierKeyCode::RightControl => write!(f, "Right Ctrl"),
992
993            #[cfg(target_os = "macos")]
994            ModifierKeyCode::RightAlt => write!(f, "Right Option"),
995            #[cfg(not(target_os = "macos"))]
996            ModifierKeyCode::RightAlt => write!(f, "Right Alt"),
997
998            #[cfg(target_os = "macos")]
999            ModifierKeyCode::RightSuper => write!(f, "Right Command"),
1000            #[cfg(target_os = "windows")]
1001            ModifierKeyCode::RightSuper => write!(f, "Right Windows"),
1002            #[cfg(not(any(target_os = "macos", target_os = "windows")))]
1003            ModifierKeyCode::RightSuper => write!(f, "Right Super"),
1004        }
1005    }
1006}
1007
1008/// Represents a key.
1009#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
1010#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1011pub enum KeyCode {
1012    /// Backspace key (Delete on macOS, Backspace on other platforms).
1013    Backspace,
1014    /// Enter key.
1015    Enter,
1016    /// Left arrow key.
1017    Left,
1018    /// Right arrow key.
1019    Right,
1020    /// Up arrow key.
1021    Up,
1022    /// Down arrow key.
1023    Down,
1024    /// Home key.
1025    Home,
1026    /// End key.
1027    End,
1028    /// Page up key.
1029    PageUp,
1030    /// Page down key.
1031    PageDown,
1032    /// Tab key.
1033    Tab,
1034    /// Shift + Tab key.
1035    BackTab,
1036    /// Delete key. (Fn+Delete on macOS, Delete on other platforms)
1037    Delete,
1038    /// Insert key.
1039    Insert,
1040    /// F key.
1041    ///
1042    /// `KeyCode::F(1)` represents F1 key, etc.
1043    F(u8),
1044    /// A character.
1045    ///
1046    /// `KeyCode::Char('c')` represents `c` character, etc.
1047    Char(char),
1048    /// Null.
1049    Null,
1050    /// Escape key.
1051    Esc,
1052    /// Caps Lock key.
1053    ///
1054    /// **Note:** this key can only be read if
1055    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
1056    /// [`PushKeyboardEnhancementFlags`].
1057    CapsLock,
1058    /// Scroll Lock key.
1059    ///
1060    /// **Note:** this key can only be read if
1061    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
1062    /// [`PushKeyboardEnhancementFlags`].
1063    ScrollLock,
1064    /// Num Lock key.
1065    ///
1066    /// **Note:** this key can only be read if
1067    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
1068    /// [`PushKeyboardEnhancementFlags`].
1069    NumLock,
1070    /// Print Screen key.
1071    ///
1072    /// **Note:** this key can only be read if
1073    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
1074    /// [`PushKeyboardEnhancementFlags`].
1075    PrintScreen,
1076    /// Pause key.
1077    ///
1078    /// **Note:** this key can only be read if
1079    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
1080    /// [`PushKeyboardEnhancementFlags`].
1081    Pause,
1082    /// Menu key.
1083    ///
1084    /// **Note:** this key can only be read if
1085    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
1086    /// [`PushKeyboardEnhancementFlags`].
1087    Menu,
1088    /// The "Begin" key (often mapped to the 5 key when Num Lock is turned on).
1089    ///
1090    /// **Note:** this key can only be read if
1091    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
1092    /// [`PushKeyboardEnhancementFlags`].
1093    KeypadBegin,
1094    /// A media key.
1095    ///
1096    /// **Note:** these keys can only be read if
1097    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
1098    /// [`PushKeyboardEnhancementFlags`].
1099    Media(MediaKeyCode),
1100    /// A modifier key.
1101    ///
1102    /// **Note:** these keys can only be read if **both**
1103    /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] and
1104    /// [`KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES`] have been enabled with
1105    /// [`PushKeyboardEnhancementFlags`].
1106    Modifier(ModifierKeyCode),
1107}
1108
1109impl Display for KeyCode {
1110    /// Formats the `KeyCode` using the given formatter.
1111    ///
1112    /// # Platform-specific Notes
1113    ///
1114    /// On macOS, the Backspace key is displayed as "Delete", the Delete key is displayed as "Fwd
1115    /// Del", and the Enter key is displayed as "Return". See
1116    /// <https://support.apple.com/guide/applestyleguide/welcome/1.0/web>.
1117    ///
1118    /// On other platforms, the Backspace key is displayed as "Backspace", the Delete key is
1119    /// displayed as "Del", and the Enter key is displayed as "Enter".
1120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1121        match self {
1122            // On macOS, the Backspace key is called "Delete" and the Delete key is called "Fwd Del".
1123            #[cfg(target_os = "macos")]
1124            KeyCode::Backspace => write!(f, "Delete"),
1125            #[cfg(target_os = "macos")]
1126            KeyCode::Delete => write!(f, "Fwd Del"),
1127
1128            #[cfg(not(target_os = "macos"))]
1129            KeyCode::Backspace => write!(f, "Backspace"),
1130            #[cfg(not(target_os = "macos"))]
1131            KeyCode::Delete => write!(f, "Del"),
1132
1133            #[cfg(target_os = "macos")]
1134            KeyCode::Enter => write!(f, "Return"),
1135            #[cfg(not(target_os = "macos"))]
1136            KeyCode::Enter => write!(f, "Enter"),
1137            KeyCode::Left => write!(f, "Left"),
1138            KeyCode::Right => write!(f, "Right"),
1139            KeyCode::Up => write!(f, "Up"),
1140            KeyCode::Down => write!(f, "Down"),
1141            KeyCode::Home => write!(f, "Home"),
1142            KeyCode::End => write!(f, "End"),
1143            KeyCode::PageUp => write!(f, "Page Up"),
1144            KeyCode::PageDown => write!(f, "Page Down"),
1145            KeyCode::Tab => write!(f, "Tab"),
1146            KeyCode::BackTab => write!(f, "Back Tab"),
1147            KeyCode::Insert => write!(f, "Insert"),
1148            KeyCode::F(n) => write!(f, "F{}", n),
1149            KeyCode::Char(c) => match c {
1150                // special case for non-visible characters
1151                ' ' => write!(f, "Space"),
1152                c => write!(f, "{}", c),
1153            },
1154            KeyCode::Null => write!(f, "Null"),
1155            KeyCode::Esc => write!(f, "Esc"),
1156            KeyCode::CapsLock => write!(f, "Caps Lock"),
1157            KeyCode::ScrollLock => write!(f, "Scroll Lock"),
1158            KeyCode::NumLock => write!(f, "Num Lock"),
1159            KeyCode::PrintScreen => write!(f, "Print Screen"),
1160            KeyCode::Pause => write!(f, "Pause"),
1161            KeyCode::Menu => write!(f, "Menu"),
1162            KeyCode::KeypadBegin => write!(f, "Begin"),
1163            KeyCode::Media(media) => write!(f, "{}", media),
1164            KeyCode::Modifier(modifier) => write!(f, "{}", modifier),
1165        }
1166    }
1167}
1168
1169/// An internal event.
1170///
1171/// Encapsulates publicly available `Event` with additional internal
1172/// events that shouldn't be publicly available to the crate users.
1173#[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Eq)]
1174pub(crate) enum InternalEvent {
1175    /// An event.
1176    Event(Event),
1177    /// A cursor position (`col`, `row`).
1178    #[cfg(unix)]
1179    CursorPosition(u16, u16),
1180    /// The progressive keyboard enhancement flags enabled by the terminal.
1181    #[cfg(unix)]
1182    KeyboardEnhancementFlags(KeyboardEnhancementFlags),
1183    /// Attributes and architectural class of the terminal.
1184    #[cfg(unix)]
1185    PrimaryDeviceAttributes,
1186}
1187
1188#[cfg(test)]
1189mod tests {
1190    use std::collections::hash_map::DefaultHasher;
1191    use std::hash::{Hash, Hasher};
1192
1193    use super::*;
1194    use KeyCode::*;
1195    use MediaKeyCode::*;
1196    use ModifierKeyCode::*;
1197
1198    #[test]
1199    fn test_equality() {
1200        let lowercase_d_with_shift = KeyEvent::new(KeyCode::Char('d'), KeyModifiers::SHIFT);
1201        let uppercase_d_with_shift = KeyEvent::new(KeyCode::Char('D'), KeyModifiers::SHIFT);
1202        let uppercase_d = KeyEvent::new(KeyCode::Char('D'), KeyModifiers::NONE);
1203        assert_eq!(lowercase_d_with_shift, uppercase_d_with_shift);
1204        assert_eq!(uppercase_d, uppercase_d_with_shift);
1205    }
1206
1207    #[test]
1208    fn test_hash() {
1209        let lowercase_d_with_shift_hash = {
1210            let mut hasher = DefaultHasher::new();
1211            KeyEvent::new(KeyCode::Char('d'), KeyModifiers::SHIFT).hash(&mut hasher);
1212            hasher.finish()
1213        };
1214        let uppercase_d_with_shift_hash = {
1215            let mut hasher = DefaultHasher::new();
1216            KeyEvent::new(KeyCode::Char('D'), KeyModifiers::SHIFT).hash(&mut hasher);
1217            hasher.finish()
1218        };
1219        let uppercase_d_hash = {
1220            let mut hasher = DefaultHasher::new();
1221            KeyEvent::new(KeyCode::Char('D'), KeyModifiers::NONE).hash(&mut hasher);
1222            hasher.finish()
1223        };
1224        assert_eq!(lowercase_d_with_shift_hash, uppercase_d_with_shift_hash);
1225        assert_eq!(uppercase_d_hash, uppercase_d_with_shift_hash);
1226    }
1227
1228    #[test]
1229    fn keycode_display() {
1230        #[cfg(target_os = "macos")]
1231        {
1232            assert_eq!(format!("{}", Backspace), "Delete");
1233            assert_eq!(format!("{}", Delete), "Fwd Del");
1234            assert_eq!(format!("{}", Enter), "Return");
1235        }
1236        #[cfg(not(target_os = "macos"))]
1237        {
1238            assert_eq!(format!("{}", Backspace), "Backspace");
1239            assert_eq!(format!("{}", Delete), "Del");
1240            assert_eq!(format!("{}", Enter), "Enter");
1241        }
1242        assert_eq!(format!("{}", Left), "Left");
1243        assert_eq!(format!("{}", Right), "Right");
1244        assert_eq!(format!("{}", Up), "Up");
1245        assert_eq!(format!("{}", Down), "Down");
1246        assert_eq!(format!("{}", Home), "Home");
1247        assert_eq!(format!("{}", End), "End");
1248        assert_eq!(format!("{}", PageUp), "Page Up");
1249        assert_eq!(format!("{}", PageDown), "Page Down");
1250        assert_eq!(format!("{}", Tab), "Tab");
1251        assert_eq!(format!("{}", BackTab), "Back Tab");
1252        assert_eq!(format!("{}", Insert), "Insert");
1253        assert_eq!(format!("{}", F(1)), "F1");
1254        assert_eq!(format!("{}", Char('a')), "a");
1255        assert_eq!(format!("{}", Null), "Null");
1256        assert_eq!(format!("{}", Esc), "Esc");
1257        assert_eq!(format!("{}", CapsLock), "Caps Lock");
1258        assert_eq!(format!("{}", ScrollLock), "Scroll Lock");
1259        assert_eq!(format!("{}", NumLock), "Num Lock");
1260        assert_eq!(format!("{}", PrintScreen), "Print Screen");
1261        assert_eq!(format!("{}", KeyCode::Pause), "Pause");
1262        assert_eq!(format!("{}", Menu), "Menu");
1263        assert_eq!(format!("{}", KeypadBegin), "Begin");
1264    }
1265
1266    #[test]
1267    fn media_keycode_display() {
1268        assert_eq!(format!("{}", Media(Play)), "Play");
1269        assert_eq!(format!("{}", Media(MediaKeyCode::Pause)), "Pause");
1270        assert_eq!(format!("{}", Media(PlayPause)), "Play/Pause");
1271        assert_eq!(format!("{}", Media(Reverse)), "Reverse");
1272        assert_eq!(format!("{}", Media(Stop)), "Stop");
1273        assert_eq!(format!("{}", Media(FastForward)), "Fast Forward");
1274        assert_eq!(format!("{}", Media(Rewind)), "Rewind");
1275        assert_eq!(format!("{}", Media(TrackNext)), "Next Track");
1276        assert_eq!(format!("{}", Media(TrackPrevious)), "Previous Track");
1277        assert_eq!(format!("{}", Media(Record)), "Record");
1278        assert_eq!(format!("{}", Media(LowerVolume)), "Lower Volume");
1279        assert_eq!(format!("{}", Media(RaiseVolume)), "Raise Volume");
1280        assert_eq!(format!("{}", Media(MuteVolume)), "Mute Volume");
1281    }
1282
1283    #[test]
1284    fn modifier_keycode_display() {
1285        assert_eq!(format!("{}", Modifier(LeftShift)), "Left Shift");
1286        assert_eq!(format!("{}", Modifier(LeftHyper)), "Left Hyper");
1287        assert_eq!(format!("{}", Modifier(LeftMeta)), "Left Meta");
1288        assert_eq!(format!("{}", Modifier(RightShift)), "Right Shift");
1289        assert_eq!(format!("{}", Modifier(RightHyper)), "Right Hyper");
1290        assert_eq!(format!("{}", Modifier(RightMeta)), "Right Meta");
1291        assert_eq!(format!("{}", Modifier(IsoLevel3Shift)), "Iso Level 3 Shift");
1292        assert_eq!(format!("{}", Modifier(IsoLevel5Shift)), "Iso Level 5 Shift");
1293    }
1294
1295    #[cfg(target_os = "macos")]
1296    #[test]
1297    fn modifier_keycode_display_macos() {
1298        assert_eq!(format!("{}", Modifier(LeftControl)), "Left Control");
1299        assert_eq!(format!("{}", Modifier(LeftAlt)), "Left Option");
1300        assert_eq!(format!("{}", Modifier(LeftSuper)), "Left Command");
1301        assert_eq!(format!("{}", Modifier(RightControl)), "Right Control");
1302        assert_eq!(format!("{}", Modifier(RightAlt)), "Right Option");
1303        assert_eq!(format!("{}", Modifier(RightSuper)), "Right Command");
1304    }
1305
1306    #[cfg(target_os = "windows")]
1307    #[test]
1308    fn modifier_keycode_display_windows() {
1309        assert_eq!(format!("{}", Modifier(LeftControl)), "Left Ctrl");
1310        assert_eq!(format!("{}", Modifier(LeftAlt)), "Left Alt");
1311        assert_eq!(format!("{}", Modifier(LeftSuper)), "Left Windows");
1312        assert_eq!(format!("{}", Modifier(RightControl)), "Right Ctrl");
1313        assert_eq!(format!("{}", Modifier(RightAlt)), "Right Alt");
1314        assert_eq!(format!("{}", Modifier(RightSuper)), "Right Windows");
1315    }
1316
1317    #[cfg(not(any(target_os = "macos", target_os = "windows")))]
1318    #[test]
1319    fn modifier_keycode_display_other() {
1320        assert_eq!(format!("{}", Modifier(LeftControl)), "Left Ctrl");
1321        assert_eq!(format!("{}", Modifier(LeftAlt)), "Left Alt");
1322        assert_eq!(format!("{}", Modifier(LeftSuper)), "Left Super");
1323        assert_eq!(format!("{}", Modifier(RightControl)), "Right Ctrl");
1324        assert_eq!(format!("{}", Modifier(RightAlt)), "Right Alt");
1325        assert_eq!(format!("{}", Modifier(RightSuper)), "Right Super");
1326    }
1327}