Skip to content

Commit

Permalink
Emit focus events
Browse files Browse the repository at this point in the history
  • Loading branch information
groves committed Jul 27, 2022
1 parent c6a8952 commit 0287275
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 5 deletions.
11 changes: 7 additions & 4 deletions examples/event-read.rs
Expand Up @@ -7,15 +7,18 @@ 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,
};
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
"#;
Expand Down Expand Up @@ -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()
}
44 changes: 44 additions & 0 deletions src/event.rs
Expand Up @@ -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),
Expand All @@ -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),
Expand Down Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions src/event/source/windows.rs
Expand Up @@ -66,6 +66,14 @@ 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,
};

Expand Down
10 changes: 10 additions & 0 deletions src/event/sys/unix/parse.rs
Expand Up @@ -167,6 +167,8 @@ pub(crate) fn parse_csi(buffer: &[u8]) -> Result<Option<InternalEvent>> {
})),
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 {
Expand Down Expand Up @@ -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!(
Expand Down
1 change: 0 additions & 1 deletion src/style.rs
Expand Up @@ -350,7 +350,6 @@ pub struct SetStyle(pub ContentStyle);

impl Command for SetStyle {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {

if let Some(bg) = self.0.background_color {
execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
}
Expand Down

0 comments on commit 0287275

Please sign in to comment.