aboutsummaryrefslogtreecommitdiff
path: root/src/raw_input.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-15 00:59:38 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-15 00:59:38 +0300
commit5d24d0d214d05858e344a9e15a63856dc1328eae (patch)
treebcd720acb376e453b0904a41b8c544af7fbf7b10 /src/raw_input.rs
parentc9676e46ad9298c49bcc66981b2c405936433809 (diff)
fix: preserve escape before sgr mouse reports
refs #1334
Diffstat (limited to 'src/raw_input.rs')
-rw-r--r--src/raw_input.rs48
1 files changed, 48 insertions, 0 deletions
diff --git a/src/raw_input.rs b/src/raw_input.rs
index 445b4be3..848df88d 100644
--- a/src/raw_input.rs
+++ b/src/raw_input.rs
@@ -766,6 +766,15 @@ fn complete_escape_sequence_len(buffer: &[u8]) -> Option<usize> {
return None;
}
+ if buffer.starts_with(b"\x1b\x1b[<") {
+ if let Some(mouse_len) = find_csi_final(&buffer[1..], b"Mm") {
+ let mouse_sequence = std::str::from_utf8(&buffer[1..1 + mouse_len]).ok()?;
+ if parse_sgr_mouse(mouse_sequence).is_some() {
+ return Some(1);
+ }
+ }
+ }
+
if buffer.starts_with(b"\x1b\x1b") {
return complete_escape_sequence_len(&buffer[1..]).map(|len| len + 1);
}
@@ -1573,6 +1582,45 @@ mod tests {
}
#[test]
+ fn lone_escape_then_complete_sgr_mouse_report_emits_both_events() {
+ for report in [b"\x1b[<35;10;20M".as_slice(), b"\x1b[<35;10;20m".as_slice()] {
+ let mut framer = RawInputFramer::default();
+
+ assert!(framer.push(b"\x1b").is_empty());
+ let events = framer.push(report);
+
+ assert_eq!(events.len(), 2);
+ let mut events = events.into_iter();
+ assert_raw_key(events.next().unwrap(), KeyCode::Esc, KeyModifiers::empty());
+ assert!(matches!(
+ events.next().unwrap(),
+ RawInputEvent::Mouse(MouseEvent {
+ kind: MouseEventKind::Moved,
+ column: 9,
+ row: 19,
+ ..
+ })
+ ));
+ assert!(framer.flush_timeout().is_empty());
+ }
+ }
+
+ #[test]
+ fn legacy_doubled_escape_alt_arrow_remains_one_event() {
+ let mut framer = RawInputFramer::default();
+
+ let events = framer.push(b"\x1b\x1b[A");
+
+ assert_eq!(events.len(), 1);
+ assert_raw_key(
+ events.into_iter().next().unwrap(),
+ KeyCode::Up,
+ KeyModifiers::ALT,
+ );
+ assert!(framer.flush_timeout().is_empty());
+ }
+
+ #[test]
fn legacy_reader_extends_only_incomplete_sgr_mouse_timeout() {
let mut mouse = RawInputFramer::default();
assert!(mouse.push(b"\x1b[<3").is_empty());