Emit focus events (#689)

This commit is contained in:
Charlie Groves 2022-07-28 06:07:01 -04:00 committed by GitHub
parent c6a8952201
commit 069497b43b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 5 deletions

View File

@ -7,7 +7,10 @@ use std::io::stdout;
use crossterm::event::poll; use crossterm::event::poll;
use crossterm::{ use crossterm::{
cursor::position, cursor::position,
event::{read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, event::{
read, DisableFocusChange, DisableMouseCapture, EnableFocusChange, EnableMouseCapture,
Event, KeyCode,
},
execute, execute,
terminal::{disable_raw_mode, enable_raw_mode}, terminal::{disable_raw_mode, enable_raw_mode},
Result, Result,
@ -15,7 +18,7 @@ use crossterm::{
use std::time::Duration; use std::time::Duration;
const HELP: &str = r#"Blocking read() 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 - Hit "c" to print current cursor position
- Use Esc to quit - Use Esc to quit
"#; "#;
@ -67,13 +70,13 @@ fn main() -> Result<()> {
enable_raw_mode()?; enable_raw_mode()?;
let mut stdout = stdout(); let mut stdout = stdout();
execute!(stdout, EnableMouseCapture)?; execute!(stdout, EnableFocusChange, EnableMouseCapture)?;
if let Err(e) = print_events() { if let Err(e) = print_events() {
println!("Error: {:?}\r", e); println!("Error: {:?}\r", e);
} }
execute!(stdout, DisableMouseCapture)?; execute!(stdout, DisableFocusChange, DisableMouseCapture)?;
disable_raw_mode() disable_raw_mode()
} }

View File

@ -34,6 +34,8 @@
//! loop { //! loop {
//! // `read()` blocks until an `Event` is available //! // `read()` blocks until an `Event` is available
//! match read()? { //! match read()? {
//! Event::FocusGained => println!("FocusGained"),
//! Event::FocusLost => println!("FocusLost"),
//! Event::Key(event) => println!("{:?}", event), //! Event::Key(event) => println!("{:?}", event),
//! Event::Mouse(event) => println!("{:?}", event), //! Event::Mouse(event) => println!("{:?}", event),
//! Event::Resize(width, height) => println!("New size {}x{}", width, height), //! 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()` //! // It's guaranteed that the `read()` won't block when the `poll()`
//! // function returns `true` //! // function returns `true`
//! match read()? { //! match read()? {
//! Event::FocusGained => println!("FocusGained"),
//! Event::FocusLost => println!("FocusLost"),
//! Event::Key(event) => println!("{:?}", event), //! Event::Key(event) => println!("{:?}", event),
//! Event::Mouse(event) => println!("{:?}", event), //! Event::Mouse(event) => println!("{:?}", event),
//! Event::Resize(width, height) => println!("New size {}x{}", width, height), //! Event::Resize(width, height) => println!("New size {}x{}", width, height),
@ -74,6 +78,7 @@
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
#[cfg(windows)]
use std::io; use std::io;
use std::time::Duration; use std::time::Duration;
@ -409,10 +414,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. /// Represents an event.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
pub enum Event { pub enum Event {
/// The terminal gained focus
FocusGained,
/// The terminal lost focus
FocusLost,
/// A single key event with additional pressed modifiers. /// A single key event with additional pressed modifiers.
Key(KeyEvent), Key(KeyEvent),
/// A single mouse event with additional pressed modifiers. /// A single mouse event with additional pressed modifiers.

View File

@ -66,6 +66,14 @@ impl EventSource for WindowsEventSource {
InputRecord::WindowBufferSizeEvent(record) => { InputRecord::WindowBufferSizeEvent(record) => {
Some(Event::Resize(record.size.x as u16, record.size.y as u16)) 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, _ => None,
}; };

View File

@ -167,6 +167,8 @@ pub(crate) fn parse_csi(buffer: &[u8]) -> Result<Option<InternalEvent>> {
})), })),
b'M' => return parse_csi_normal_mouse(buffer), b'M' => return parse_csi_normal_mouse(buffer),
b'<' => return parse_csi_sgr_mouse(buffer), b'<' => return parse_csi_sgr_mouse(buffer),
b'I' => Some(Event::FocusGained),
b'O' => Some(Event::FocusLost),
b'0'..=b'9' => { b'0'..=b'9' => {
// Numbered escape code. // Numbered escape code.
if buffer.len() == 3 { 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] #[test]
fn test_parse_csi_rxvt_mouse() { fn test_parse_csi_rxvt_mouse() {
assert_eq!( assert_eq!(

View File

@ -350,7 +350,6 @@ pub struct SetStyle(pub ContentStyle);
impl Command for SetStyle { impl Command for SetStyle {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
if let Some(bg) = self.0.background_color { if let Some(bg) = self.0.background_color {
execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?; execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
} }