From d75ec2013df66acc106a4b924e6e3e1a11b0ef43 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Fri, 22 Jul 2022 17:00:41 -0400 Subject: [PATCH] Emit focus events --- examples/event-read.rs | 11 ++++++---- src/event.rs | 44 +++++++++++++++++++++++++++++++++++++ src/event/source/windows.rs | 4 ++++ src/event/sys/unix/parse.rs | 10 +++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/examples/event-read.rs b/examples/event-read.rs index 7de5f42ca..6162141b9 100644 --- a/examples/event-read.rs +++ b/examples/event-read.rs @@ -7,7 +7,10 @@ use std::io::stdout; use crossterm::event::poll; use crossterm::{ cursor::position, - event::{read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, + event::{ + read, DisableFocusChange, DisableMouseCapture, EnableFocusChange, EnableMouseCapture, + Event, KeyCode + }, execute, terminal::{disable_raw_mode, enable_raw_mode}, Result, @@ -15,7 +18,7 @@ use crossterm::{ use std::time::Duration; const HELP: &str = r#"Blocking read() - - Keyboard, mouse and terminal resize events enabled + - Keyboard, mouse, focus and terminal resize events enabled - Hit "c" to print current cursor position - Use Esc to quit "#; @@ -67,13 +70,13 @@ fn main() -> Result<()> { enable_raw_mode()?; let mut stdout = stdout(); - execute!(stdout, EnableMouseCapture)?; + execute!(stdout, EnableFocusChange, EnableMouseCapture)?; if let Err(e) = print_events() { println!("Error: {:?}\r", e); } - execute!(stdout, DisableMouseCapture)?; + execute!(stdout, DisableFocusChange, DisableMouseCapture)?; disable_raw_mode() } diff --git a/src/event.rs b/src/event.rs index accc19c1d..d3b30a9a7 100644 --- a/src/event.rs +++ b/src/event.rs @@ -34,6 +34,8 @@ //! loop { //! // `read()` blocks until an `Event` is available //! match read()? { +//! Event::FocusGained => println!("FocusGained"), +//! Event::FocusLost => println!("FocusLost"), //! Event::Key(event) => println!("{:?}", event), //! Event::Mouse(event) => println!("{:?}", event), //! Event::Resize(width, height) => println!("New size {}x{}", width, height), @@ -57,6 +59,8 @@ //! // It's guaranteed that the `read()` won't block when the `poll()` //! // function returns `true` //! match read()? { +//! Event::FocusGained => println!("FocusGained"), +//! Event::FocusLost => println!("FocusLost"), //! Event::Key(event) => println!("{:?}", event), //! Event::Mouse(event) => println!("{:?}", event), //! Event::Resize(width, height) => println!("New size {}x{}", width, height), @@ -409,10 +413,50 @@ impl Command for PopKeyboardEnhancementFlags { } } +/// A command that enables focus event emission. +/// +/// Focus events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EnableFocusChange; + +impl Command for EnableFocusChange { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?1004h")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + // Focus events are always enabled on Windows + Ok(()) + } +} + +/// A command that disables focus event emission. +/// +/// Focus events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DisableFocusChange; + +impl Command for DisableFocusChange { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?1004l")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + // Focus events can't be disabled on Windows + Ok(()) + } +} + /// Represents an event. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] pub enum Event { + /// The terminal gained focus + FocusGained, + /// The terminal lost focus + FocusLost, /// A single key event with additional pressed modifiers. Key(KeyEvent), /// A single mouse event with additional pressed modifiers. diff --git a/src/event/source/windows.rs b/src/event/source/windows.rs index 669272d48..fa4bdca62 100644 --- a/src/event/source/windows.rs +++ b/src/event/source/windows.rs @@ -66,6 +66,10 @@ impl EventSource for WindowsEventSource { InputRecord::WindowBufferSizeEvent(record) => { Some(Event::Resize(record.size.x as u16, record.size.y as u16)) } + InputRecord::FocusEvent(record) => { + let event = if record.set_focus { Event::FocusGained } else { Event::FocusLost }; + Some(event) + } _ => None, }; diff --git a/src/event/sys/unix/parse.rs b/src/event/sys/unix/parse.rs index 4e127cac8..89cfeb537 100644 --- a/src/event/sys/unix/parse.rs +++ b/src/event/sys/unix/parse.rs @@ -167,6 +167,8 @@ pub(crate) fn parse_csi(buffer: &[u8]) -> Result> { })), b'M' => return parse_csi_normal_mouse(buffer), b'<' => return parse_csi_sgr_mouse(buffer), + b'I' => Some(Event::FocusGained), + b'O' => Some(Event::FocusLost), b'0'..=b'9' => { // Numbered escape code. if buffer.len() == 3 { @@ -745,6 +747,14 @@ mod tests { ); } + #[test] + fn test_parse_csi_focus() { + assert_eq!( + parse_csi(b"\x1B[O").unwrap(), + Some(InternalEvent::Event(Event::FocusLost)) + ); + } + #[test] fn test_parse_csi_rxvt_mouse() { assert_eq!(