427 lines
12 KiB
Rust
427 lines
12 KiB
Rust
//! # 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),
|
|
}
|