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 |
|
121 | pub(crate) mod filter;
|
122 | pub(crate) mod read;
|
123 | pub(crate) mod source;
|
124 | #[cfg (feature = "event-stream" )]
|
125 | pub(crate) mod stream;
|
126 | pub(crate) mod sys;
|
127 | pub(crate) mod timeout;
|
128 |
|
129 | #[cfg (feature = "derive-more" )]
|
130 | use derive_more::derive::IsVariant;
|
131 | #[cfg (feature = "event-stream" )]
|
132 | pub use stream::EventStream;
|
133 |
|
134 | use crate::event::{
|
135 | filter::{EventFilter, Filter},
|
136 | read::InternalEventReader,
|
137 | timeout::PollTimeout,
|
138 | };
|
139 | use crate::{csi, Command};
|
140 | use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
|
141 | use std::fmt::{self, Display};
|
142 | use std::time::Duration;
|
143 |
|
144 | use bitflags::bitflags;
|
145 | use std::hash::{Hash, Hasher};
|
146 |
|
147 | /// Static instance of `InternalEventReader`.
|
148 | /// This needs to be static because there can be one event reader.
|
149 | static INTERNAL_EVENT_READER: Mutex<Option<InternalEventReader>> = parking_lot::const_mutex(val:None);
|
150 |
|
151 | pub(crate) fn lock_internal_event_reader() -> MappedMutexGuard<'static, InternalEventReader> {
|
152 | MutexGuard::map(s:INTERNAL_EVENT_READER.lock(), |reader: &mut Option| {
|
153 | reader.get_or_insert_with(InternalEventReader::default)
|
154 | })
|
155 | }
|
156 | fn try_lock_internal_event_reader_for(
|
157 | duration: Duration,
|
158 | ) -> Option<MappedMutexGuard<'static, InternalEventReader>> {
|
159 | Some(MutexGuard::map(
|
160 | s:INTERNAL_EVENT_READER.try_lock_for(duration)?,
|
161 | |reader: &mut Option| reader.get_or_insert_with(InternalEventReader::default),
|
162 | ))
|
163 | }
|
164 |
|
165 | /// Checks if there is an [`Event`](enum.Event.html) available.
|
166 | ///
|
167 | /// Returns `Ok(true)` if an [`Event`](enum.Event.html) is available otherwise it returns `Ok(false)`.
|
168 | ///
|
169 | /// `Ok(true)` guarantees that subsequent call to the [`read`](fn.read.html) function
|
170 | /// won't block.
|
171 | ///
|
172 | /// # Arguments
|
173 | ///
|
174 | /// * `timeout` - maximum waiting time for event availability
|
175 | ///
|
176 | /// # Examples
|
177 | ///
|
178 | /// Return immediately:
|
179 | ///
|
180 | /// ```no_run
|
181 | /// use std::{time::Duration, io};
|
182 | /// use crossterm::{event::poll};
|
183 | ///
|
184 | /// fn is_event_available() -> io::Result<bool> {
|
185 | /// // Zero duration says that the `poll` function must return immediately
|
186 | /// // with an `Event` availability information
|
187 | /// poll(Duration::from_secs(0))
|
188 | /// }
|
189 | /// ```
|
190 | ///
|
191 | /// Wait up to 100ms:
|
192 | ///
|
193 | /// ```no_run
|
194 | /// use std::{time::Duration, io};
|
195 | ///
|
196 | /// use crossterm::event::poll;
|
197 | ///
|
198 | /// fn is_event_available() -> io::Result<bool> {
|
199 | /// // Wait for an `Event` availability for 100ms. It returns immediately
|
200 | /// // if an `Event` is/becomes available.
|
201 | /// poll(Duration::from_millis(100))
|
202 | /// }
|
203 | /// ```
|
204 | pub fn poll(timeout: Duration) -> std::io::Result<bool> {
|
205 | poll_internal(timeout:Some(timeout), &EventFilter)
|
206 | }
|
207 |
|
208 | /// Reads a single [`Event`](enum.Event.html).
|
209 | ///
|
210 | /// This function blocks until an [`Event`](enum.Event.html) is available. Combine it with the
|
211 | /// [`poll`](fn.poll.html) function to get non-blocking reads.
|
212 | ///
|
213 | /// # Examples
|
214 | ///
|
215 | /// Blocking read:
|
216 | ///
|
217 | /// ```no_run
|
218 | /// use crossterm::event::read;
|
219 | /// use std::io;
|
220 | ///
|
221 | /// fn print_events() -> io::Result<bool> {
|
222 | /// loop {
|
223 | /// // Blocks until an `Event` is available
|
224 | /// println!("{:?}" , read()?);
|
225 | /// }
|
226 | /// }
|
227 | /// ```
|
228 | ///
|
229 | /// Non-blocking read:
|
230 | ///
|
231 | /// ```no_run
|
232 | /// use std::time::Duration;
|
233 | /// use std::io;
|
234 | ///
|
235 | /// use crossterm::event::{read, poll};
|
236 | ///
|
237 | /// fn print_events() -> io::Result<bool> {
|
238 | /// loop {
|
239 | /// if poll(Duration::from_millis(100))? {
|
240 | /// // It's guaranteed that `read` won't block, because `poll` returned
|
241 | /// // `Ok(true)`.
|
242 | /// println!("{:?}" , read()?);
|
243 | /// } else {
|
244 | /// // Timeout expired, no `Event` is available
|
245 | /// }
|
246 | /// }
|
247 | /// }
|
248 | /// ```
|
249 | pub fn read() -> std::io::Result<Event> {
|
250 | match read_internal(&EventFilter)? {
|
251 | InternalEvent::Event(event: Event) => Ok(event),
|
252 | #[cfg (unix)]
|
253 | _ => unreachable!(),
|
254 | }
|
255 | }
|
256 |
|
257 | /// Polls to check if there are any `InternalEvent`s that can be read within the given duration.
|
258 | pub(crate) fn poll_internal<F>(timeout: Option<Duration>, filter: &F) -> std::io::Result<bool>
|
259 | where
|
260 | F: Filter,
|
261 | {
|
262 | let (mut reader: MappedMutexGuard<'static, …, …>, timeout: Option) = if let Some(timeout: Duration) = timeout {
|
263 | let poll_timeout: PollTimeout = PollTimeout::new(timeout:Some(timeout));
|
264 | if let Some(reader: MappedMutexGuard<'static, …, …>) = try_lock_internal_event_reader_for(duration:timeout) {
|
265 | (reader, poll_timeout.leftover())
|
266 | } else {
|
267 | return Ok(false);
|
268 | }
|
269 | } else {
|
270 | (lock_internal_event_reader(), None)
|
271 | };
|
272 | reader.poll(timeout, filter)
|
273 | }
|
274 |
|
275 | /// Reads a single `InternalEvent`.
|
276 | pub(crate) fn read_internal<F>(filter: &F) -> std::io::Result<InternalEvent>
|
277 | where
|
278 | F: Filter,
|
279 | {
|
280 | let mut reader: MappedMutexGuard<'static, …, …> = lock_internal_event_reader();
|
281 | reader.read(filter)
|
282 | }
|
283 |
|
284 | bitflags! {
|
285 | /// Represents special flags that tell compatible terminals to add extra information to keyboard events.
|
286 | ///
|
287 | /// See <https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement> for more information.
|
288 | ///
|
289 | /// Alternate keys and Unicode codepoints are not yet supported by crossterm.
|
290 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize), serde(transparent))]
|
291 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
292 | pub struct KeyboardEnhancementFlags: u8 {
|
293 | /// Represent Escape and modified keys using CSI-u sequences, so they can be unambiguously
|
294 | /// read.
|
295 | const DISAMBIGUATE_ESCAPE_CODES = 0b0000_0001;
|
296 | /// Add extra events with [`KeyEvent.kind`] set to [`KeyEventKind::Repeat`] or
|
297 | /// [`KeyEventKind::Release`] when keys are autorepeated or released.
|
298 | const REPORT_EVENT_TYPES = 0b0000_0010;
|
299 | /// Send [alternate keycodes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#key-codes)
|
300 | /// in addition to the base keycode. The alternate keycode overrides the base keycode in
|
301 | /// resulting `KeyEvent`s.
|
302 | const REPORT_ALTERNATE_KEYS = 0b0000_0100;
|
303 | /// Represent all keyboard events as CSI-u sequences. This is required to get repeat/release
|
304 | /// events for plain-text keys.
|
305 | const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 0b0000_1000;
|
306 | // Send the Unicode codepoint as well as the keycode.
|
307 | //
|
308 | // *Note*: this is not yet supported by crossterm.
|
309 | // const REPORT_ASSOCIATED_TEXT = 0b0001_0000;
|
310 | }
|
311 | }
|
312 |
|
313 | /// A command that enables mouse event capturing.
|
314 | ///
|
315 | /// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
|
316 | #[cfg (feature = "events" )]
|
317 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
318 | pub struct EnableMouseCapture;
|
319 |
|
320 | #[cfg (feature = "events" )]
|
321 | impl Command for EnableMouseCapture {
|
322 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
323 | f.write_str(concat!(
|
324 | // Normal tracking: Send mouse X & Y on button press and release
|
325 | csi!("?1000h" ),
|
326 | // Button-event tracking: Report button motion events (dragging)
|
327 | csi!("?1002h" ),
|
328 | // Any-event tracking: Report all motion events
|
329 | csi!("?1003h" ),
|
330 | // RXVT mouse mode: Allows mouse coordinates of >223
|
331 | csi!("?1015h" ),
|
332 | // SGR mouse mode: Allows mouse coordinates of >223, preferred over RXVT mode
|
333 | csi!("?1006h" ),
|
334 | ))
|
335 | }
|
336 |
|
337 | #[cfg (windows)]
|
338 | fn execute_winapi(&self) -> std::io::Result<()> {
|
339 | sys::windows::enable_mouse_capture()
|
340 | }
|
341 |
|
342 | #[cfg (windows)]
|
343 | fn is_ansi_code_supported(&self) -> bool {
|
344 | false
|
345 | }
|
346 | }
|
347 |
|
348 | /// A command that disables mouse event capturing.
|
349 | ///
|
350 | /// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
|
351 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
352 | pub struct DisableMouseCapture;
|
353 |
|
354 | impl Command for DisableMouseCapture {
|
355 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
356 | f.write_str(concat!(
|
357 | // The inverse commands of EnableMouseCapture, in reverse order.
|
358 | csi!("?1006l" ),
|
359 | csi!("?1015l" ),
|
360 | csi!("?1003l" ),
|
361 | csi!("?1002l" ),
|
362 | csi!("?1000l" ),
|
363 | ))
|
364 | }
|
365 |
|
366 | #[cfg (windows)]
|
367 | fn execute_winapi(&self) -> std::io::Result<()> {
|
368 | sys::windows::disable_mouse_capture()
|
369 | }
|
370 |
|
371 | #[cfg (windows)]
|
372 | fn is_ansi_code_supported(&self) -> bool {
|
373 | false
|
374 | }
|
375 | }
|
376 |
|
377 | /// A command that enables focus event emission.
|
378 | ///
|
379 | /// It should be paired with [`DisableFocusChange`] at the end of execution.
|
380 | ///
|
381 | /// Focus events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
|
382 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
383 | pub struct EnableFocusChange;
|
384 |
|
385 | impl Command for EnableFocusChange {
|
386 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
387 | f.write_str(csi!("?1004h" ))
|
388 | }
|
389 |
|
390 | #[cfg (windows)]
|
391 | fn execute_winapi(&self) -> std::io::Result<()> {
|
392 | // Focus events are always enabled on Windows
|
393 | Ok(())
|
394 | }
|
395 | }
|
396 |
|
397 | /// A command that disables focus event emission.
|
398 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
399 | pub struct DisableFocusChange;
|
400 |
|
401 | impl Command for DisableFocusChange {
|
402 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
403 | f.write_str(csi!("?1004l" ))
|
404 | }
|
405 |
|
406 | #[cfg (windows)]
|
407 | fn execute_winapi(&self) -> std::io::Result<()> {
|
408 | // Focus events can't be disabled on Windows
|
409 | Ok(())
|
410 | }
|
411 | }
|
412 |
|
413 | /// A command that enables [bracketed paste mode](https://en.wikipedia.org/wiki/Bracketed-paste).
|
414 | ///
|
415 | /// It should be paired with [`DisableBracketedPaste`] at the end of execution.
|
416 | ///
|
417 | /// This is not supported in older Windows terminals without
|
418 | /// [virtual terminal sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences).
|
419 | #[cfg (feature = "bracketed-paste" )]
|
420 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
421 | pub struct EnableBracketedPaste;
|
422 |
|
423 | #[cfg (feature = "bracketed-paste" )]
|
424 | impl Command for EnableBracketedPaste {
|
425 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
426 | f.write_str(csi!("?2004h" ))
|
427 | }
|
428 |
|
429 | #[cfg (windows)]
|
430 | fn execute_winapi(&self) -> std::io::Result<()> {
|
431 | Err(std::io::Error::new(
|
432 | std::io::ErrorKind::Unsupported,
|
433 | "Bracketed paste not implemented in the legacy Windows API." ,
|
434 | ))
|
435 | }
|
436 | }
|
437 |
|
438 | /// A command that disables bracketed paste mode.
|
439 | #[cfg (feature = "bracketed-paste" )]
|
440 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
441 | pub struct DisableBracketedPaste;
|
442 |
|
443 | #[cfg (feature = "bracketed-paste" )]
|
444 | impl Command for DisableBracketedPaste {
|
445 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
446 | f.write_str(csi!("?2004l" ))
|
447 | }
|
448 |
|
449 | #[cfg (windows)]
|
450 | fn execute_winapi(&self) -> std::io::Result<()> {
|
451 | Ok(())
|
452 | }
|
453 | }
|
454 |
|
455 | /// 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.
|
456 | ///
|
457 | /// It should be paired with [`PopKeyboardEnhancementFlags`] at the end of execution.
|
458 | ///
|
459 | /// Example usage:
|
460 | /// ```no_run
|
461 | /// use std::io::{Write, stdout};
|
462 | /// use crossterm::execute;
|
463 | /// use crossterm::event::{
|
464 | /// KeyboardEnhancementFlags,
|
465 | /// PushKeyboardEnhancementFlags,
|
466 | /// PopKeyboardEnhancementFlags
|
467 | /// };
|
468 | ///
|
469 | /// let mut stdout = stdout();
|
470 | ///
|
471 | /// execute!(
|
472 | /// stdout,
|
473 | /// PushKeyboardEnhancementFlags(
|
474 | /// KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
|
475 | /// )
|
476 | /// );
|
477 | ///
|
478 | /// // ...
|
479 | ///
|
480 | /// execute!(stdout, PopKeyboardEnhancementFlags);
|
481 | /// ```
|
482 | ///
|
483 | /// Note that, currently, only the following support this protocol:
|
484 | /// * [kitty terminal](https://sw.kovidgoyal.net/kitty/)
|
485 | /// * [foot terminal](https://codeberg.org/dnkl/foot/issues/319)
|
486 | /// * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html)
|
487 | /// * [alacritty terminal](https://github.com/alacritty/alacritty/issues/6378)
|
488 | /// * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131)
|
489 | /// * [neovim text editor](https://github.com/neovim/neovim/pull/18181)
|
490 | /// * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103)
|
491 | /// * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138)
|
492 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
493 | pub struct PushKeyboardEnhancementFlags(pub KeyboardEnhancementFlags);
|
494 |
|
495 | impl Command for PushKeyboardEnhancementFlags {
|
496 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
497 | write!(f, " {}{}u" , csi!(">" ), self.0.bits())
|
498 | }
|
499 |
|
500 | #[cfg (windows)]
|
501 | fn execute_winapi(&self) -> std::io::Result<()> {
|
502 | use std::io;
|
503 |
|
504 | Err(io::Error::new(
|
505 | io::ErrorKind::Unsupported,
|
506 | "Keyboard progressive enhancement not implemented for the legacy Windows API." ,
|
507 | ))
|
508 | }
|
509 |
|
510 | #[cfg (windows)]
|
511 | fn is_ansi_code_supported(&self) -> bool {
|
512 | false
|
513 | }
|
514 | }
|
515 |
|
516 | /// A command that disables extra kinds of keyboard events.
|
517 | ///
|
518 | /// Specifically, it pops one level of keyboard enhancement flags.
|
519 | ///
|
520 | /// See [`PushKeyboardEnhancementFlags`] and <https://sw.kovidgoyal.net/kitty/keyboard-protocol/> for more information.
|
521 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
522 | pub struct PopKeyboardEnhancementFlags;
|
523 |
|
524 | impl Command for PopKeyboardEnhancementFlags {
|
525 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
526 | f.write_str(csi!("<1u" ))
|
527 | }
|
528 |
|
529 | #[cfg (windows)]
|
530 | fn execute_winapi(&self) -> std::io::Result<()> {
|
531 | use std::io;
|
532 |
|
533 | Err(io::Error::new(
|
534 | io::ErrorKind::Unsupported,
|
535 | "Keyboard progressive enhancement not implemented for the legacy Windows API." ,
|
536 | ))
|
537 | }
|
538 |
|
539 | #[cfg (windows)]
|
540 | fn is_ansi_code_supported(&self) -> bool {
|
541 | false
|
542 | }
|
543 | }
|
544 |
|
545 | /// Represents an event.
|
546 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
547 | #[cfg_attr (feature = "derive-more" , derive(IsVariant))]
|
548 | #[cfg_attr (not(feature = "bracketed-paste" ), derive(Copy))]
|
549 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Hash)]
|
550 | pub enum Event {
|
551 | /// The terminal gained focus
|
552 | FocusGained,
|
553 | /// The terminal lost focus
|
554 | FocusLost,
|
555 | /// A single key event with additional pressed modifiers.
|
556 | Key(KeyEvent),
|
557 | /// A single mouse event with additional pressed modifiers.
|
558 | Mouse(MouseEvent),
|
559 | /// A string that was pasted into the terminal. Only emitted if bracketed paste has been
|
560 | /// enabled.
|
561 | #[cfg (feature = "bracketed-paste" )]
|
562 | Paste(String),
|
563 | /// An resize event with new dimensions after resize (columns, rows).
|
564 | /// **Note** that resize events can occur in batches.
|
565 | Resize(u16, u16),
|
566 | }
|
567 |
|
568 | impl Event {
|
569 | /// Returns `true` if the event is a key press event.
|
570 | ///
|
571 | /// This is useful for waiting for any key press event, regardless of the key that was pressed.
|
572 | ///
|
573 | /// Returns `false` for key release and repeat events (as well as for non-key events).
|
574 | ///
|
575 | /// # Examples
|
576 | ///
|
577 | /// The following code runs a loop that processes events until a key press event is encountered:
|
578 | ///
|
579 | /// ```no_run
|
580 | /// use crossterm::event;
|
581 | ///
|
582 | /// while !event::read()?.is_key_press() {
|
583 | /// // ...
|
584 | /// }
|
585 | /// # Ok::<(), std::io::Error>(())
|
586 | /// ```
|
587 | #[inline ]
|
588 | pub fn is_key_press(&self) -> bool {
|
589 | matches!(
|
590 | self,
|
591 | Event::Key(KeyEvent {
|
592 | kind: KeyEventKind::Press,
|
593 | ..
|
594 | })
|
595 | )
|
596 | }
|
597 |
|
598 | /// Returns `true` if the event is a key release event.
|
599 | #[inline ]
|
600 | pub fn is_key_release(&self) -> bool {
|
601 | matches!(
|
602 | self,
|
603 | Event::Key(KeyEvent {
|
604 | kind: KeyEventKind::Release,
|
605 | ..
|
606 | })
|
607 | )
|
608 | }
|
609 |
|
610 | /// Returns `true` if the event is a key repeat event.
|
611 | #[inline ]
|
612 | pub fn is_key_repeat(&self) -> bool {
|
613 | matches!(
|
614 | self,
|
615 | Event::Key(KeyEvent {
|
616 | kind: KeyEventKind::Repeat,
|
617 | ..
|
618 | })
|
619 | )
|
620 | }
|
621 |
|
622 | /// Returns the key event if the event is a key event, otherwise `None`.
|
623 | ///
|
624 | /// This is a convenience method that makes apps that only care about key events easier to write.
|
625 | ///
|
626 | /// # Examples
|
627 | ///
|
628 | /// The following code runs a loop that only processes key events:
|
629 | ///
|
630 | /// ```no_run
|
631 | /// use crossterm::event;
|
632 | ///
|
633 | /// while let Some(key_event) = event::read()?.as_key_event() {
|
634 | /// // ...
|
635 | /// }
|
636 | /// # std::io::Result::Ok(())
|
637 | /// ```
|
638 | #[inline ]
|
639 | pub fn as_key_event(&self) -> Option<KeyEvent> {
|
640 | match self {
|
641 | Event::Key(event) => Some(*event),
|
642 | _ => None,
|
643 | }
|
644 | }
|
645 |
|
646 | /// Returns an Option containing the KeyEvent if the event is a key press event.
|
647 | ///
|
648 | /// This is a convenience method that makes apps that only care about key press events, and not
|
649 | /// key release or repeat events (or non-key events), easier to write.
|
650 | ///
|
651 | /// Returns `None` for key release and repeat events (as well as for non-key events).
|
652 | ///
|
653 | /// # Examples
|
654 | ///
|
655 | /// The following code runs a loop that only processes key press events:
|
656 | ///
|
657 | /// ```no_run
|
658 | /// use crossterm::event;
|
659 | ///
|
660 | /// while let Ok(event) = event::read() {
|
661 | /// if let Some(key) = event.as_key_press_event() {
|
662 | /// // ...
|
663 | /// }
|
664 | /// }
|
665 | #[inline ]
|
666 | pub fn as_key_press_event(&self) -> Option<KeyEvent> {
|
667 | match self {
|
668 | Event::Key(event) if self.is_key_press() => Some(*event),
|
669 | _ => None,
|
670 | }
|
671 | }
|
672 |
|
673 | /// Returns an Option containing the `KeyEvent` if the event is a key release event.
|
674 | #[inline ]
|
675 | pub fn as_key_release_event(&self) -> Option<KeyEvent> {
|
676 | match self {
|
677 | Event::Key(event) if self.is_key_release() => Some(*event),
|
678 | _ => None,
|
679 | }
|
680 | }
|
681 |
|
682 | /// Returns an Option containing the `KeyEvent` if the event is a key repeat event.
|
683 | #[inline ]
|
684 | pub fn as_key_repeat_event(&self) -> Option<KeyEvent> {
|
685 | match self {
|
686 | Event::Key(event) if self.is_key_repeat() => Some(*event),
|
687 | _ => None,
|
688 | }
|
689 | }
|
690 |
|
691 | /// Returns the mouse event if the event is a mouse event, otherwise `None`.
|
692 | ///
|
693 | /// This is a convenience method that makes code which only cares about mouse events easier to
|
694 | /// write.
|
695 | ///
|
696 | /// # Examples
|
697 | ///
|
698 | /// ```no_run
|
699 | /// use crossterm::event;
|
700 | ///
|
701 | /// while let Some(mouse_event) = event::read()?.as_mouse_event() {
|
702 | /// // ...
|
703 | /// }
|
704 | /// # std::io::Result::Ok(())
|
705 | /// ```
|
706 | #[inline ]
|
707 | pub fn as_mouse_event(&self) -> Option<MouseEvent> {
|
708 | match self {
|
709 | Event::Mouse(event) => Some(*event),
|
710 | _ => None,
|
711 | }
|
712 | }
|
713 |
|
714 | /// Returns the pasted string if the event is a paste event, otherwise `None`.
|
715 | ///
|
716 | /// This is a convenience method that makes code which only cares about paste events easier to write.
|
717 | ///
|
718 | /// # Examples
|
719 | ///
|
720 | /// ```no_run
|
721 | /// use crossterm::event;
|
722 | ///
|
723 | /// while let Some(paste) = event::read()?.as_paste_event() {
|
724 | /// // ...
|
725 | /// }
|
726 | /// # std::io::Result::Ok(())
|
727 | /// ```
|
728 | #[cfg (feature = "bracketed-paste" )]
|
729 | #[inline ]
|
730 | pub fn as_paste_event(&self) -> Option<&str> {
|
731 | match self {
|
732 | Event::Paste(paste) => Some(paste),
|
733 | _ => None,
|
734 | }
|
735 | }
|
736 |
|
737 | /// Returns the size as a tuple if the event is a resize event, otherwise `None`.
|
738 | ///
|
739 | /// This is a convenience method that makes code which only cares about resize events easier to write.
|
740 | ///
|
741 | /// # Examples
|
742 | ///
|
743 | /// ```no_run
|
744 | /// use crossterm::event;
|
745 | ///
|
746 | /// while let Some((columns, rows)) = event::read()?.as_resize_event() {
|
747 | /// // ...
|
748 | /// }
|
749 | /// # std::io::Result::Ok(())
|
750 | /// ```
|
751 | #[inline ]
|
752 | pub fn as_resize_event(&self) -> Option<(u16, u16)> {
|
753 | match self {
|
754 | Event::Resize(columns, rows) => Some((*columns, *rows)),
|
755 | _ => None,
|
756 | }
|
757 | }
|
758 | }
|
759 |
|
760 | /// Represents a mouse event.
|
761 | ///
|
762 | /// # Platform-specific Notes
|
763 | ///
|
764 | /// ## Mouse Buttons
|
765 | ///
|
766 | /// Some platforms/terminals do not report mouse button for the
|
767 | /// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left`
|
768 | /// is returned if we don't know which button was used.
|
769 | ///
|
770 | /// ## Key Modifiers
|
771 | ///
|
772 | /// Some platforms/terminals does not report all key modifiers
|
773 | /// combinations for all mouse event types. For example - macOS reports
|
774 | /// `Ctrl` + left mouse button click as a right mouse button click.
|
775 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
776 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
777 | pub struct MouseEvent {
|
778 | /// The kind of mouse event that was caused.
|
779 | pub kind: MouseEventKind,
|
780 | /// The column that the event occurred on.
|
781 | pub column: u16,
|
782 | /// The row that the event occurred on.
|
783 | pub row: u16,
|
784 | /// The key modifiers active when the event occurred.
|
785 | pub modifiers: KeyModifiers,
|
786 | }
|
787 |
|
788 | /// A mouse event kind.
|
789 | ///
|
790 | /// # Platform-specific Notes
|
791 | ///
|
792 | /// ## Mouse Buttons
|
793 | ///
|
794 | /// Some platforms/terminals do not report mouse button for the
|
795 | /// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left`
|
796 | /// is returned if we don't know which button was used.
|
797 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
798 | #[cfg_attr (feature = "derive-more" , derive(IsVariant))]
|
799 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
800 | pub enum MouseEventKind {
|
801 | /// Pressed mouse button. Contains the button that was pressed.
|
802 | Down(MouseButton),
|
803 | /// Released mouse button. Contains the button that was released.
|
804 | Up(MouseButton),
|
805 | /// Moved the mouse cursor while pressing the contained mouse button.
|
806 | Drag(MouseButton),
|
807 | /// Moved the mouse cursor while not pressing a mouse button.
|
808 | Moved,
|
809 | /// Scrolled mouse wheel downwards (towards the user).
|
810 | ScrollDown,
|
811 | /// Scrolled mouse wheel upwards (away from the user).
|
812 | ScrollUp,
|
813 | /// Scrolled mouse wheel left (mostly on a laptop touchpad).
|
814 | ScrollLeft,
|
815 | /// Scrolled mouse wheel right (mostly on a laptop touchpad).
|
816 | ScrollRight,
|
817 | }
|
818 |
|
819 | /// Represents a mouse button.
|
820 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
821 | #[cfg_attr (feature = "derive-more" , derive(IsVariant))]
|
822 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
823 | pub enum MouseButton {
|
824 | /// Left mouse button.
|
825 | Left,
|
826 | /// Right mouse button.
|
827 | Right,
|
828 | /// Middle mouse button.
|
829 | Middle,
|
830 | }
|
831 |
|
832 | bitflags! {
|
833 | /// Represents key modifiers (shift, control, alt, etc.).
|
834 | ///
|
835 | /// **Note:** `SUPER`, `HYPER`, and `META` can only be read if
|
836 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
837 | /// [`PushKeyboardEnhancementFlags`].
|
838 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize), serde(transparent))]
|
839 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
840 | pub struct KeyModifiers: u8 {
|
841 | const SHIFT = 0b0000_0001;
|
842 | const CONTROL = 0b0000_0010;
|
843 | const ALT = 0b0000_0100;
|
844 | const SUPER = 0b0000_1000;
|
845 | const HYPER = 0b0001_0000;
|
846 | const META = 0b0010_0000;
|
847 | const NONE = 0b0000_0000;
|
848 | }
|
849 | }
|
850 |
|
851 | impl Display for KeyModifiers {
|
852 | /// Formats the key modifiers using the given formatter.
|
853 | ///
|
854 | /// The key modifiers are joined by a `+` character.
|
855 | ///
|
856 | /// # Platform-specific Notes
|
857 | ///
|
858 | /// On macOS, the control, alt, and super keys is displayed as "Control", "Option", and
|
859 | /// "Command" respectively. See
|
860 | /// <https://support.apple.com/guide/applestyleguide/welcome/1.0/web>.
|
861 | ///
|
862 | /// On Windows, the super key is displayed as "Windows" and the control key is displayed as
|
863 | /// "Ctrl". See
|
864 | /// <https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/keys-keyboard-shortcuts>.
|
865 | ///
|
866 | /// On other platforms, the super key is referred to as "Super" and the control key is
|
867 | /// displayed as "Ctrl".
|
868 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
869 | let mut first = true;
|
870 | for modifier in self.iter() {
|
871 | if !first {
|
872 | f.write_str("+" )?;
|
873 | }
|
874 |
|
875 | first = false;
|
876 | match modifier {
|
877 | KeyModifiers::SHIFT => f.write_str("Shift" )?,
|
878 | #[cfg (unix)]
|
879 | KeyModifiers::CONTROL => f.write_str("Control" )?,
|
880 | #[cfg (windows)]
|
881 | KeyModifiers::CONTROL => f.write_str("Ctrl" )?,
|
882 | #[cfg (target_os = "macos" )]
|
883 | KeyModifiers::ALT => f.write_str("Option" )?,
|
884 | #[cfg (not(target_os = "macos" ))]
|
885 | KeyModifiers::ALT => f.write_str("Alt" )?,
|
886 | #[cfg (target_os = "macos" )]
|
887 | KeyModifiers::SUPER => f.write_str("Command" )?,
|
888 | #[cfg (target_os = "windows" )]
|
889 | KeyModifiers::SUPER => f.write_str("Windows" )?,
|
890 | #[cfg (not(any(target_os = "macos" , target_os = "windows" )))]
|
891 | KeyModifiers::SUPER => f.write_str("Super" )?,
|
892 | KeyModifiers::HYPER => f.write_str("Hyper" )?,
|
893 | KeyModifiers::META => f.write_str("Meta" )?,
|
894 | _ => unreachable!(),
|
895 | }
|
896 | }
|
897 | Ok(())
|
898 | }
|
899 | }
|
900 |
|
901 | /// Represents a keyboard event kind.
|
902 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
903 | #[cfg_attr (feature = "derive-more" , derive(IsVariant))]
|
904 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
905 | pub enum KeyEventKind {
|
906 | Press,
|
907 | Repeat,
|
908 | Release,
|
909 | }
|
910 |
|
911 | bitflags! {
|
912 | /// Represents extra state about the key event.
|
913 | ///
|
914 | /// **Note:** This state can only be read if
|
915 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
916 | /// [`PushKeyboardEnhancementFlags`].
|
917 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
918 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize), serde(transparent))]
|
919 | pub struct KeyEventState: u8 {
|
920 | /// The key event origins from the keypad.
|
921 | const KEYPAD = 0b0000_0001;
|
922 | /// Caps Lock was enabled for this key event.
|
923 | ///
|
924 | /// **Note:** this is set for the initial press of Caps Lock itself.
|
925 | const CAPS_LOCK = 0b0000_0010;
|
926 | /// Num Lock was enabled for this key event.
|
927 | ///
|
928 | /// **Note:** this is set for the initial press of Num Lock itself.
|
929 | const NUM_LOCK = 0b0000_0100;
|
930 | const NONE = 0b0000_0000;
|
931 | }
|
932 | }
|
933 |
|
934 | /// Represents a key event.
|
935 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
936 | #[derive (Debug, PartialOrd, Clone, Copy)]
|
937 | pub struct KeyEvent {
|
938 | /// The key itself.
|
939 | pub code: KeyCode,
|
940 | /// Additional key modifiers.
|
941 | pub modifiers: KeyModifiers,
|
942 | /// Kind of event.
|
943 | ///
|
944 | /// Only set if:
|
945 | /// - Unix: [`KeyboardEnhancementFlags::REPORT_EVENT_TYPES`] has been enabled with [`PushKeyboardEnhancementFlags`].
|
946 | /// - Windows: always
|
947 | pub kind: KeyEventKind,
|
948 | /// Keyboard state.
|
949 | ///
|
950 | /// Only set if [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
951 | /// [`PushKeyboardEnhancementFlags`].
|
952 | pub state: KeyEventState,
|
953 | }
|
954 |
|
955 | impl KeyEvent {
|
956 | pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent {
|
957 | KeyEvent {
|
958 | code,
|
959 | modifiers,
|
960 | kind: KeyEventKind::Press,
|
961 | state: KeyEventState::empty(),
|
962 | }
|
963 | }
|
964 |
|
965 | pub const fn new_with_kind(
|
966 | code: KeyCode,
|
967 | modifiers: KeyModifiers,
|
968 | kind: KeyEventKind,
|
969 | ) -> KeyEvent {
|
970 | KeyEvent {
|
971 | code,
|
972 | modifiers,
|
973 | kind,
|
974 | state: KeyEventState::empty(),
|
975 | }
|
976 | }
|
977 |
|
978 | pub const fn new_with_kind_and_state(
|
979 | code: KeyCode,
|
980 | modifiers: KeyModifiers,
|
981 | kind: KeyEventKind,
|
982 | state: KeyEventState,
|
983 | ) -> KeyEvent {
|
984 | KeyEvent {
|
985 | code,
|
986 | modifiers,
|
987 | kind,
|
988 | state,
|
989 | }
|
990 | }
|
991 |
|
992 | // modifies the KeyEvent,
|
993 | // so that KeyModifiers::SHIFT is present iff
|
994 | // an uppercase char is present.
|
995 | fn normalize_case(mut self) -> KeyEvent {
|
996 | let c = match self.code {
|
997 | KeyCode::Char(c) => c,
|
998 | _ => return self,
|
999 | };
|
1000 |
|
1001 | if c.is_ascii_uppercase() {
|
1002 | self.modifiers.insert(KeyModifiers::SHIFT);
|
1003 | } else if self.modifiers.contains(KeyModifiers::SHIFT) {
|
1004 | self.code = KeyCode::Char(c.to_ascii_uppercase())
|
1005 | }
|
1006 | self
|
1007 | }
|
1008 |
|
1009 | /// Returns whether the key event is a press event.
|
1010 | pub fn is_press(&self) -> bool {
|
1011 | matches!(self.kind, KeyEventKind::Press)
|
1012 | }
|
1013 |
|
1014 | /// Returns whether the key event is a release event.
|
1015 | pub fn is_release(&self) -> bool {
|
1016 | matches!(self.kind, KeyEventKind::Release)
|
1017 | }
|
1018 |
|
1019 | /// Returns whether the key event is a repeat event.
|
1020 | pub fn is_repeat(&self) -> bool {
|
1021 | matches!(self.kind, KeyEventKind::Repeat)
|
1022 | }
|
1023 | }
|
1024 |
|
1025 | impl From<KeyCode> for KeyEvent {
|
1026 | fn from(code: KeyCode) -> Self {
|
1027 | KeyEvent {
|
1028 | code,
|
1029 | modifiers: KeyModifiers::empty(),
|
1030 | kind: KeyEventKind::Press,
|
1031 | state: KeyEventState::empty(),
|
1032 | }
|
1033 | }
|
1034 | }
|
1035 |
|
1036 | impl PartialEq for KeyEvent {
|
1037 | fn eq(&self, other: &KeyEvent) -> bool {
|
1038 | let KeyEvent {
|
1039 | code: lhs_code: KeyCode,
|
1040 | modifiers: lhs_modifiers: KeyModifiers,
|
1041 | kind: lhs_kind: KeyEventKind,
|
1042 | state: lhs_state: KeyEventState,
|
1043 | } = self.normalize_case();
|
1044 | let KeyEvent {
|
1045 | code: rhs_code: KeyCode,
|
1046 | modifiers: rhs_modifiers: KeyModifiers,
|
1047 | kind: rhs_kind: KeyEventKind,
|
1048 | state: rhs_state: KeyEventState,
|
1049 | } = other.normalize_case();
|
1050 | (lhs_code == rhs_code)
|
1051 | && (lhs_modifiers == rhs_modifiers)
|
1052 | && (lhs_kind == rhs_kind)
|
1053 | && (lhs_state == rhs_state)
|
1054 | }
|
1055 | }
|
1056 |
|
1057 | impl Eq for KeyEvent {}
|
1058 |
|
1059 | impl Hash for KeyEvent {
|
1060 | fn hash<H: Hasher>(&self, hash_state: &mut H) {
|
1061 | let KeyEvent {
|
1062 | code: KeyCode,
|
1063 | modifiers: KeyModifiers,
|
1064 | kind: KeyEventKind,
|
1065 | state: KeyEventState,
|
1066 | } = self.normalize_case();
|
1067 | code.hash(hash_state);
|
1068 | modifiers.hash(hash_state);
|
1069 | kind.hash(hash_state);
|
1070 | state.hash(hash_state);
|
1071 | }
|
1072 | }
|
1073 |
|
1074 | /// Represents a media key (as part of [`KeyCode::Media`]).
|
1075 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
1076 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
1077 | pub enum MediaKeyCode {
|
1078 | /// Play media key.
|
1079 | Play,
|
1080 | /// Pause media key.
|
1081 | Pause,
|
1082 | /// Play/Pause media key.
|
1083 | PlayPause,
|
1084 | /// Reverse media key.
|
1085 | Reverse,
|
1086 | /// Stop media key.
|
1087 | Stop,
|
1088 | /// Fast-forward media key.
|
1089 | FastForward,
|
1090 | /// Rewind media key.
|
1091 | Rewind,
|
1092 | /// Next-track media key.
|
1093 | TrackNext,
|
1094 | /// Previous-track media key.
|
1095 | TrackPrevious,
|
1096 | /// Record media key.
|
1097 | Record,
|
1098 | /// Lower-volume media key.
|
1099 | LowerVolume,
|
1100 | /// Raise-volume media key.
|
1101 | RaiseVolume,
|
1102 | /// Mute media key.
|
1103 | MuteVolume,
|
1104 | }
|
1105 |
|
1106 | impl Display for MediaKeyCode {
|
1107 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
1108 | match self {
|
1109 | MediaKeyCode::Play => write!(f, "Play" ),
|
1110 | MediaKeyCode::Pause => write!(f, "Pause" ),
|
1111 | MediaKeyCode::PlayPause => write!(f, "Play/Pause" ),
|
1112 | MediaKeyCode::Reverse => write!(f, "Reverse" ),
|
1113 | MediaKeyCode::Stop => write!(f, "Stop" ),
|
1114 | MediaKeyCode::FastForward => write!(f, "Fast Forward" ),
|
1115 | MediaKeyCode::Rewind => write!(f, "Rewind" ),
|
1116 | MediaKeyCode::TrackNext => write!(f, "Next Track" ),
|
1117 | MediaKeyCode::TrackPrevious => write!(f, "Previous Track" ),
|
1118 | MediaKeyCode::Record => write!(f, "Record" ),
|
1119 | MediaKeyCode::LowerVolume => write!(f, "Lower Volume" ),
|
1120 | MediaKeyCode::RaiseVolume => write!(f, "Raise Volume" ),
|
1121 | MediaKeyCode::MuteVolume => write!(f, "Mute Volume" ),
|
1122 | }
|
1123 | }
|
1124 | }
|
1125 |
|
1126 | /// Represents a modifier key (as part of [`KeyCode::Modifier`]).
|
1127 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
1128 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
1129 | pub enum ModifierKeyCode {
|
1130 | /// Left Shift key.
|
1131 | LeftShift,
|
1132 | /// Left Control key. (Control on macOS, Ctrl on other platforms)
|
1133 | LeftControl,
|
1134 | /// Left Alt key. (Option on macOS, Alt on other platforms)
|
1135 | LeftAlt,
|
1136 | /// Left Super key. (Command on macOS, Windows on Windows, Super on other platforms)
|
1137 | LeftSuper,
|
1138 | /// Left Hyper key.
|
1139 | LeftHyper,
|
1140 | /// Left Meta key.
|
1141 | LeftMeta,
|
1142 | /// Right Shift key.
|
1143 | RightShift,
|
1144 | /// Right Control key. (Control on macOS, Ctrl on other platforms)
|
1145 | RightControl,
|
1146 | /// Right Alt key. (Option on macOS, Alt on other platforms)
|
1147 | RightAlt,
|
1148 | /// Right Super key. (Command on macOS, Windows on Windows, Super on other platforms)
|
1149 | RightSuper,
|
1150 | /// Right Hyper key.
|
1151 | RightHyper,
|
1152 | /// Right Meta key.
|
1153 | RightMeta,
|
1154 | /// Iso Level3 Shift key.
|
1155 | IsoLevel3Shift,
|
1156 | /// Iso Level5 Shift key.
|
1157 | IsoLevel5Shift,
|
1158 | }
|
1159 |
|
1160 | impl Display for ModifierKeyCode {
|
1161 | /// Formats the modifier key using the given formatter.
|
1162 | ///
|
1163 | /// # Platform-specific Notes
|
1164 | ///
|
1165 | /// On macOS, the control, alt, and super keys are displayed as "Control", "Option", and
|
1166 | /// "Command" respectively. See
|
1167 | /// <https://support.apple.com/guide/applestyleguide/welcome/1.0/web>.
|
1168 | ///
|
1169 | /// On Windows, the super key is displayed as "Windows" and the control key is displayed as
|
1170 | /// "Ctrl". See
|
1171 | /// <https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/keys-keyboard-shortcuts>.
|
1172 | ///
|
1173 | /// On other platforms, the super key is referred to as "Super".
|
1174 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
1175 | match self {
|
1176 | ModifierKeyCode::LeftShift => write!(f, "Left Shift" ),
|
1177 | ModifierKeyCode::LeftHyper => write!(f, "Left Hyper" ),
|
1178 | ModifierKeyCode::LeftMeta => write!(f, "Left Meta" ),
|
1179 | ModifierKeyCode::RightShift => write!(f, "Right Shift" ),
|
1180 | ModifierKeyCode::RightHyper => write!(f, "Right Hyper" ),
|
1181 | ModifierKeyCode::RightMeta => write!(f, "Right Meta" ),
|
1182 | ModifierKeyCode::IsoLevel3Shift => write!(f, "Iso Level 3 Shift" ),
|
1183 | ModifierKeyCode::IsoLevel5Shift => write!(f, "Iso Level 5 Shift" ),
|
1184 |
|
1185 | #[cfg (target_os = "macos" )]
|
1186 | ModifierKeyCode::LeftControl => write!(f, "Left Control" ),
|
1187 | #[cfg (not(target_os = "macos" ))]
|
1188 | ModifierKeyCode::LeftControl => write!(f, "Left Ctrl" ),
|
1189 |
|
1190 | #[cfg (target_os = "macos" )]
|
1191 | ModifierKeyCode::LeftAlt => write!(f, "Left Option" ),
|
1192 | #[cfg (not(target_os = "macos" ))]
|
1193 | ModifierKeyCode::LeftAlt => write!(f, "Left Alt" ),
|
1194 |
|
1195 | #[cfg (target_os = "macos" )]
|
1196 | ModifierKeyCode::LeftSuper => write!(f, "Left Command" ),
|
1197 | #[cfg (target_os = "windows" )]
|
1198 | ModifierKeyCode::LeftSuper => write!(f, "Left Windows" ),
|
1199 | #[cfg (not(any(target_os = "macos" , target_os = "windows" )))]
|
1200 | ModifierKeyCode::LeftSuper => write!(f, "Left Super" ),
|
1201 |
|
1202 | #[cfg (target_os = "macos" )]
|
1203 | ModifierKeyCode::RightControl => write!(f, "Right Control" ),
|
1204 | #[cfg (not(target_os = "macos" ))]
|
1205 | ModifierKeyCode::RightControl => write!(f, "Right Ctrl" ),
|
1206 |
|
1207 | #[cfg (target_os = "macos" )]
|
1208 | ModifierKeyCode::RightAlt => write!(f, "Right Option" ),
|
1209 | #[cfg (not(target_os = "macos" ))]
|
1210 | ModifierKeyCode::RightAlt => write!(f, "Right Alt" ),
|
1211 |
|
1212 | #[cfg (target_os = "macos" )]
|
1213 | ModifierKeyCode::RightSuper => write!(f, "Right Command" ),
|
1214 | #[cfg (target_os = "windows" )]
|
1215 | ModifierKeyCode::RightSuper => write!(f, "Right Windows" ),
|
1216 | #[cfg (not(any(target_os = "macos" , target_os = "windows" )))]
|
1217 | ModifierKeyCode::RightSuper => write!(f, "Right Super" ),
|
1218 | }
|
1219 | }
|
1220 | }
|
1221 |
|
1222 | /// Represents a key.
|
1223 | #[derive (Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
1224 | #[cfg_attr (feature = "derive-more" , derive(IsVariant))]
|
1225 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
1226 | pub enum KeyCode {
|
1227 | /// Backspace key (Delete on macOS, Backspace on other platforms).
|
1228 | Backspace,
|
1229 | /// Enter key.
|
1230 | Enter,
|
1231 | /// Left arrow key.
|
1232 | Left,
|
1233 | /// Right arrow key.
|
1234 | Right,
|
1235 | /// Up arrow key.
|
1236 | Up,
|
1237 | /// Down arrow key.
|
1238 | Down,
|
1239 | /// Home key.
|
1240 | Home,
|
1241 | /// End key.
|
1242 | End,
|
1243 | /// Page up key.
|
1244 | PageUp,
|
1245 | /// Page down key.
|
1246 | PageDown,
|
1247 | /// Tab key.
|
1248 | Tab,
|
1249 | /// Shift + Tab key.
|
1250 | BackTab,
|
1251 | /// Delete key. (Fn+Delete on macOS, Delete on other platforms)
|
1252 | Delete,
|
1253 | /// Insert key.
|
1254 | Insert,
|
1255 | /// F key.
|
1256 | ///
|
1257 | /// `KeyCode::F(1)` represents F1 key, etc.
|
1258 | #[cfg_attr (feature = "derive-more" , is_variant(ignore))]
|
1259 | F(u8),
|
1260 | /// A character.
|
1261 | ///
|
1262 | /// `KeyCode::Char('c')` represents `c` character, etc.
|
1263 | #[cfg_attr (feature = "derive-more" , is_variant(ignore))]
|
1264 | Char(char),
|
1265 | /// Null.
|
1266 | Null,
|
1267 | /// Escape key.
|
1268 | Esc,
|
1269 | /// Caps Lock key.
|
1270 | ///
|
1271 | /// **Note:** this key can only be read if
|
1272 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
1273 | /// [`PushKeyboardEnhancementFlags`].
|
1274 | CapsLock,
|
1275 | /// Scroll Lock key.
|
1276 | ///
|
1277 | /// **Note:** this key can only be read if
|
1278 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
1279 | /// [`PushKeyboardEnhancementFlags`].
|
1280 | ScrollLock,
|
1281 | /// Num Lock key.
|
1282 | ///
|
1283 | /// **Note:** this key can only be read if
|
1284 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
1285 | /// [`PushKeyboardEnhancementFlags`].
|
1286 | NumLock,
|
1287 | /// Print Screen key.
|
1288 | ///
|
1289 | /// **Note:** this key can only be read if
|
1290 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
1291 | /// [`PushKeyboardEnhancementFlags`].
|
1292 | PrintScreen,
|
1293 | /// Pause key.
|
1294 | ///
|
1295 | /// **Note:** this key can only be read if
|
1296 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
1297 | /// [`PushKeyboardEnhancementFlags`].
|
1298 | Pause,
|
1299 | /// Menu key.
|
1300 | ///
|
1301 | /// **Note:** this key can only be read if
|
1302 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
1303 | /// [`PushKeyboardEnhancementFlags`].
|
1304 | Menu,
|
1305 | /// The "Begin" key (often mapped to the 5 key when Num Lock is turned on).
|
1306 | ///
|
1307 | /// **Note:** this key can only be read if
|
1308 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
1309 | /// [`PushKeyboardEnhancementFlags`].
|
1310 | KeypadBegin,
|
1311 | /// A media key.
|
1312 | ///
|
1313 | /// **Note:** these keys can only be read if
|
1314 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
1315 | /// [`PushKeyboardEnhancementFlags`].
|
1316 | #[cfg_attr (feature = "derive-more" , is_variant(ignore))]
|
1317 | Media(MediaKeyCode),
|
1318 | /// A modifier key.
|
1319 | ///
|
1320 | /// **Note:** these keys can only be read if **both**
|
1321 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] and
|
1322 | /// [`KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES`] have been enabled with
|
1323 | /// [`PushKeyboardEnhancementFlags`].
|
1324 | #[cfg_attr (feature = "derive-more" , is_variant(ignore))]
|
1325 | Modifier(ModifierKeyCode),
|
1326 | }
|
1327 |
|
1328 | impl KeyCode {
|
1329 | /// Returns `true` if the key code is the given function key.
|
1330 | ///
|
1331 | /// # Examples
|
1332 | ///
|
1333 | /// ```
|
1334 | /// # use crossterm::event::KeyCode;
|
1335 | /// assert!(KeyCode::F(1).is_function_key(1));
|
1336 | /// assert!(!KeyCode::F(1).is_function_key(2));
|
1337 | /// ```
|
1338 | pub fn is_function_key(&self, n: u8) -> bool {
|
1339 | matches!(self, KeyCode::F(m) if *m == n)
|
1340 | }
|
1341 |
|
1342 | /// Returns `true` if the key code is the given character.
|
1343 | ///
|
1344 | /// # Examples
|
1345 | ///
|
1346 | /// ```
|
1347 | /// # use crossterm::event::KeyCode;
|
1348 | /// assert!(KeyCode::Char('a' ).is_char('a' ));
|
1349 | /// assert!(!KeyCode::Char('a' ).is_char('b' ));
|
1350 | /// assert!(!KeyCode::F(1).is_char('a' ));
|
1351 | /// ```
|
1352 | pub fn is_char(&self, c: char) -> bool {
|
1353 | matches!(self, KeyCode::Char(m) if *m == c)
|
1354 | }
|
1355 |
|
1356 | /// Returns the character if the key code is a character key.
|
1357 | ///
|
1358 | /// Returns `None` if the key code is not a character key.
|
1359 | ///
|
1360 | /// # Examples
|
1361 | ///
|
1362 | /// ```
|
1363 | /// # use crossterm::event::KeyCode;
|
1364 | /// assert_eq!(KeyCode::Char('a' ).as_char(), Some('a' ));
|
1365 | /// assert_eq!(KeyCode::F(1).as_char(), None);
|
1366 | /// ```
|
1367 | pub fn as_char(&self) -> Option<char> {
|
1368 | match self {
|
1369 | KeyCode::Char(c) => Some(*c),
|
1370 | _ => None,
|
1371 | }
|
1372 | }
|
1373 |
|
1374 | /// Returns `true` if the key code is the given media key.
|
1375 | ///
|
1376 | /// **Note:** this method requires
|
1377 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] to be enabled with
|
1378 | /// [`PushKeyboardEnhancementFlags`].
|
1379 | ///
|
1380 | /// # Examples
|
1381 | ///
|
1382 | /// ```
|
1383 | /// # use crossterm::event::{KeyCode, MediaKeyCode};
|
1384 | /// assert!(KeyCode::Media(MediaKeyCode::Play).is_media_key(MediaKeyCode::Play));
|
1385 | /// assert!(!KeyCode::Media(MediaKeyCode::Play).is_media_key(MediaKeyCode::Pause));
|
1386 | /// ```
|
1387 | pub fn is_media_key(&self, media: MediaKeyCode) -> bool {
|
1388 | matches!(self, KeyCode::Media(m) if *m == media)
|
1389 | }
|
1390 |
|
1391 | /// Returns `true` if the key code is the given modifier key.
|
1392 | ///
|
1393 | /// **Note:** this method requires both
|
1394 | /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] and
|
1395 | /// [`KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES`] to be enabled with
|
1396 | /// [`PushKeyboardEnhancementFlags`].
|
1397 | ///
|
1398 | /// # Examples
|
1399 | ///
|
1400 | /// ```
|
1401 | /// # use crossterm::event::{KeyCode, ModifierKeyCode};
|
1402 | /// assert!(KeyCode::Modifier(ModifierKeyCode::LeftShift).is_modifier(ModifierKeyCode::LeftShift));
|
1403 | /// assert!(!KeyCode::Modifier(ModifierKeyCode::LeftShift).is_modifier(ModifierKeyCode::RightShift));
|
1404 | /// ```
|
1405 | pub fn is_modifier(&self, modifier: ModifierKeyCode) -> bool {
|
1406 | matches!(self, KeyCode::Modifier(m) if *m == modifier)
|
1407 | }
|
1408 | }
|
1409 |
|
1410 | impl Display for KeyCode {
|
1411 | /// Formats the `KeyCode` using the given formatter.
|
1412 | ///
|
1413 | /// # Platform-specific Notes
|
1414 | ///
|
1415 | /// On macOS, the Backspace key is displayed as "Delete", the Delete key is displayed as "Fwd
|
1416 | /// Del", and the Enter key is displayed as "Return". See
|
1417 | /// <https://support.apple.com/guide/applestyleguide/welcome/1.0/web>.
|
1418 | ///
|
1419 | /// On other platforms, the Backspace key is displayed as "Backspace", the Delete key is
|
1420 | /// displayed as "Del", and the Enter key is displayed as "Enter".
|
1421 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
1422 | match self {
|
1423 | // On macOS, the Backspace key is called "Delete" and the Delete key is called "Fwd Del".
|
1424 | #[cfg (target_os = "macos" )]
|
1425 | KeyCode::Backspace => write!(f, "Delete" ),
|
1426 | #[cfg (target_os = "macos" )]
|
1427 | KeyCode::Delete => write!(f, "Fwd Del" ),
|
1428 |
|
1429 | #[cfg (not(target_os = "macos" ))]
|
1430 | KeyCode::Backspace => write!(f, "Backspace" ),
|
1431 | #[cfg (not(target_os = "macos" ))]
|
1432 | KeyCode::Delete => write!(f, "Del" ),
|
1433 |
|
1434 | #[cfg (target_os = "macos" )]
|
1435 | KeyCode::Enter => write!(f, "Return" ),
|
1436 | #[cfg (not(target_os = "macos" ))]
|
1437 | KeyCode::Enter => write!(f, "Enter" ),
|
1438 | KeyCode::Left => write!(f, "Left" ),
|
1439 | KeyCode::Right => write!(f, "Right" ),
|
1440 | KeyCode::Up => write!(f, "Up" ),
|
1441 | KeyCode::Down => write!(f, "Down" ),
|
1442 | KeyCode::Home => write!(f, "Home" ),
|
1443 | KeyCode::End => write!(f, "End" ),
|
1444 | KeyCode::PageUp => write!(f, "Page Up" ),
|
1445 | KeyCode::PageDown => write!(f, "Page Down" ),
|
1446 | KeyCode::Tab => write!(f, "Tab" ),
|
1447 | KeyCode::BackTab => write!(f, "Back Tab" ),
|
1448 | KeyCode::Insert => write!(f, "Insert" ),
|
1449 | KeyCode::F(n) => write!(f, "F {}" , n),
|
1450 | KeyCode::Char(c) => match c {
|
1451 | // special case for non-visible characters
|
1452 | ' ' => write!(f, "Space" ),
|
1453 | c => write!(f, " {}" , c),
|
1454 | },
|
1455 | KeyCode::Null => write!(f, "Null" ),
|
1456 | KeyCode::Esc => write!(f, "Esc" ),
|
1457 | KeyCode::CapsLock => write!(f, "Caps Lock" ),
|
1458 | KeyCode::ScrollLock => write!(f, "Scroll Lock" ),
|
1459 | KeyCode::NumLock => write!(f, "Num Lock" ),
|
1460 | KeyCode::PrintScreen => write!(f, "Print Screen" ),
|
1461 | KeyCode::Pause => write!(f, "Pause" ),
|
1462 | KeyCode::Menu => write!(f, "Menu" ),
|
1463 | KeyCode::KeypadBegin => write!(f, "Begin" ),
|
1464 | KeyCode::Media(media) => write!(f, " {}" , media),
|
1465 | KeyCode::Modifier(modifier) => write!(f, " {}" , modifier),
|
1466 | }
|
1467 | }
|
1468 | }
|
1469 |
|
1470 | /// An internal event.
|
1471 | ///
|
1472 | /// Encapsulates publicly available `Event` with additional internal
|
1473 | /// events that shouldn't be publicly available to the crate users.
|
1474 | #[derive (Debug, PartialOrd, PartialEq, Hash, Clone, Eq)]
|
1475 | pub(crate) enum InternalEvent {
|
1476 | /// An event.
|
1477 | Event(Event),
|
1478 | /// A cursor position (`col`, `row`).
|
1479 | #[cfg (unix)]
|
1480 | CursorPosition(u16, u16),
|
1481 | /// The progressive keyboard enhancement flags enabled by the terminal.
|
1482 | #[cfg (unix)]
|
1483 | KeyboardEnhancementFlags(KeyboardEnhancementFlags),
|
1484 | /// Attributes and architectural class of the terminal.
|
1485 | #[cfg (unix)]
|
1486 | PrimaryDeviceAttributes,
|
1487 | }
|
1488 |
|
1489 | #[cfg (test)]
|
1490 | mod tests {
|
1491 | use std::collections::hash_map::DefaultHasher;
|
1492 | use std::hash::{Hash, Hasher};
|
1493 |
|
1494 | use super::*;
|
1495 | use KeyCode::*;
|
1496 | use MediaKeyCode::*;
|
1497 | use ModifierKeyCode::*;
|
1498 |
|
1499 | #[test ]
|
1500 | fn test_equality() {
|
1501 | let lowercase_d_with_shift = KeyEvent::new(KeyCode::Char('d' ), KeyModifiers::SHIFT);
|
1502 | let uppercase_d_with_shift = KeyEvent::new(KeyCode::Char('D' ), KeyModifiers::SHIFT);
|
1503 | let uppercase_d = KeyEvent::new(KeyCode::Char('D' ), KeyModifiers::NONE);
|
1504 | assert_eq!(lowercase_d_with_shift, uppercase_d_with_shift);
|
1505 | assert_eq!(uppercase_d, uppercase_d_with_shift);
|
1506 | }
|
1507 |
|
1508 | #[test ]
|
1509 | fn test_hash() {
|
1510 | let lowercase_d_with_shift_hash = {
|
1511 | let mut hasher = DefaultHasher::new();
|
1512 | KeyEvent::new(KeyCode::Char('d' ), KeyModifiers::SHIFT).hash(&mut hasher);
|
1513 | hasher.finish()
|
1514 | };
|
1515 | let uppercase_d_with_shift_hash = {
|
1516 | let mut hasher = DefaultHasher::new();
|
1517 | KeyEvent::new(KeyCode::Char('D' ), KeyModifiers::SHIFT).hash(&mut hasher);
|
1518 | hasher.finish()
|
1519 | };
|
1520 | let uppercase_d_hash = {
|
1521 | let mut hasher = DefaultHasher::new();
|
1522 | KeyEvent::new(KeyCode::Char('D' ), KeyModifiers::NONE).hash(&mut hasher);
|
1523 | hasher.finish()
|
1524 | };
|
1525 | assert_eq!(lowercase_d_with_shift_hash, uppercase_d_with_shift_hash);
|
1526 | assert_eq!(uppercase_d_hash, uppercase_d_with_shift_hash);
|
1527 | }
|
1528 |
|
1529 | #[test ]
|
1530 | fn keycode_display() {
|
1531 | #[cfg (target_os = "macos" )]
|
1532 | {
|
1533 | assert_eq!(format!("{}" , Backspace), "Delete" );
|
1534 | assert_eq!(format!("{}" , Delete), "Fwd Del" );
|
1535 | assert_eq!(format!("{}" , Enter), "Return" );
|
1536 | }
|
1537 | #[cfg (not(target_os = "macos" ))]
|
1538 | {
|
1539 | assert_eq!(format!("{}" , Backspace), "Backspace" );
|
1540 | assert_eq!(format!("{}" , Delete), "Del" );
|
1541 | assert_eq!(format!("{}" , Enter), "Enter" );
|
1542 | }
|
1543 | assert_eq!(format!("{}" , Left), "Left" );
|
1544 | assert_eq!(format!("{}" , Right), "Right" );
|
1545 | assert_eq!(format!("{}" , Up), "Up" );
|
1546 | assert_eq!(format!("{}" , Down), "Down" );
|
1547 | assert_eq!(format!("{}" , Home), "Home" );
|
1548 | assert_eq!(format!("{}" , End), "End" );
|
1549 | assert_eq!(format!("{}" , PageUp), "Page Up" );
|
1550 | assert_eq!(format!("{}" , PageDown), "Page Down" );
|
1551 | assert_eq!(format!("{}" , Tab), "Tab" );
|
1552 | assert_eq!(format!("{}" , BackTab), "Back Tab" );
|
1553 | assert_eq!(format!("{}" , Insert), "Insert" );
|
1554 | assert_eq!(format!("{}" , F(1)), "F1" );
|
1555 | assert_eq!(format!("{}" , Char('a' )), "a" );
|
1556 | assert_eq!(format!("{}" , Null), "Null" );
|
1557 | assert_eq!(format!("{}" , Esc), "Esc" );
|
1558 | assert_eq!(format!("{}" , CapsLock), "Caps Lock" );
|
1559 | assert_eq!(format!("{}" , ScrollLock), "Scroll Lock" );
|
1560 | assert_eq!(format!("{}" , NumLock), "Num Lock" );
|
1561 | assert_eq!(format!("{}" , PrintScreen), "Print Screen" );
|
1562 | assert_eq!(format!("{}" , KeyCode::Pause), "Pause" );
|
1563 | assert_eq!(format!("{}" , Menu), "Menu" );
|
1564 | assert_eq!(format!("{}" , KeypadBegin), "Begin" );
|
1565 | }
|
1566 |
|
1567 | #[test ]
|
1568 | fn media_keycode_display() {
|
1569 | assert_eq!(format!("{}" , Media(Play)), "Play" );
|
1570 | assert_eq!(format!("{}" , Media(MediaKeyCode::Pause)), "Pause" );
|
1571 | assert_eq!(format!("{}" , Media(PlayPause)), "Play/Pause" );
|
1572 | assert_eq!(format!("{}" , Media(Reverse)), "Reverse" );
|
1573 | assert_eq!(format!("{}" , Media(Stop)), "Stop" );
|
1574 | assert_eq!(format!("{}" , Media(FastForward)), "Fast Forward" );
|
1575 | assert_eq!(format!("{}" , Media(Rewind)), "Rewind" );
|
1576 | assert_eq!(format!("{}" , Media(TrackNext)), "Next Track" );
|
1577 | assert_eq!(format!("{}" , Media(TrackPrevious)), "Previous Track" );
|
1578 | assert_eq!(format!("{}" , Media(Record)), "Record" );
|
1579 | assert_eq!(format!("{}" , Media(LowerVolume)), "Lower Volume" );
|
1580 | assert_eq!(format!("{}" , Media(RaiseVolume)), "Raise Volume" );
|
1581 | assert_eq!(format!("{}" , Media(MuteVolume)), "Mute Volume" );
|
1582 | }
|
1583 |
|
1584 | #[test ]
|
1585 | fn modifier_keycode_display() {
|
1586 | assert_eq!(format!("{}" , Modifier(LeftShift)), "Left Shift" );
|
1587 | assert_eq!(format!("{}" , Modifier(LeftHyper)), "Left Hyper" );
|
1588 | assert_eq!(format!("{}" , Modifier(LeftMeta)), "Left Meta" );
|
1589 | assert_eq!(format!("{}" , Modifier(RightShift)), "Right Shift" );
|
1590 | assert_eq!(format!("{}" , Modifier(RightHyper)), "Right Hyper" );
|
1591 | assert_eq!(format!("{}" , Modifier(RightMeta)), "Right Meta" );
|
1592 | assert_eq!(format!("{}" , Modifier(IsoLevel3Shift)), "Iso Level 3 Shift" );
|
1593 | assert_eq!(format!("{}" , Modifier(IsoLevel5Shift)), "Iso Level 5 Shift" );
|
1594 | }
|
1595 |
|
1596 | #[cfg (target_os = "macos" )]
|
1597 | #[test ]
|
1598 | fn modifier_keycode_display_macos() {
|
1599 | assert_eq!(format!("{}" , Modifier(LeftControl)), "Left Control" );
|
1600 | assert_eq!(format!("{}" , Modifier(LeftAlt)), "Left Option" );
|
1601 | assert_eq!(format!("{}" , Modifier(LeftSuper)), "Left Command" );
|
1602 | assert_eq!(format!("{}" , Modifier(RightControl)), "Right Control" );
|
1603 | assert_eq!(format!("{}" , Modifier(RightAlt)), "Right Option" );
|
1604 | assert_eq!(format!("{}" , Modifier(RightSuper)), "Right Command" );
|
1605 | }
|
1606 |
|
1607 | #[cfg (target_os = "windows" )]
|
1608 | #[test ]
|
1609 | fn modifier_keycode_display_windows() {
|
1610 | assert_eq!(format!("{}" , Modifier(LeftControl)), "Left Ctrl" );
|
1611 | assert_eq!(format!("{}" , Modifier(LeftAlt)), "Left Alt" );
|
1612 | assert_eq!(format!("{}" , Modifier(LeftSuper)), "Left Windows" );
|
1613 | assert_eq!(format!("{}" , Modifier(RightControl)), "Right Ctrl" );
|
1614 | assert_eq!(format!("{}" , Modifier(RightAlt)), "Right Alt" );
|
1615 | assert_eq!(format!("{}" , Modifier(RightSuper)), "Right Windows" );
|
1616 | }
|
1617 |
|
1618 | #[cfg (not(any(target_os = "macos" , target_os = "windows" )))]
|
1619 | #[test ]
|
1620 | fn modifier_keycode_display_other() {
|
1621 | assert_eq!(format!("{}" , Modifier(LeftControl)), "Left Ctrl" );
|
1622 | assert_eq!(format!("{}" , Modifier(LeftAlt)), "Left Alt" );
|
1623 | assert_eq!(format!("{}" , Modifier(LeftSuper)), "Left Super" );
|
1624 | assert_eq!(format!("{}" , Modifier(RightControl)), "Right Ctrl" );
|
1625 | assert_eq!(format!("{}" , Modifier(RightAlt)), "Right Alt" );
|
1626 | assert_eq!(format!("{}" , Modifier(RightSuper)), "Right Super" );
|
1627 | }
|
1628 |
|
1629 | #[test ]
|
1630 | fn key_modifiers_display() {
|
1631 | let modifiers = KeyModifiers::SHIFT | KeyModifiers::CONTROL | KeyModifiers::ALT;
|
1632 |
|
1633 | #[cfg (target_os = "macos" )]
|
1634 | assert_eq!(modifiers.to_string(), "Shift+Control+Option" );
|
1635 |
|
1636 | #[cfg (target_os = "windows" )]
|
1637 | assert_eq!(modifiers.to_string(), "Shift+Ctrl+Alt" );
|
1638 |
|
1639 | #[cfg (not(any(target_os = "macos" , target_os = "windows" )))]
|
1640 | assert_eq!(modifiers.to_string(), "Shift+Control+Alt" );
|
1641 | }
|
1642 |
|
1643 | const ESC_PRESSED: KeyEvent =
|
1644 | KeyEvent::new_with_kind(KeyCode::Esc, KeyModifiers::empty(), KeyEventKind::Press);
|
1645 | const ESC_RELEASED: KeyEvent =
|
1646 | KeyEvent::new_with_kind(KeyCode::Esc, KeyModifiers::empty(), KeyEventKind::Release);
|
1647 | const ESC_REPEAT: KeyEvent =
|
1648 | KeyEvent::new_with_kind(KeyCode::Esc, KeyModifiers::empty(), KeyEventKind::Repeat);
|
1649 | const MOUSE_CLICK: MouseEvent = MouseEvent {
|
1650 | kind: MouseEventKind::Down(MouseButton::Left),
|
1651 | column: 1,
|
1652 | row: 1,
|
1653 | modifiers: KeyModifiers::empty(),
|
1654 | };
|
1655 |
|
1656 | #[cfg (feature = "derive-more" )]
|
1657 | #[test ]
|
1658 | fn event_is() {
|
1659 | let event = Event::FocusGained;
|
1660 | assert!(event.is_focus_gained());
|
1661 | assert!(event.is_focus_gained());
|
1662 | assert!(!event.is_key());
|
1663 |
|
1664 | let event = Event::FocusLost;
|
1665 | assert!(event.is_focus_lost());
|
1666 | assert!(!event.is_focus_gained());
|
1667 | assert!(!event.is_key());
|
1668 |
|
1669 | let event = Event::Resize(1, 1);
|
1670 | assert!(event.is_resize());
|
1671 | assert!(!event.is_key());
|
1672 |
|
1673 | let event = Event::Key(ESC_PRESSED);
|
1674 | assert!(event.is_key());
|
1675 | assert!(event.is_key_press());
|
1676 | assert!(!event.is_key_release());
|
1677 | assert!(!event.is_key_repeat());
|
1678 | assert!(!event.is_focus_gained());
|
1679 |
|
1680 | let event = Event::Key(ESC_RELEASED);
|
1681 | assert!(event.is_key());
|
1682 | assert!(!event.is_key_press());
|
1683 | assert!(event.is_key_release());
|
1684 | assert!(!event.is_key_repeat());
|
1685 | assert!(!event.is_focus_gained());
|
1686 |
|
1687 | let event = Event::Key(ESC_REPEAT);
|
1688 | assert!(event.is_key());
|
1689 | assert!(!event.is_key_press());
|
1690 | assert!(!event.is_key_release());
|
1691 | assert!(event.is_key_repeat());
|
1692 | assert!(!event.is_focus_gained());
|
1693 |
|
1694 | let event = Event::Mouse(MOUSE_CLICK);
|
1695 | assert!(event.is_mouse());
|
1696 | assert!(!event.is_key());
|
1697 |
|
1698 | #[cfg (feature = "bracketed-paste" )]
|
1699 | {
|
1700 | let event = Event::Paste("" .to_string());
|
1701 | assert!(event.is_paste());
|
1702 | assert!(!event.is_key());
|
1703 | }
|
1704 | }
|
1705 |
|
1706 | #[test ]
|
1707 | fn event_as() {
|
1708 | let event = Event::FocusGained;
|
1709 | assert_eq!(event.as_key_event(), None);
|
1710 |
|
1711 | let event = Event::Key(ESC_PRESSED);
|
1712 | assert_eq!(event.as_key_event(), Some(ESC_PRESSED));
|
1713 | assert_eq!(event.as_key_press_event(), Some(ESC_PRESSED));
|
1714 | assert_eq!(event.as_key_release_event(), None);
|
1715 | assert_eq!(event.as_key_repeat_event(), None);
|
1716 | assert_eq!(event.as_resize_event(), None);
|
1717 |
|
1718 | let event = Event::Key(ESC_RELEASED);
|
1719 | assert_eq!(event.as_key_event(), Some(ESC_RELEASED));
|
1720 | assert_eq!(event.as_key_release_event(), Some(ESC_RELEASED));
|
1721 | assert_eq!(event.as_key_press_event(), None);
|
1722 | assert_eq!(event.as_key_repeat_event(), None);
|
1723 | assert_eq!(event.as_resize_event(), None);
|
1724 |
|
1725 | let event = Event::Key(ESC_REPEAT);
|
1726 | assert_eq!(event.as_key_event(), Some(ESC_REPEAT));
|
1727 | assert_eq!(event.as_key_repeat_event(), Some(ESC_REPEAT));
|
1728 | assert_eq!(event.as_key_press_event(), None);
|
1729 | assert_eq!(event.as_key_release_event(), None);
|
1730 | assert_eq!(event.as_resize_event(), None);
|
1731 |
|
1732 | let event = Event::Resize(1, 1);
|
1733 | assert_eq!(event.as_resize_event(), Some((1, 1)));
|
1734 | assert_eq!(event.as_key_event(), None);
|
1735 |
|
1736 | let event = Event::Mouse(MOUSE_CLICK);
|
1737 | assert_eq!(event.as_mouse_event(), Some(MOUSE_CLICK));
|
1738 | assert_eq!(event.as_key_event(), None);
|
1739 |
|
1740 | #[cfg (feature = "bracketed-paste" )]
|
1741 | {
|
1742 | let event = Event::Paste("" .to_string());
|
1743 | assert_eq!(event.as_paste_event(), Some("" ));
|
1744 | assert_eq!(event.as_key_event(), None);
|
1745 | }
|
1746 | }
|
1747 | }
|
1748 | |