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}