Add support for any event tracking (#502)
This commit is contained in:
parent
30ce6364a9
commit
e5d3959119
54
src/event.rs
54
src/event.rs
@ -285,7 +285,7 @@ pub enum Event {
|
|||||||
/// ## Mouse Buttons
|
/// ## Mouse Buttons
|
||||||
///
|
///
|
||||||
/// Some platforms/terminals do not report mouse button for the
|
/// Some platforms/terminals do not report mouse button for the
|
||||||
/// `MouseEvent::Up` and `MouseEvent::Drag` events. `MouseButton::Left`
|
/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left`
|
||||||
/// is returned if we don't know which button was used.
|
/// is returned if we don't know which button was used.
|
||||||
///
|
///
|
||||||
/// ## Key Modifiers
|
/// ## Key Modifiers
|
||||||
@ -295,27 +295,41 @@ pub enum Event {
|
|||||||
/// `Ctrl` + left mouse button click as a right mouse button click.
|
/// `Ctrl` + left mouse button click as a right mouse button click.
|
||||||
#[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 MouseEvent {
|
pub struct MouseEvent {
|
||||||
/// Pressed mouse button.
|
/// The kind of mouse event that was caused.
|
||||||
///
|
pub kind: MouseEventKind,
|
||||||
/// Contains mouse button, pressed pointer location (column, row), and additional key modifiers.
|
/// The column that the event occurred on.
|
||||||
Down(MouseButton, u16, u16, KeyModifiers),
|
pub column: u16,
|
||||||
/// Released mouse button.
|
/// The row that the event occurred on.
|
||||||
///
|
pub row: u16,
|
||||||
/// Contains mouse button, released pointer location (column, row), and additional key modifiers.
|
/// The key modifiers active when the event occurred.
|
||||||
Up(MouseButton, u16, u16, KeyModifiers),
|
pub modifiers: KeyModifiers,
|
||||||
/// Moved mouse pointer while pressing a mouse button.
|
}
|
||||||
///
|
|
||||||
/// Contains the pressed mouse button, released pointer location (column, row), and additional key modifiers.
|
/// A mouse event kind.
|
||||||
Drag(MouseButton, u16, u16, KeyModifiers),
|
///
|
||||||
|
/// # Platform-specific Notes
|
||||||
|
///
|
||||||
|
/// ## Mouse Buttons
|
||||||
|
///
|
||||||
|
/// Some platforms/terminals do not report mouse button for the
|
||||||
|
/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left`
|
||||||
|
/// is returned if we don't know which button was used.
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
pub enum MouseEventKind {
|
||||||
|
/// Pressed mouse button. Contains the button that was pressed.
|
||||||
|
Down(MouseButton),
|
||||||
|
/// Released mouse button. Contains the button that was released.
|
||||||
|
Up(MouseButton),
|
||||||
|
/// Moved the mouse cursor while pressing the contained mouse button.
|
||||||
|
Drag(MouseButton),
|
||||||
|
/// Moved the mouse cursor while not pressing a mouse button.
|
||||||
|
Moved,
|
||||||
/// Scrolled mouse wheel downwards (towards the user).
|
/// Scrolled mouse wheel downwards (towards the user).
|
||||||
///
|
ScrollDown,
|
||||||
/// Contains the scroll location (column, row), and additional key modifiers.
|
|
||||||
ScrollDown(u16, u16, KeyModifiers),
|
|
||||||
/// Scrolled mouse wheel upwards (away from the user).
|
/// Scrolled mouse wheel upwards (away from the user).
|
||||||
///
|
ScrollUp,
|
||||||
/// Contains the scroll location (column, row), and additional key modifiers.
|
|
||||||
ScrollUp(u16, u16, KeyModifiers),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a mouse button.
|
/// Represents a mouse button.
|
||||||
|
@ -3,15 +3,23 @@
|
|||||||
use crate::csi;
|
use crate::csi;
|
||||||
|
|
||||||
pub(crate) const ENABLE_MOUSE_MODE_CSI_SEQUENCE: &str = concat!(
|
pub(crate) const ENABLE_MOUSE_MODE_CSI_SEQUENCE: &str = concat!(
|
||||||
|
// Normal tracking: Send mouse X & Y on button press and release
|
||||||
csi!("?1000h"),
|
csi!("?1000h"),
|
||||||
|
// Button-event tracking: Report button motion events (dragging)
|
||||||
csi!("?1002h"),
|
csi!("?1002h"),
|
||||||
|
// Any-event tracking: Report all motion events
|
||||||
|
csi!("?1003h"),
|
||||||
|
// RXVT mouse mode: Allows mouse coordinates of >223
|
||||||
csi!("?1015h"),
|
csi!("?1015h"),
|
||||||
csi!("?1006h")
|
// SGR mouse mode: Allows mouse coordinates of >223, preferred over RXVT mode
|
||||||
|
csi!("?1006h"),
|
||||||
);
|
);
|
||||||
|
|
||||||
pub(crate) const DISABLE_MOUSE_MODE_CSI_SEQUENCE: &str = concat!(
|
pub(crate) const DISABLE_MOUSE_MODE_CSI_SEQUENCE: &str = concat!(
|
||||||
|
// The above, in reverse order.
|
||||||
csi!("?1006l"),
|
csi!("?1006l"),
|
||||||
csi!("?1015l"),
|
csi!("?1015l"),
|
||||||
|
csi!("?1003l"),
|
||||||
csi!("?1002l"),
|
csi!("?1002l"),
|
||||||
csi!("?1000l")
|
csi!("?1000l"),
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent},
|
event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind},
|
||||||
ErrorKind, Result,
|
ErrorKind, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -141,8 +141,8 @@ pub(crate) fn parse_csi(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
|||||||
code: KeyCode::BackTab,
|
code: KeyCode::BackTab,
|
||||||
modifiers: KeyModifiers::SHIFT,
|
modifiers: KeyModifiers::SHIFT,
|
||||||
})),
|
})),
|
||||||
b'M' => return parse_csi_x10_mouse(buffer),
|
b'M' => return parse_csi_normal_mouse(buffer),
|
||||||
b'<' => return parse_csi_xterm_mouse(buffer),
|
b'<' => return parse_csi_sgr_mouse(buffer),
|
||||||
b'0'..=b'9' => {
|
b'0'..=b'9' => {
|
||||||
// Numbered escape code.
|
// Numbered escape code.
|
||||||
if buffer.len() == 3 {
|
if buffer.len() == 3 {
|
||||||
@ -285,54 +285,24 @@ pub(crate) fn parse_csi_rxvt_mouse(buffer: &[u8]) -> Result<Option<InternalEvent
|
|||||||
.map_err(|_| could_not_parse_event_error())?;
|
.map_err(|_| could_not_parse_event_error())?;
|
||||||
let mut split = s.split(';');
|
let mut split = s.split(';');
|
||||||
|
|
||||||
let cb = next_parsed::<u16>(&mut split)?;
|
let cb = next_parsed::<u8>(&mut split)?
|
||||||
|
.checked_sub(32)
|
||||||
|
.ok_or_else(could_not_parse_event_error)?;
|
||||||
|
let (kind, modifiers) = parse_cb(cb)?;
|
||||||
|
|
||||||
let cx = next_parsed::<u16>(&mut split)? - 1;
|
let cx = next_parsed::<u16>(&mut split)? - 1;
|
||||||
let cy = next_parsed::<u16>(&mut split)? - 1;
|
let cy = next_parsed::<u16>(&mut split)? - 1;
|
||||||
|
|
||||||
let mut modifiers = KeyModifiers::empty();
|
Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
|
kind,
|
||||||
if cb & 0b0000_0100 == 0b0000_0100 {
|
column: cx,
|
||||||
modifiers |= KeyModifiers::SHIFT;
|
row: cy,
|
||||||
}
|
modifiers,
|
||||||
|
}))))
|
||||||
if cb & 0b0000_1000 == 0b0000_1000 {
|
|
||||||
modifiers |= KeyModifiers::ALT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if cb & 0b0001_0000 == 0b0001_0000 {
|
|
||||||
modifiers |= KeyModifiers::CONTROL;
|
|
||||||
}
|
|
||||||
|
|
||||||
let event = if cb & 0b0110_0000 == 0b0110_0000 {
|
|
||||||
if cb & 0b0000_0001 == 0b0000_0001 {
|
|
||||||
MouseEvent::ScrollDown(cx, cy, modifiers)
|
|
||||||
} else {
|
|
||||||
MouseEvent::ScrollUp(cx, cy, modifiers)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let drag = cb & 0b0100_0000 == 0b0100_0000;
|
|
||||||
|
|
||||||
match (cb & 0b0000_0011, drag) {
|
|
||||||
(0b0000_0000, false) => MouseEvent::Down(MouseButton::Left, cx, cy, modifiers),
|
|
||||||
(0b0000_0010, false) => MouseEvent::Down(MouseButton::Right, cx, cy, modifiers),
|
|
||||||
(0b0000_0001, false) => MouseEvent::Down(MouseButton::Middle, cx, cy, modifiers),
|
|
||||||
|
|
||||||
(0b0000_0000, true) => MouseEvent::Drag(MouseButton::Left, cx, cy, modifiers),
|
|
||||||
(0b0000_0010, true) => MouseEvent::Drag(MouseButton::Right, cx, cy, modifiers),
|
|
||||||
(0b0000_0001, true) => MouseEvent::Drag(MouseButton::Middle, cx, cy, modifiers),
|
|
||||||
|
|
||||||
(0b0000_0011, false) => MouseEvent::Up(MouseButton::Left, cx, cy, modifiers),
|
|
||||||
|
|
||||||
_ => return Err(could_not_parse_event_error()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(InternalEvent::Event(Event::Mouse(event))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_csi_x10_mouse(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
pub(crate) fn parse_csi_normal_mouse(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
||||||
// X10 emulation mouse encoding: ESC [ M CB Cx Cy (6 characters only).
|
// Normal mouse encoding: ESC [ M CB Cx Cy (6 characters only).
|
||||||
// NOTE (@imdaveho): cannot find documentation on this
|
|
||||||
|
|
||||||
assert!(buffer.starts_with(&[b'\x1B', b'[', b'M'])); // ESC [ M
|
assert!(buffer.starts_with(&[b'\x1B', b'[', b'M'])); // ESC [ M
|
||||||
|
|
||||||
@ -340,51 +310,26 @@ pub(crate) fn parse_csi_x10_mouse(buffer: &[u8]) -> Result<Option<InternalEvent>
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cb = buffer[3] - 0x30;
|
let cb = buffer[3]
|
||||||
|
.checked_sub(32)
|
||||||
|
.ok_or_else(could_not_parse_event_error)?;
|
||||||
|
let (kind, modifiers) = parse_cb(cb)?;
|
||||||
|
|
||||||
// See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
|
// See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
|
||||||
// The upper left character position on the terminal is denoted as 1,1.
|
// The upper left character position on the terminal is denoted as 1,1.
|
||||||
// Subtract 1 to keep it synced with cursor
|
// Subtract 1 to keep it synced with cursor
|
||||||
let cx = u16::from(buffer[4].saturating_sub(32)) - 1;
|
let cx = u16::from(buffer[4].saturating_sub(32)) - 1;
|
||||||
let cy = u16::from(buffer[5].saturating_sub(32)) - 1;
|
let cy = u16::from(buffer[5].saturating_sub(32)) - 1;
|
||||||
|
|
||||||
let mut modifiers = KeyModifiers::empty();
|
Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
|
kind,
|
||||||
if cb & 0b0000_0100 == 0b0000_0100 {
|
column: cx,
|
||||||
modifiers |= KeyModifiers::SHIFT;
|
row: cy,
|
||||||
}
|
modifiers,
|
||||||
|
}))))
|
||||||
if cb & 0b0000_1000 == 0b0000_1000 {
|
|
||||||
modifiers |= KeyModifiers::ALT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if cb & 0b0001_0000 == 0b0001_0000 {
|
|
||||||
modifiers |= KeyModifiers::CONTROL;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mouse_input_event = match cb & 0b0000_0011 {
|
|
||||||
0 => {
|
|
||||||
if cb & 0b0100_0000 == 0b0100_0000 {
|
|
||||||
MouseEvent::ScrollUp(cx, cy, modifiers)
|
|
||||||
} else {
|
|
||||||
MouseEvent::Down(MouseButton::Left, cx, cy, modifiers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
if cb & 0b0100_0000 == 0b0100_0000 {
|
|
||||||
MouseEvent::ScrollDown(cx, cy, modifiers)
|
|
||||||
} else {
|
|
||||||
MouseEvent::Down(MouseButton::Middle, cx, cy, modifiers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2 => MouseEvent::Down(MouseButton::Right, cx, cy, modifiers),
|
|
||||||
3 => MouseEvent::Up(MouseButton::Left, cx, cy, modifiers),
|
|
||||||
_ => return Err(could_not_parse_event_error()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(InternalEvent::Event(Event::Mouse(mouse_input_event))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_csi_xterm_mouse(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
pub(crate) fn parse_csi_sgr_mouse(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
||||||
// ESC [ < Cb ; Cx ; Cy (;) (M or m)
|
// ESC [ < Cb ; Cx ; Cy (;) (M or m)
|
||||||
|
|
||||||
assert!(buffer.starts_with(&[b'\x1B', b'[', b'<'])); // ESC [ <
|
assert!(buffer.starts_with(&[b'\x1B', b'[', b'<'])); // ESC [ <
|
||||||
@ -397,7 +342,8 @@ pub(crate) fn parse_csi_xterm_mouse(buffer: &[u8]) -> Result<Option<InternalEven
|
|||||||
.map_err(|_| could_not_parse_event_error())?;
|
.map_err(|_| could_not_parse_event_error())?;
|
||||||
let mut split = s.split(';');
|
let mut split = s.split(';');
|
||||||
|
|
||||||
let cb = next_parsed::<u16>(&mut split)?;
|
let cb = next_parsed::<u8>(&mut split)?;
|
||||||
|
let (kind, modifiers) = parse_cb(cb)?;
|
||||||
|
|
||||||
// See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
|
// See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
|
||||||
// The upper left character position on the terminal is denoted as 1,1.
|
// The upper left character position on the terminal is denoted as 1,1.
|
||||||
@ -405,50 +351,74 @@ pub(crate) fn parse_csi_xterm_mouse(buffer: &[u8]) -> Result<Option<InternalEven
|
|||||||
let cx = next_parsed::<u16>(&mut split)? - 1;
|
let cx = next_parsed::<u16>(&mut split)? - 1;
|
||||||
let cy = next_parsed::<u16>(&mut split)? - 1;
|
let cy = next_parsed::<u16>(&mut split)? - 1;
|
||||||
|
|
||||||
|
// When button 3 in Cb is used to represent mouse release, you can't tell which button was
|
||||||
|
// released. SGR mode solves this by having the sequence end with a lowercase m if it's a
|
||||||
|
// button release and an uppercase M if it's a buton press.
|
||||||
|
//
|
||||||
|
// We've already checked that the last character is a lowercase or uppercase M at the start of
|
||||||
|
// this function, so we just need one if.
|
||||||
|
let kind = if buffer.last() == Some(&b'm') {
|
||||||
|
match kind {
|
||||||
|
MouseEventKind::Down(button) => MouseEventKind::Up(button),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
kind
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
|
kind,
|
||||||
|
column: cx,
|
||||||
|
row: cy,
|
||||||
|
modifiers,
|
||||||
|
}))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cb is the byte of a mouse input that contains the button being used, the key modifiers being
|
||||||
|
/// held and whether the mouse is dragging or not.
|
||||||
|
///
|
||||||
|
/// Bit layout of cb, from low to high:
|
||||||
|
///
|
||||||
|
/// - button number
|
||||||
|
/// - button number
|
||||||
|
/// - shift
|
||||||
|
/// - meta (alt)
|
||||||
|
/// - control
|
||||||
|
/// - mouse is dragging
|
||||||
|
/// - button number
|
||||||
|
/// - button number
|
||||||
|
fn parse_cb(cb: u8) -> Result<(MouseEventKind, KeyModifiers)> {
|
||||||
|
let button_number = (cb & 0b0000_0011) | ((cb & 0b1100_0000) >> 4);
|
||||||
|
let dragging = cb & 0b0010_0000 == 0b0010_0000;
|
||||||
|
|
||||||
|
let kind = match (button_number, dragging) {
|
||||||
|
(0, false) => MouseEventKind::Down(MouseButton::Left),
|
||||||
|
(1, false) => MouseEventKind::Down(MouseButton::Middle),
|
||||||
|
(2, false) => MouseEventKind::Down(MouseButton::Right),
|
||||||
|
(0, true) => MouseEventKind::Drag(MouseButton::Left),
|
||||||
|
(1, true) => MouseEventKind::Drag(MouseButton::Middle),
|
||||||
|
(2, true) => MouseEventKind::Drag(MouseButton::Right),
|
||||||
|
(3, false) => MouseEventKind::Up(MouseButton::Left),
|
||||||
|
(3, true) | (4, true) | (5, true) => MouseEventKind::Moved,
|
||||||
|
(4, false) => MouseEventKind::ScrollUp,
|
||||||
|
(5, false) => MouseEventKind::ScrollDown,
|
||||||
|
// We do not support other buttons.
|
||||||
|
_ => return Err(could_not_parse_event_error()),
|
||||||
|
};
|
||||||
|
|
||||||
let mut modifiers = KeyModifiers::empty();
|
let mut modifiers = KeyModifiers::empty();
|
||||||
|
|
||||||
if cb & 0b0000_0100 == 0b0000_0100 {
|
if cb & 0b0000_0100 == 0b0000_0100 {
|
||||||
modifiers |= KeyModifiers::SHIFT;
|
modifiers |= KeyModifiers::SHIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cb & 0b0000_1000 == 0b0000_1000 {
|
if cb & 0b0000_1000 == 0b0000_1000 {
|
||||||
modifiers |= KeyModifiers::ALT;
|
modifiers |= KeyModifiers::ALT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cb & 0b0001_0000 == 0b0001_0000 {
|
if cb & 0b0001_0000 == 0b0001_0000 {
|
||||||
modifiers |= KeyModifiers::CONTROL;
|
modifiers |= KeyModifiers::CONTROL;
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = if cb & 0b0100_0000 == 0b0100_0000 {
|
Ok((kind, modifiers))
|
||||||
if cb & 0b0000_0001 == 0b0000_0001 {
|
|
||||||
MouseEvent::ScrollDown(cx, cy, modifiers)
|
|
||||||
} else {
|
|
||||||
MouseEvent::ScrollUp(cx, cy, modifiers)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let up = match buffer.last().unwrap() {
|
|
||||||
b'm' => true,
|
|
||||||
b'M' => false,
|
|
||||||
_ => return Err(could_not_parse_event_error()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let drag = cb & 0b0010_0000 == 0b0010_0000;
|
|
||||||
|
|
||||||
match (cb & 0b0000_0011, up, drag) {
|
|
||||||
(0, true, _) => MouseEvent::Up(MouseButton::Left, cx, cy, modifiers),
|
|
||||||
(0, false, false) => MouseEvent::Down(MouseButton::Left, cx, cy, modifiers),
|
|
||||||
(0, false, true) => MouseEvent::Drag(MouseButton::Left, cx, cy, modifiers),
|
|
||||||
(1, true, _) => MouseEvent::Up(MouseButton::Middle, cx, cy, modifiers),
|
|
||||||
(1, false, false) => MouseEvent::Down(MouseButton::Middle, cx, cy, modifiers),
|
|
||||||
(1, false, true) => MouseEvent::Drag(MouseButton::Middle, cx, cy, modifiers),
|
|
||||||
(2, true, _) => MouseEvent::Up(MouseButton::Right, cx, cy, modifiers),
|
|
||||||
(2, false, false) => MouseEvent::Down(MouseButton::Right, cx, cy, modifiers),
|
|
||||||
(2, false, true) => MouseEvent::Drag(MouseButton::Right, cx, cy, modifiers),
|
|
||||||
_ => return Err(could_not_parse_event_error()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(InternalEvent::Event(Event::Mouse(event))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_utf8_char(buffer: &[u8]) -> Result<Option<char>> {
|
pub(crate) fn parse_utf8_char(buffer: &[u8]) -> Result<Option<char>> {
|
||||||
@ -499,20 +469,20 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_esc_key() {
|
fn test_esc_key() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\x1B".as_bytes(), false).unwrap(),
|
parse_event(b"\x1B", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into()))),
|
Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into()))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_possible_esc_sequence() {
|
fn test_possible_esc_sequence() {
|
||||||
assert_eq!(parse_event("\x1B".as_bytes(), true).unwrap(), None,);
|
assert_eq!(parse_event(b"\x1B", true).unwrap(), None,);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_alt_key() {
|
fn test_alt_key() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\x1Bc".as_bytes(), false).unwrap(),
|
parse_event(b"\x1Bc", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||||
KeyCode::Char('c'),
|
KeyCode::Char('c'),
|
||||||
KeyModifiers::ALT
|
KeyModifiers::ALT
|
||||||
@ -527,19 +497,19 @@ mod tests {
|
|||||||
|
|
||||||
// parse_csi_cursor_position
|
// parse_csi_cursor_position
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\x1B[20;10R".as_bytes(), false).unwrap(),
|
parse_event(b"\x1B[20;10R", false).unwrap(),
|
||||||
Some(InternalEvent::CursorPosition(9, 19))
|
Some(InternalEvent::CursorPosition(9, 19))
|
||||||
);
|
);
|
||||||
|
|
||||||
// parse_csi
|
// parse_csi
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\x1B[D".as_bytes(), false).unwrap(),
|
parse_event(b"\x1B[D", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))),
|
Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))),
|
||||||
);
|
);
|
||||||
|
|
||||||
// parse_csi_modifier_key_code
|
// parse_csi_modifier_key_code
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\x1B[2D".as_bytes(), false).unwrap(),
|
parse_event(b"\x1B[2D", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||||
KeyCode::Left,
|
KeyCode::Left,
|
||||||
KeyModifiers::SHIFT
|
KeyModifiers::SHIFT
|
||||||
@ -548,41 +518,41 @@ mod tests {
|
|||||||
|
|
||||||
// parse_csi_special_key_code
|
// parse_csi_special_key_code
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\x1B[3~".as_bytes(), false).unwrap(),
|
parse_event(b"\x1B[3~", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))),
|
Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))),
|
||||||
);
|
);
|
||||||
|
|
||||||
// parse_csi_rxvt_mouse
|
// parse_csi_rxvt_mouse
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\x1B[32;30;40;M".as_bytes(), false).unwrap(),
|
parse_event(b"\x1B[32;30;40;M", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Mouse(MouseEvent::Down(
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
MouseButton::Left,
|
kind: MouseEventKind::Down(MouseButton::Left),
|
||||||
29,
|
column: 29,
|
||||||
39,
|
row: 39,
|
||||||
KeyModifiers::empty(),
|
modifiers: KeyModifiers::empty(),
|
||||||
))))
|
})))
|
||||||
);
|
);
|
||||||
|
|
||||||
// parse_csi_x10_mouse
|
// parse_csi_normal_mouse
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\x1B[M0\x60\x70".as_bytes(), false).unwrap(),
|
parse_event(b"\x1B[M0\x60\x70", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Mouse(MouseEvent::Down(
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
MouseButton::Left,
|
kind: MouseEventKind::Down(MouseButton::Left),
|
||||||
63,
|
column: 63,
|
||||||
79,
|
row: 79,
|
||||||
KeyModifiers::empty(),
|
modifiers: KeyModifiers::CONTROL,
|
||||||
))))
|
})))
|
||||||
);
|
);
|
||||||
|
|
||||||
// parse_csi_xterm_mouse
|
// parse_csi_sgr_mouse
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\x1B[<0;20;10;M".as_bytes(), false).unwrap(),
|
parse_event(b"\x1B[<0;20;10;M", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Mouse(MouseEvent::Down(
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
MouseButton::Left,
|
kind: MouseEventKind::Down(MouseButton::Left),
|
||||||
19,
|
column: 19,
|
||||||
9,
|
row: 9,
|
||||||
KeyModifiers::empty(),
|
modifiers: KeyModifiers::empty(),
|
||||||
))))
|
})))
|
||||||
);
|
);
|
||||||
|
|
||||||
// parse_utf8_char
|
// parse_utf8_char
|
||||||
@ -598,7 +568,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_event() {
|
fn test_parse_event() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("\t".as_bytes(), false).unwrap(),
|
parse_event(b"\t", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyCode::Tab.into()))),
|
Some(InternalEvent::Event(Event::Key(KeyCode::Tab.into()))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -606,7 +576,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_csi_cursor_position() {
|
fn test_parse_csi_cursor_position() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_cursor_position("\x1B[20;10R".as_bytes()).unwrap(),
|
parse_csi_cursor_position(b"\x1B[20;10R").unwrap(),
|
||||||
Some(InternalEvent::CursorPosition(9, 19))
|
Some(InternalEvent::CursorPosition(9, 19))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -614,7 +584,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_csi() {
|
fn test_parse_csi() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi("\x1B[D".as_bytes()).unwrap(),
|
parse_csi(b"\x1B[D").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))),
|
Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -622,7 +592,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_csi_modifier_key_code() {
|
fn test_parse_csi_modifier_key_code() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_modifier_key_code("\x1B[2D".as_bytes()).unwrap(),
|
parse_csi_modifier_key_code(b"\x1B[2D").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||||
KeyCode::Left,
|
KeyCode::Left,
|
||||||
KeyModifiers::SHIFT
|
KeyModifiers::SHIFT
|
||||||
@ -633,7 +603,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_csi_special_key_code() {
|
fn test_parse_csi_special_key_code() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_special_key_code("\x1B[3~".as_bytes()).unwrap(),
|
parse_csi_special_key_code(b"\x1B[3~").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))),
|
Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -641,7 +611,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_csi_special_key_code_multiple_values_not_supported() {
|
fn test_parse_csi_special_key_code_multiple_values_not_supported() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_special_key_code("\x1B[3;2~".as_bytes()).unwrap(),
|
parse_csi_special_key_code(b"\x1B[3;2~").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||||
KeyCode::Delete,
|
KeyCode::Delete,
|
||||||
KeyModifiers::SHIFT
|
KeyModifiers::SHIFT
|
||||||
@ -652,66 +622,66 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_csi_rxvt_mouse() {
|
fn test_parse_csi_rxvt_mouse() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_rxvt_mouse("\x1B[32;30;40;M".as_bytes()).unwrap(),
|
parse_csi_rxvt_mouse(b"\x1B[32;30;40;M").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Mouse(MouseEvent::Down(
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
MouseButton::Left,
|
kind: MouseEventKind::Down(MouseButton::Left),
|
||||||
29,
|
column: 29,
|
||||||
39,
|
row: 39,
|
||||||
KeyModifiers::empty(),
|
modifiers: KeyModifiers::empty(),
|
||||||
))))
|
})))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_csi_x10_mouse() {
|
fn test_parse_csi_normal_mouse() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_x10_mouse("\x1B[M0\x60\x70".as_bytes()).unwrap(),
|
parse_csi_normal_mouse(b"\x1B[M0\x60\x70").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Mouse(MouseEvent::Down(
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
MouseButton::Left,
|
kind: MouseEventKind::Down(MouseButton::Left),
|
||||||
63,
|
column: 63,
|
||||||
79,
|
row: 79,
|
||||||
KeyModifiers::empty(),
|
modifiers: KeyModifiers::CONTROL,
|
||||||
))))
|
})))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_csi_xterm_mouse() {
|
fn test_parse_csi_sgr_mouse() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_xterm_mouse("\x1B[<0;20;10;M".as_bytes()).unwrap(),
|
parse_csi_sgr_mouse(b"\x1B[<0;20;10;M").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Mouse(MouseEvent::Down(
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
MouseButton::Left,
|
kind: MouseEventKind::Down(MouseButton::Left),
|
||||||
19,
|
column: 19,
|
||||||
9,
|
row: 9,
|
||||||
KeyModifiers::empty(),
|
modifiers: KeyModifiers::empty(),
|
||||||
))))
|
})))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_xterm_mouse("\x1B[<0;20;10M".as_bytes()).unwrap(),
|
parse_csi_sgr_mouse(b"\x1B[<0;20;10M").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Mouse(MouseEvent::Down(
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
MouseButton::Left,
|
kind: MouseEventKind::Down(MouseButton::Left),
|
||||||
19,
|
column: 19,
|
||||||
9,
|
row: 9,
|
||||||
KeyModifiers::empty(),
|
modifiers: KeyModifiers::empty(),
|
||||||
))))
|
})))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_xterm_mouse("\x1B[<0;20;10;m".as_bytes()).unwrap(),
|
parse_csi_sgr_mouse(b"\x1B[<0;20;10;m").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Mouse(MouseEvent::Up(
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
MouseButton::Left,
|
kind: MouseEventKind::Up(MouseButton::Left),
|
||||||
19,
|
column: 19,
|
||||||
9,
|
row: 9,
|
||||||
KeyModifiers::empty(),
|
modifiers: KeyModifiers::empty(),
|
||||||
))))
|
})))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_csi_xterm_mouse("\x1B[<0;20;10m".as_bytes()).unwrap(),
|
parse_csi_sgr_mouse(b"\x1B[<0;20;10m").unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Mouse(MouseEvent::Up(
|
Some(InternalEvent::Event(Event::Mouse(MouseEvent {
|
||||||
MouseButton::Left,
|
kind: MouseEventKind::Up(MouseButton::Left),
|
||||||
19,
|
column: 19,
|
||||||
9,
|
row: 9,
|
||||||
KeyModifiers::empty(),
|
modifiers: KeyModifiers::empty(),
|
||||||
))))
|
})))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,7 +690,7 @@ mod tests {
|
|||||||
// https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805
|
// https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805
|
||||||
|
|
||||||
// 'Valid ASCII' => "a",
|
// 'Valid ASCII' => "a",
|
||||||
assert_eq!(parse_utf8_char("a".as_bytes()).unwrap(), Some('a'),);
|
assert_eq!(parse_utf8_char(b"a").unwrap(), Some('a'),);
|
||||||
|
|
||||||
// 'Valid 2 Octet Sequence' => "\xc3\xb1",
|
// 'Valid 2 Octet Sequence' => "\xc3\xb1",
|
||||||
assert_eq!(parse_utf8_char(&[0xC3, 0xB1]).unwrap(), Some('ñ'),);
|
assert_eq!(parse_utf8_char(&[0xC3, 0xB1]).unwrap(), Some('ñ'),);
|
||||||
@ -762,7 +732,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_char_event_lowercase() {
|
fn test_parse_char_event_lowercase() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("c".as_bytes(), false).unwrap(),
|
parse_event(b"c", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||||
KeyCode::Char('c'),
|
KeyCode::Char('c'),
|
||||||
KeyModifiers::empty()
|
KeyModifiers::empty()
|
||||||
@ -773,7 +743,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_char_event_uppercase() {
|
fn test_parse_char_event_uppercase() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_event("C".as_bytes(), false).unwrap(),
|
parse_event(b"C", false).unwrap(),
|
||||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||||
KeyCode::Char('C'),
|
KeyCode::Char('C'),
|
||||||
KeyModifiers::SHIFT
|
KeyModifiers::SHIFT
|
||||||
|
@ -10,7 +10,7 @@ use winapi::um::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton},
|
event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEventKind},
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -142,32 +142,20 @@ fn parse_mouse_event_record(event: &MouseEvent) -> Result<Option<crate::event::M
|
|||||||
MouseButton::Left
|
MouseButton::Left
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(match event.event_flags {
|
let kind = match event.event_flags {
|
||||||
EventFlags::PressOrRelease => {
|
EventFlags::PressOrRelease => {
|
||||||
if button_state.release_button() {
|
if button_state.release_button() {
|
||||||
// in order to read the up button type, we have to check the last down input record.
|
// in order to read the up button type, we have to check the last down input record.
|
||||||
Some(crate::event::MouseEvent::Up(
|
Some(MouseEventKind::Up(MouseButton::Left))
|
||||||
MouseButton::Left,
|
|
||||||
xpos,
|
|
||||||
ypos,
|
|
||||||
modifiers,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
Some(crate::event::MouseEvent::Down(
|
Some(MouseEventKind::Down(button))
|
||||||
button, xpos, ypos, modifiers,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventFlags::MouseMoved => {
|
EventFlags::MouseMoved => {
|
||||||
// Click + Move
|
if button_state.release_button() {
|
||||||
// Only register when mouse is not released
|
Some(MouseEventKind::Moved)
|
||||||
// because unix systems share this behaviour.
|
|
||||||
if !button_state.release_button() {
|
|
||||||
Some(crate::event::MouseEvent::Drag(
|
|
||||||
button, xpos, ypos, modifiers,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
Some(MouseEventKind::Drag(button))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventFlags::MouseWheeled => {
|
EventFlags::MouseWheeled => {
|
||||||
@ -175,14 +163,21 @@ fn parse_mouse_event_record(event: &MouseEvent) -> Result<Option<crate::event::M
|
|||||||
// from https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str
|
// from https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str
|
||||||
// if `button_state` is negative then the wheel was rotated backward, toward the user.
|
// if `button_state` is negative then the wheel was rotated backward, toward the user.
|
||||||
if button_state.scroll_down() {
|
if button_state.scroll_down() {
|
||||||
Some(crate::event::MouseEvent::ScrollDown(xpos, ypos, modifiers))
|
Some(MouseEventKind::ScrollDown)
|
||||||
} else if button_state.scroll_up() {
|
} else if button_state.scroll_up() {
|
||||||
Some(crate::event::MouseEvent::ScrollUp(xpos, ypos, modifiers))
|
Some(MouseEventKind::ScrollUp)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventFlags::DoubleClick => None, // double click not supported by unix terminals
|
EventFlags::DoubleClick => None, // double click not supported by unix terminals
|
||||||
EventFlags::MouseHwheeled => None, // horizontal scroll not supported by unix terminals
|
EventFlags::MouseHwheeled => None, // horizontal scroll not supported by unix terminals
|
||||||
})
|
};
|
||||||
|
|
||||||
|
Ok(kind.map(|kind| crate::event::MouseEvent {
|
||||||
|
kind,
|
||||||
|
column: xpos,
|
||||||
|
row: ypos,
|
||||||
|
modifiers,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,9 @@ pub(crate) struct WinApiPoll {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WinApiPoll {
|
impl WinApiPoll {
|
||||||
#[cfg(not(feature = "event-stream"))]
|
|
||||||
pub(crate) fn new() -> Result<WinApiPoll> {
|
|
||||||
Ok(WinApiPoll {})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "event-stream")]
|
|
||||||
pub(crate) fn new() -> Result<WinApiPoll> {
|
pub(crate) fn new() -> Result<WinApiPoll> {
|
||||||
Ok(WinApiPoll {
|
Ok(WinApiPoll {
|
||||||
|
#[cfg(feature = "event-stream")]
|
||||||
waker: Waker::new()?,
|
waker: Waker::new()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user