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