aboutsummaryrefslogtreecommitdiff
path: root/src/input
diff options
context:
space:
mode:
Diffstat (limited to 'src/input')
-rw-r--r--src/input/encode.rs73
-rw-r--r--src/input/mod.rs2
-rw-r--r--src/input/tests.rs29
3 files changed, 101 insertions, 3 deletions
diff --git a/src/input/encode.rs b/src/input/encode.rs
index b114935a..ee466f42 100644
--- a/src/input/encode.rs
+++ b/src/input/encode.rs
@@ -1,4 +1,4 @@
-use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
+use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEventKind};
use super::{KeyboardProtocol, TerminalKey};
@@ -23,6 +23,77 @@ pub fn encode_terminal_key(key: TerminalKey, protocol: KeyboardProtocol) -> Vec<
encode_legacy(key.as_key_event())
}
+pub fn encode_cursor_key(code: KeyCode, application_cursor: bool) -> Vec<u8> {
+ match (code, application_cursor) {
+ (KeyCode::Up, true) => b"\x1bOA".to_vec(),
+ (KeyCode::Down, true) => b"\x1bOB".to_vec(),
+ (KeyCode::Right, true) => b"\x1bOC".to_vec(),
+ (KeyCode::Left, true) => b"\x1bOD".to_vec(),
+ (KeyCode::Up, false) => b"\x1b[A".to_vec(),
+ (KeyCode::Down, false) => b"\x1b[B".to_vec(),
+ (KeyCode::Right, false) => b"\x1b[C".to_vec(),
+ (KeyCode::Left, false) => b"\x1b[D".to_vec(),
+ _ => encode_legacy(KeyEvent::new(code, KeyModifiers::empty())),
+ }
+}
+
+pub fn encode_mouse_scroll(
+ kind: MouseEventKind,
+ column: u16,
+ row: u16,
+ modifiers: KeyModifiers,
+ encoding: vt100::MouseProtocolEncoding,
+) -> Option<Vec<u8>> {
+ let button = match kind {
+ MouseEventKind::ScrollUp => 64u16,
+ MouseEventKind::ScrollDown => 65u16,
+ MouseEventKind::ScrollLeft => 66u16,
+ MouseEventKind::ScrollRight => 67u16,
+ _ => return None,
+ };
+
+ let mut cb = button;
+ if modifiers.contains(KeyModifiers::SHIFT) {
+ cb += 4;
+ }
+ if modifiers.contains(KeyModifiers::ALT) {
+ cb += 8;
+ }
+ if modifiers.contains(KeyModifiers::CONTROL) {
+ cb += 16;
+ }
+
+ let column = column as u32 + 1;
+ let row = row as u32 + 1;
+
+ match encoding {
+ vt100::MouseProtocolEncoding::Sgr => {
+ Some(format!("\x1b[<{cb};{column};{row}M").into_bytes())
+ }
+ vt100::MouseProtocolEncoding::Default => {
+ let cb = u8::try_from(cb + 32).ok()?;
+ let column = u8::try_from(column + 32).ok()?;
+ let row = u8::try_from(row + 32).ok()?;
+ Some(vec![0x1b, b'[', b'M', cb, column, row])
+ }
+ vt100::MouseProtocolEncoding::Utf8 => {
+ let mut bytes = Vec::with_capacity(16);
+ bytes.extend_from_slice(b"\x1b[M");
+ push_mouse_codepoint(&mut bytes, cb as u32 + 32)?;
+ push_mouse_codepoint(&mut bytes, column + 32)?;
+ push_mouse_codepoint(&mut bytes, row + 32)?;
+ Some(bytes)
+ }
+ }
+}
+
+fn push_mouse_codepoint(bytes: &mut Vec<u8>, value: u32) -> Option<()> {
+ let ch = char::from_u32(value)?;
+ let mut buf = [0u8; 4];
+ bytes.extend_from_slice(ch.encode_utf8(&mut buf).as_bytes());
+ Some(())
+}
+
/// CSI u encoding: \e[{codepoint};{modifiers}u
/// Used when the child has pushed Kitty keyboard enhancement.
/// Returns None if the key doesn't need CSI u (unmodified basic keys).
diff --git a/src/input/mod.rs b/src/input/mod.rs
index de4330a4..b5ed99b4 100644
--- a/src/input/mod.rs
+++ b/src/input/mod.rs
@@ -2,7 +2,7 @@ mod encode;
mod model;
mod parse;
-pub use encode::{encode_key, encode_terminal_key};
+pub use encode::{encode_cursor_key, encode_key, encode_mouse_scroll, encode_terminal_key};
pub use model::{KeyboardProtocol, TerminalKey};
pub use parse::parse_terminal_key_sequence;
diff --git a/src/input/tests.rs b/src/input/tests.rs
index 0d9d2c74..e6900df7 100644
--- a/src/input/tests.rs
+++ b/src/input/tests.rs
@@ -1,7 +1,8 @@
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, ModifierKeyCode};
use super::{
- encode_key, encode_terminal_key, parse_terminal_key_sequence, KeyboardProtocol, TerminalKey,
+ encode_cursor_key, encode_key, encode_mouse_scroll, encode_terminal_key,
+ parse_terminal_key_sequence, KeyboardProtocol, TerminalKey,
};
fn assert_terminal_key_eq(
@@ -143,6 +144,32 @@ fn legacy_alt_char_still_esc_prefix() {
}
#[test]
+fn application_cursor_keys_use_ss3_sequences() {
+ assert_eq!(encode_cursor_key(KeyCode::Up, true), b"\x1bOA");
+ assert_eq!(encode_cursor_key(KeyCode::Down, true), b"\x1bOB");
+}
+
+#[test]
+fn normal_cursor_keys_use_csi_sequences() {
+ assert_eq!(encode_cursor_key(KeyCode::Up, false), b"\x1b[A");
+ assert_eq!(encode_cursor_key(KeyCode::Down, false), b"\x1b[B");
+}
+
+#[test]
+fn sgr_mouse_scroll_encodes_wheel_button_and_coordinates() {
+ let encoded = encode_mouse_scroll(
+ crossterm::event::MouseEventKind::ScrollDown,
+ 4,
+ 6,
+ KeyModifiers::SHIFT,
+ vt100::MouseProtocolEncoding::Sgr,
+ )
+ .expect("mouse scroll should encode");
+
+ assert_eq!(encoded, b"\x1b[<69;5;7M");
+}
+
+#[test]
fn kitty_shift_enter() {
let key = KeyEvent::new(KeyCode::Enter, KeyModifiers::SHIFT);
assert_eq!(