| //! # Event |
| //! |
| //! The `event` module provides the functionality to read keyboard, mouse and terminal resize events. |
| //! |
| //! * The [`read`](fn.read.html) function returns an [`Event`](enum.Event.html) immediately |
| //! (if available) or blocks until an [`Event`](enum.Event.html) is available. |
| //! |
| //! * The [`poll`](fn.poll.html) function allows you to check if there is or isn't an [`Event`](enum.Event.html) available |
| //! within the given period of time. In other words - if subsequent call to the [`read`](fn.read.html) |
| //! function will block or not. |
| //! |
| //! It's **not allowed** to call these functions from different threads or combine them with the |
| //! [`EventStream`](struct.EventStream.html). You're allowed to either: |
| //! |
| //! * use the [`read`](fn.read.html) & [`poll`](fn.poll.html) functions on any, but same, thread |
| //! * or the [`EventStream`](struct.EventStream.html). |
| //! |
| //! ## Mouse Events |
| //! |
| //! Mouse events are not enabled by default. You have to enable them with the |
| //! [`EnableMouseCapture`](struct.EnableMouseCapture.html) command. See [Command API](../index.html#command-api) |
| //! for more information. |
| //! |
| //! ## Examples |
| //! |
| //! Blocking read: |
| //! |
| //! ```no_run |
| //! use crossterm::event::{read, Event}; |
| //! |
| //! fn print_events() -> crossterm::Result<()> { |
| //! loop { |
| //! // `read()` blocks until an `Event` is available |
| //! match read()? { |
| //! Event::Key(event) => println!("{:?}", event), |
| //! Event::Mouse(event) => println!("{:?}", event), |
| //! Event::Resize(width, height) => println!("New size {}x{}", width, height), |
| //! } |
| //! } |
| //! Ok(()) |
| //! } |
| //! ``` |
| //! |
| //! Non-blocking read: |
| //! |
| //! ```no_run |
| //! use std::time::Duration; |
| //! |
| //! use crossterm::event::{poll, read, Event}; |
| //! |
| //! fn print_events() -> crossterm::Result<()> { |
| //! loop { |
| //! // `poll()` waits for an `Event` for a given time period |
| //! if poll(Duration::from_millis(500))? { |
| //! // It's guaranteed that the `read()` won't block when the `poll()` |
| //! // function returns `true` |
| //! match read()? { |
| //! Event::Key(event) => println!("{:?}", event), |
| //! Event::Mouse(event) => println!("{:?}", event), |
| //! Event::Resize(width, height) => println!("New size {}x{}", width, height), |
| //! } |
| //! } else { |
| //! // Timeout expired and no `Event` is available |
| //! } |
| //! } |
| //! Ok(()) |
| //! } |
| //! ``` |
| //! |
| //! Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder for more of |
| //! them (`event-*`). |
| |
| use std::time::Duration; |
| |
| use parking_lot::RwLock; |
| #[cfg(feature = "serde")] |
| use serde::{Deserialize, Serialize}; |
| |
| use bitflags::bitflags; |
| use filter::{EventFilter, Filter}; |
| use lazy_static::lazy_static; |
| #[cfg(feature = "event-stream")] |
| pub use stream::EventStream; |
| use timeout::PollTimeout; |
| |
| use crate::{Command, Result}; |
| |
| mod ansi; |
| pub(crate) mod filter; |
| mod read; |
| mod source; |
| #[cfg(feature = "event-stream")] |
| mod stream; |
| pub(crate) mod sys; |
| mod timeout; |
| |
| lazy_static! { |
| /// Static instance of `InternalEventReader`. |
| /// This needs to be static because there can be one event reader. |
| static ref INTERNAL_EVENT_READER: RwLock<read::InternalEventReader> = RwLock::new(read::InternalEventReader::default()); |
| } |
| |
| /// Checks if there is an [`Event`](enum.Event.html) available. |
| /// |
| /// Returns `Ok(true)` if an [`Event`](enum.Event.html) is available otherwise it returns `Ok(false)`. |
| /// |
| /// `Ok(true)` guarantees that subsequent call to the [`read`](fn.read.html) function |
| /// wont block. |
| /// |
| /// # Arguments |
| /// |
| /// * `timeout` - maximum waiting time for event availability |
| /// |
| /// # Examples |
| /// |
| /// Return immediately: |
| /// |
| /// ```no_run |
| /// use std::time::Duration; |
| /// |
| /// use crossterm::{event::poll, Result}; |
| /// |
| /// fn is_event_available() -> Result<bool> { |
| /// // Zero duration says that the `poll` function must return immediately |
| /// // with an `Event` availability information |
| /// poll(Duration::from_secs(0)) |
| /// } |
| /// ``` |
| /// |
| /// Wait up to 100ms: |
| /// |
| /// ```no_run |
| /// use std::time::Duration; |
| /// |
| /// use crossterm::{event::poll, Result}; |
| /// |
| /// fn is_event_available() -> Result<bool> { |
| /// // Wait for an `Event` availability for 100ms. It returns immediately |
| /// // if an `Event` is/becomes available. |
| /// poll(Duration::from_millis(100)) |
| /// } |
| /// ``` |
| pub fn poll(timeout: Duration) -> Result<bool> { |
| poll_internal(Some(timeout), &EventFilter) |
| } |
| |
| /// Reads a single [`Event`](enum.Event.html). |
| /// |
| /// This function blocks until an [`Event`](enum.Event.html) is available. Combine it with the |
| /// [`poll`](fn.poll.html) function to get non-blocking reads. |
| /// |
| /// # Examples |
| /// |
| /// Blocking read: |
| /// |
| /// ```no_run |
| /// use crossterm::{event::read, Result}; |
| /// |
| /// fn print_events() -> Result<bool> { |
| /// loop { |
| /// // Blocks until an `Event` is available |
| /// println!("{:?}", read()?); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// Non-blocking read: |
| /// |
| /// ```no_run |
| /// use std::time::Duration; |
| /// |
| /// use crossterm::{event::{read, poll}, Result}; |
| /// |
| /// fn print_events() -> Result<bool> { |
| /// loop { |
| /// if poll(Duration::from_millis(100))? { |
| /// // It's guaranteed that `read` wont block, because `poll` returned |
| /// // `Ok(true)`. |
| /// println!("{:?}", read()?); |
| /// } else { |
| /// // Timeout expired, no `Event` is available |
| /// } |
| /// } |
| /// } |
| /// ``` |
| pub fn read() -> Result<Event> { |
| match read_internal(&EventFilter)? { |
| InternalEvent::Event(event) => Ok(event), |
| #[cfg(unix)] |
| _ => unreachable!(), |
| } |
| } |
| |
| /// Polls to check if there are any `InternalEvent`s that can be read withing the given duration. |
| pub(crate) fn poll_internal<F>(timeout: Option<Duration>, filter: &F) -> Result<bool> |
| where |
| F: Filter, |
| { |
| let (mut reader, timeout) = if let Some(timeout) = timeout { |
| let poll_timeout = PollTimeout::new(Some(timeout)); |
| if let Some(reader) = INTERNAL_EVENT_READER.try_write_for(timeout) { |
| (reader, poll_timeout.leftover()) |
| } else { |
| return Ok(false); |
| } |
| } else { |
| (INTERNAL_EVENT_READER.write(), None) |
| }; |
| reader.poll(timeout, filter) |
| } |
| |
| /// Reads a single `InternalEvent`. |
| pub(crate) fn read_internal<F>(filter: &F) -> Result<InternalEvent> |
| where |
| F: Filter, |
| { |
| let mut reader = INTERNAL_EVENT_READER.write(); |
| reader.read(filter) |
| } |
| |
| /// A command that enables mouse event capturing. |
| /// |
| /// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). |
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| pub struct EnableMouseCapture; |
| |
| impl Command for EnableMouseCapture { |
| type AnsiType = &'static str; |
| |
| fn ansi_code(&self) -> Self::AnsiType { |
| ansi::ENABLE_MOUSE_MODE_CSI_SEQUENCE |
| } |
| |
| #[cfg(windows)] |
| fn execute_winapi(&self) -> Result<()> { |
| sys::windows::enable_mouse_capture() |
| } |
| |
| #[cfg(windows)] |
| fn is_ansi_code_supported(&self) -> bool { |
| false |
| } |
| } |
| |
| /// A command that disables mouse event capturing. |
| /// |
| /// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). |
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| pub struct DisableMouseCapture; |
| |
| impl Command for DisableMouseCapture { |
| type AnsiType = &'static str; |
| |
| fn ansi_code(&self) -> Self::AnsiType { |
| ansi::DISABLE_MOUSE_MODE_CSI_SEQUENCE |
| } |
| |
| #[cfg(windows)] |
| fn execute_winapi(&self) -> Result<()> { |
| sys::windows::disable_mouse_capture() |
| } |
| |
| #[cfg(windows)] |
| fn is_ansi_code_supported(&self) -> bool { |
| false |
| } |
| } |
| |
| /// Represents an event. |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] |
| pub enum Event { |
| /// A single key event with additional pressed modifiers. |
| Key(KeyEvent), |
| /// A single mouse event with additional pressed modifiers. |
| Mouse(MouseEvent), |
| /// An resize event with new dimensions after resize (columns, rows). |
| Resize(u16, u16), |
| } |
| |
| /// Represents a mouse event. |
| /// |
| /// # Platform-specific Notes |
| /// |
| /// ## Mouse Buttons |
| /// |
| /// Some platforms/terminals do not report mouse button for the |
| /// `MouseEvent::Up` and `MouseEvent::Drag` events. `MouseButton::Left` |
| /// is returned if we don't know which button was used. |
| /// |
| /// ## Key Modifiers |
| /// |
| /// Some platforms/terminals does not report all key modifiers |
| /// combinations for all mouse event types. For example - macOS reports |
| /// `Ctrl` + left mouse button click as a right mouse button click. |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] |
| pub enum MouseEvent { |
| /// Pressed mouse button. |
| /// |
| /// Contains mouse button, pressed pointer location (column, row), and additional key modifiers. |
| Down(MouseButton, u16, u16, KeyModifiers), |
| /// Released mouse button. |
| /// |
| /// Contains mouse button, released pointer location (column, row), and additional key modifiers. |
| Up(MouseButton, u16, u16, KeyModifiers), |
| /// Moved mouse pointer while pressing a mouse button. |
| /// |
| /// Contains the pressed mouse button, released pointer location (column, row), and additional key modifiers. |
| Drag(MouseButton, u16, u16, KeyModifiers), |
| /// Scrolled mouse wheel downwards (towards the user). |
| /// |
| /// Contains the scroll location (column, row), and additional key modifiers. |
| ScrollDown(u16, u16, KeyModifiers), |
| /// Scrolled mouse wheel upwards (away from the user). |
| /// |
| /// Contains the scroll location (column, row), and additional key modifiers. |
| ScrollUp(u16, u16, KeyModifiers), |
| } |
| |
| /// Represents a mouse button. |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] |
| pub enum MouseButton { |
| /// Left mouse button. |
| Left, |
| /// Right mouse button. |
| Right, |
| /// Middle mouse button. |
| Middle, |
| } |
| |
| bitflags! { |
| /// Represents key modifiers (shift, control, alt). |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| pub struct KeyModifiers: u8 { |
| const SHIFT = 0b0000_0001; |
| const CONTROL = 0b0000_0010; |
| const ALT = 0b0000_0100; |
| const NONE = 0b0000_0000; |
| } |
| } |
| |
| /// Represents a key event. |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] |
| pub struct KeyEvent { |
| /// The key itself. |
| pub code: KeyCode, |
| /// Additional key modifiers. |
| pub modifiers: KeyModifiers, |
| } |
| |
| impl KeyEvent { |
| pub fn new(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent { |
| KeyEvent { code, modifiers } |
| } |
| } |
| |
| impl From<KeyCode> for KeyEvent { |
| fn from(code: KeyCode) -> Self { |
| KeyEvent { |
| code, |
| modifiers: KeyModifiers::empty(), |
| } |
| } |
| } |
| |
| /// Represents a key. |
| #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| pub enum KeyCode { |
| /// Backspace key. |
| Backspace, |
| /// Enter key. |
| Enter, |
| /// Left arrow key. |
| Left, |
| /// Right arrow key. |
| Right, |
| /// Up arrow key. |
| Up, |
| /// Down arrow key. |
| Down, |
| /// Home key. |
| Home, |
| /// End key. |
| End, |
| /// Page up key. |
| PageUp, |
| /// Page dow key. |
| PageDown, |
| /// Tab key. |
| Tab, |
| /// Shift + Tab key. |
| BackTab, |
| /// Delete key. |
| Delete, |
| /// Insert key. |
| Insert, |
| /// F key. |
| /// |
| /// `KeyEvent::F(1)` represents F1 key, etc. |
| F(u8), |
| /// A character. |
| /// |
| /// `KeyEvent::Char('c')` represents `c` character, etc. |
| Char(char), |
| /// Null. |
| Null, |
| /// Escape key. |
| Esc, |
| } |
| |
| /// An internal event. |
| /// |
| /// Encapsulates publicly available `Event` with additional internal |
| /// events that shouldn't be publicly available to the crate users. |
| #[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Eq)] |
| pub(crate) enum InternalEvent { |
| /// An event. |
| Event(Event), |
| /// A cursor position (`col`, `row`). |
| #[cfg(unix)] |
| CursorPosition(u16, u16), |
| } |