Emit focus events (#689)
This commit is contained in:
parent
c6a8952201
commit
069497b43b
@ -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()
|
||||||
}
|
}
|
||||||
|
45
src/event.rs
45
src/event.rs
@ -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.
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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!(
|
||||||
|
@ -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)?;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user