diff --git a/Cargo.toml b/Cargo.toml
index 27d6448e..20f01e39 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -122,6 +122,10 @@ required-features = ["event-stream", "events"]
name = "event-stream-tokio"
required-features = ["event-stream", "events"]
+[[example]]
+name = "event-stream-tokio-pixels"
+required-features = ["event-stream", "events"]
+
[[example]]
name = "event-read-char-line"
required-features = ["events"]
diff --git a/examples/event-stream-tokio-pixels.rs b/examples/event-stream-tokio-pixels.rs
new file mode 100644
index 00000000..7df41d90
--- /dev/null
+++ b/examples/event-stream-tokio-pixels.rs
@@ -0,0 +1,78 @@
+//! Demonstrates how to read events asynchronously with tokio.
+//!
+//! cargo run --features="event-stream" --example event-stream-tokio-pixels
+
+use std::{io::stdout, time::Duration};
+
+use futures::{future::FutureExt, select, StreamExt};
+use futures_timer::Delay;
+
+use crossterm::{
+ cursor::position,
+ event::{DisableMousePixelCapture, EnableMousePixelCapture, Event, EventStream, KeyCode},
+ execute,
+ terminal::{self, cell_size, disable_raw_mode, enable_raw_mode},
+};
+
+const HELP: &str = r#"EventStream based on futures_util::Stream with tokio
+ - Keyboard, mouse and terminal resize events enabled
+ - Prints "." every second if there's no event
+ - Hit "c" to print current cursor position
+ - Hit "s" to print current the cell size in pixels
+ - Use Esc to quit
+"#;
+
+async fn print_events() {
+ let mut reader = EventStream::new();
+
+ loop {
+ let mut delay = Delay::new(Duration::from_millis(1_000)).fuse();
+ let mut event = reader.next().fuse();
+
+ select! {
+ _ = delay => { println!(".\r"); },
+ maybe_event = event => {
+ match maybe_event {
+ Some(Ok(event)) => {
+ println!("Event::{:?}\r", event);
+
+ if event == Event::Key(KeyCode::Char('c').into()) {
+ println!("Cursor position: {:?}\r", position());
+ }
+
+ if event == Event::Key(KeyCode::Char('s').into()) {
+ println!("CSI Cell size (pixels): {:?}\r", cell_size());
+
+ let s = terminal::window_size().unwrap();
+ let width = s.width/(s.columns);
+ let height = s.height/(s.rows);
+ println!("Window Calculated Cell size (pixels): {}, {}\r", height, width);
+ }
+
+ if event == Event::Key(KeyCode::Esc.into()) {
+ break;
+ }
+ }
+ Some(Err(e)) => println!("Error: {:?}\r", e),
+ None => break,
+ }
+ }
+ };
+ }
+}
+
+#[tokio::main]
+async fn main() -> std::io::Result<()> {
+ println!("{}", HELP);
+
+ enable_raw_mode()?;
+
+ let mut stdout = stdout();
+ execute!(stdout, EnableMousePixelCapture)?;
+
+ print_events().await;
+
+ execute!(stdout, DisableMousePixelCapture)?;
+
+ disable_raw_mode()
+}
diff --git a/src/event.rs b/src/event.rs
index 4d28dc15..788c2882 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -372,6 +372,71 @@ impl Command for DisableMouseCapture {
}
}
+/// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html).
+#[cfg(feature = "events")]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct EnableMousePixelCapture;
+
+#[cfg(feature = "events")]
+impl Command for EnableMousePixelCapture {
+ fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
+ f.write_str(concat!(
+ // Normal tracking: Send mouse X & Y on button press and release
+ csi!("?1000h"),
+ // Button-event tracking: Report button motion events (dragging)
+ csi!("?1002h"),
+ // Any-event tracking: Report all motion events
+ csi!("?1003h"),
+ // RXVT mouse mode: Allows mouse coordinates of >223
+ csi!("?1015h"),
+ // SGR mouse mode: Allows mouse coordinates of >223, preferred over RXVT mode
+ csi!("?1006h"),
+ // SGR-Pixels mouse mode: Allows mouse coordinates in pixels
+ csi!("?1016h"),
+ ))
+ }
+
+ #[cfg(windows)]
+ fn execute_winapi(&self) -> std::io::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 DisableMousePixelCapture;
+
+impl Command for DisableMousePixelCapture {
+ fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
+ f.write_str(concat!(
+ // The inverse commands of EnableMouseCapture, in reverse order.
+ csi!("?1016l"),
+ csi!("?1006l"),
+ csi!("?1015l"),
+ csi!("?1003l"),
+ csi!("?1002l"),
+ csi!("?1000l"),
+ ))
+ }
+
+ #[cfg(windows)]
+ fn execute_winapi(&self) -> std::io::Result<()> {
+ sys::windows::disable_mouse_capture()
+ }
+
+ #[cfg(windows)]
+ fn is_ansi_code_supported(&self) -> bool {
+ false
+ }
+}
+
/// A command that enables focus event emission.
///
/// It should be paired with [`DisableFocusChange`] at the end of execution.
@@ -1177,6 +1242,9 @@ pub(crate) enum InternalEvent {
/// A cursor position (`col`, `row`).
#[cfg(unix)]
CursorPosition(u16, u16),
+ /// The cell size in pixels (`height`, `width`).
+ #[cfg(unix)]
+ CellSizePixels(u16, u16),
/// The progressive keyboard enhancement flags enabled by the terminal.
#[cfg(unix)]
KeyboardEnhancementFlags(KeyboardEnhancementFlags),
diff --git a/src/event/filter.rs b/src/event/filter.rs
index f78730dc..6eebf59d 100644
--- a/src/event/filter.rs
+++ b/src/event/filter.rs
@@ -17,6 +17,17 @@ impl Filter for CursorPositionFilter {
}
}
+#[cfg(unix)]
+#[derive(Debug, Clone)]
+pub(crate) struct CellPixelSizeFilter;
+
+#[cfg(unix)]
+impl Filter for CellPixelSizeFilter {
+ fn eval(&self, event: &InternalEvent) -> bool {
+ matches!(*event, InternalEvent::CellSizePixels(_, _))
+ }
+}
+
#[cfg(unix)]
#[derive(Debug, Clone)]
pub(crate) struct KeyboardEnhancementFlagsFilter;
diff --git a/src/event/sys/unix/parse.rs b/src/event/sys/unix/parse.rs
index 2019b5f2..5361cb6e 100644
--- a/src/event/sys/unix/parse.rs
+++ b/src/event/sys/unix/parse.rs
@@ -202,6 +202,7 @@ pub(crate) fn parse_csi(buffer: &[u8]) -> io::Result