1use vt_push_parser::event::{CSI, SS2, SS3, VTEvent};
2use vt_push_parser::{VTPushParser, capture};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum PasteEvent {
6 Start,
7 End,
8 Continue,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum MouseButton {
13 Left,
14 Middle,
15 Right,
16 WheelUp,
17 WheelDown,
18 WheelLeft,
19 WheelRight,
20 Extra(u8),
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum MouseEventKind {
25 Press(MouseButton),
26 Release(MouseButton),
27 Drag(MouseButton),
28 Motion,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct MouseEvent {
33 pub kind: MouseEventKind,
34 pub x: u16,
35 pub y: u16,
36 pub modifiers: Modifier,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum InputEvent<'a> {
41 Key(keys::Key, Modifier),
43 KeyChar(char, Modifier),
46 Char(char),
48 Mouse(MouseEvent),
50 Paste(PasteEvent, &'a [u8]),
54 Csi(CSI<'a>),
56 Osc(&'a [u8]),
58 Dcs(&'a [u8]),
60 Ss2(SS2),
62 Ss3(SS3),
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
68pub enum InputReport {
69 CursorPosition(u16, u16),
71 CursorPositionPage(u16, u16, u16),
73 DeviceStatus(bool, u16),
75 DeviceStatus2(bool, u16, u16),
77 DeviceAttributes1(u8, u32),
79 DeviceAttributes2(u16, u16, u8),
81 ReportMode(bool, u16, u8),
83 ReportWindowState(bool),
85 ReportWindowPosition(u16, u16),
87 ReportWindowSizePixel(u16, u16),
89 ReportTextSizeCharacter(u16, u16),
91 ReportScreenSizeCharacter(u16, u16),
93 ReportDynamicColor(u8, u16, u16, u16),
95 FocusIn,
97 FocusOut,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102#[repr(transparent)]
103pub struct Modifier(pub(crate) u8);
104
105bitflags::bitflags! {
106 impl Modifier: u8 {
107 const SHIFT = 1 << 0;
108 const ALT = 1 << 1;
109 const CTRL = 1 << 2;
110
111 const KEYPAD = 1 << 3;
112 const SUN = 1 << 4;
113 const VT52 = 1 << 5;
114 }
115}
116
117mod keys {
118 use super::Modifier;
119
120 macro_rules! e {
121 ($depth:literal, $c:literal, ($($mod:ident),*)) => {
122 MatchResult::Match {
123 length: $depth,
124 what: pack_mod($c as _, (Modifier::empty() $( | Modifier::$mod )*).0),
125 }
126 };
127 ($depth:literal, $name:ident, ($($mod:ident),*)) => {
128 MatchResult::Match {
129 length: $depth,
130 what: pack_mod(Key::$name as _, 0x80 | (Modifier::empty() $( | Modifier::$mod )*).0),
131 }
132 };
133 }
134
135 #[derive(Debug, Clone, PartialEq, Eq)]
137 pub(crate) enum MatchResult {
138 NoMatch {
141 length: u8,
142 },
143 PendingMatch,
144 Match {
145 length: u8,
146 what: u32,
147 },
148 }
149
150 #[inline(always)]
151 const fn pack(char: char, alt: bool) -> u32 {
152 char as u32 | (((if alt { Modifier::ALT.0 as _ } else { 0 }) as u32) << 24)
153 }
154
155 #[inline(always)]
156 const fn pack_mod(char: u32, modifiers: u8) -> u32 {
157 char | (modifiers as u32) << 24
158 }
159
160 #[inline(always)]
161 fn utf8_1(alt: bool, byte: u8) -> MatchResult {
162 MatchResult::Match {
163 length: 1 + alt as u8,
164 what: pack(byte as char, alt),
165 }
166 }
167
168 #[inline(always)]
169 fn utf8_2(alt: bool, bytes: &[u8]) -> MatchResult {
170 if bytes.len() < 2 {
171 return MatchResult::PendingMatch;
172 }
173 let Some(char) =
175 char::from_u32((bytes[0] & 0b11111) as u32 | ((bytes[1] & 0b111111) as u32) << 6)
176 else {
177 return MatchResult::Match {
178 length: 2 + alt as u8,
179 what: pack('\u{FFFD}', false),
180 };
181 };
182 MatchResult::Match {
183 length: 2 + alt as u8,
184 what: pack(char, alt),
185 }
186 }
187
188 #[inline(always)]
189 fn utf8_3(alt: bool, bytes: &[u8]) -> MatchResult {
190 if bytes.len() < 3 {
191 return MatchResult::PendingMatch;
192 }
193 let Some(char) = char::from_u32(
195 (((bytes[0] & 0b1111) as u32) << 12)
196 | (((bytes[1] & 0b111111) as u32) << 6)
197 | ((bytes[2] & 0b111111) as u32),
198 ) else {
199 return MatchResult::Match {
200 length: 3 + alt as u8,
201 what: pack('\u{FFFD}', false),
202 };
203 };
204 MatchResult::Match {
205 length: 3 + alt as u8,
206 what: pack(char, alt),
207 }
208 }
209
210 #[inline(always)]
211 fn utf8_4(alt: bool, bytes: &[u8]) -> MatchResult {
212 if bytes.len() < 4 {
213 return MatchResult::PendingMatch;
214 }
215 let Some(char) = char::from_u32(
217 (((bytes[0] & 0b111) as u32) << 18)
218 | (((bytes[1] & 0b111111) as u32) << 12)
219 | (((bytes[2] & 0b111111) as u32) << 6)
220 | ((bytes[3] & 0b111111) as u32),
221 ) else {
222 return MatchResult::Match {
223 length: 4 + alt as u8,
224 what: pack('\u{FFFD}', false),
225 };
226 };
227 MatchResult::Match {
228 length: 4 + alt as u8,
229 what: pack(char, alt),
230 }
231 }
232
233 #[inline(always)]
234 fn invalid_utf8(_: u8) -> MatchResult {
235 MatchResult::Match {
236 length: 1,
237 what: pack('\u{FFFD}', false),
238 }
239 }
240
241 pub fn find_sequence(bytes: &[u8]) -> MatchResult {
242 match find_sequence_nonidle_gen(bytes) {
243 MatchResult::NoMatch { length } => {
244 if matches!(bytes, [0x1b, 0x1b, ..]) {
245 MatchResult::Match {
246 length: 1,
247 what: pack('\x1b', false),
248 }
249 } else {
250 MatchResult::NoMatch { length }
251 }
252 }
253 res => res,
254 }
255 }
256
257 #[allow(unused)]
258 pub fn find_sequence_idle(bytes: &[u8]) -> MatchResult {
259 match find_sequence_idle_gen(bytes) {
260 MatchResult::NoMatch { length } => match bytes {
261 [0x1b] => MatchResult::Match {
262 length: 1,
263 what: pack('\x1b', false),
264 },
265 [0x1b, 0x1b] => MatchResult::Match {
266 length: 2,
267 what: pack('\x1b', true),
268 },
269 [0x1b, 0x1b, ..] => MatchResult::Match {
270 length: 1,
271 what: pack('\x1b', false),
272 },
273 _ => MatchResult::NoMatch { length },
274 },
275 res => res,
276 }
277 }
278
279 include!(concat!(env!("OUT_DIR"), "/keys.rs"));
280}
281
282#[derive(Debug, Default)]
283enum CaptureState {
284 #[default]
285 None,
286 #[allow(dead_code)]
287 Paste,
288 Mouse,
289}
290
291fn decode_mouse_button_byte(cb: u16) -> (MouseButton, Modifier, bool) {
295 let modifiers = Modifier::from_bits_truncate(
296 (if cb & 0x04 != 0 { Modifier::SHIFT.0 } else { 0 })
297 | (if cb & 0x08 != 0 { Modifier::ALT.0 } else { 0 })
298 | (if cb & 0x10 != 0 { Modifier::CTRL.0 } else { 0 }),
299 );
300 let is_motion = cb & 0x20 != 0;
301 let button_bits = cb & 0xC3; let button = match button_bits {
303 0 => MouseButton::Left,
304 1 => MouseButton::Middle,
305 2 => MouseButton::Right,
306 64 => MouseButton::WheelUp,
307 65 => MouseButton::WheelDown,
308 66 => MouseButton::WheelLeft,
309 67 => MouseButton::WheelRight,
310 n => MouseButton::Extra(n as u8),
311 };
312 (button, modifiers, is_motion)
313}
314
315enum HandleAction<'a> {
316 Handled,
317 Capture(CaptureState, capture::VTCaptureInternal),
318 Unhandled(VTEvent<'a>),
319}
320
321pub struct VTPushParserInput {
329 key_buffer: [u8; keys::MAX_SEQUENCE_LEN],
330 key_buffer_len: usize,
331 data_accumulator: Vec<u8>,
332
333 capture: capture::VTCaptureInternal,
334 capture_state: CaptureState,
335 parser: VTPushParser,
336}
337
338fn handle_vt_event<'a>(event: VTEvent<'a>, cb: &mut impl FnMut(InputEvent)) -> HandleAction<'a> {
339 if let VTEvent::Csi(ref csi) = event {
340 match (csi.private, csi.final_byte) {
341 (Some(b'<'), b'M') => {
343 if let (Some(pb), Some(px), Some(py)) = (
344 csi.params.try_parse::<u16>(0),
345 csi.params.try_parse::<u16>(1),
346 csi.params.try_parse::<u16>(2),
347 ) {
348 let (button, modifiers, is_motion) = decode_mouse_button_byte(pb);
349 let kind = if is_motion {
350 MouseEventKind::Drag(button)
351 } else {
352 MouseEventKind::Press(button)
353 };
354 cb(InputEvent::Mouse(MouseEvent {
355 kind,
356 x: px.saturating_sub(1),
357 y: py.saturating_sub(1),
358 modifiers,
359 }));
360 return HandleAction::Handled;
361 }
362 }
363 (Some(b'<'), b'm') => {
365 if let (Some(pb), Some(px), Some(py)) = (
366 csi.params.try_parse::<u16>(0),
367 csi.params.try_parse::<u16>(1),
368 csi.params.try_parse::<u16>(2),
369 ) {
370 let (button, modifiers, _) = decode_mouse_button_byte(pb);
371 cb(InputEvent::Mouse(MouseEvent {
372 kind: MouseEventKind::Release(button),
373 x: px.saturating_sub(1),
374 y: py.saturating_sub(1),
375 modifiers,
376 }));
377 return HandleAction::Handled;
378 }
379 }
380 (None, b'M') => {
382 if csi.params.is_empty() {
383 return HandleAction::Capture(
385 CaptureState::Mouse,
386 capture::VTCaptureInternal::Count(3),
387 );
388 } else if let (Some(pb), Some(px), Some(py)) = (
389 csi.params.try_parse::<u16>(0),
390 csi.params.try_parse::<u16>(1),
391 csi.params.try_parse::<u16>(2),
392 ) {
393 let (button, modifiers, is_motion) = decode_mouse_button_byte(pb);
395 let kind = if is_motion {
396 MouseEventKind::Drag(button)
397 } else {
398 MouseEventKind::Press(button)
399 };
400 cb(InputEvent::Mouse(MouseEvent {
401 kind,
402 x: px.saturating_sub(1),
403 y: py.saturating_sub(1),
404 modifiers,
405 }));
406 return HandleAction::Handled;
407 }
408 }
409 (None, b'u') => match csi.params.len() {
410 1 => {}
411 2 => {}
412 _ => {}
413 },
414 (None | Some(b'>'), b'~') => match csi.params.len() {
415 2 => {}
416 3 => {}
417 _ => {}
418 },
419 _ => {}
420 }
421 }
422
423 HandleAction::Unhandled(event)
424}
425
426fn handle_key_event(what: u32, mut cb: impl FnMut(InputEvent)) {
427 if what & 0x80000000 != 0 {
428 let key = keys::Key::try_from(what as u8).unwrap();
429 cb(InputEvent::Key(key, Modifier(((what >> 24) & !0x80) as u8)));
430 } else {
431 cb(InputEvent::KeyChar(
432 char::from_u32(what & 0xffffff).unwrap(),
433 Modifier((what >> 24) as u8),
434 ));
435 }
436}
437
438impl Default for VTPushParserInput {
439 fn default() -> Self {
440 Self::new()
441 }
442}
443
444impl VTPushParserInput {
445 pub fn new() -> Self {
446 Self {
447 key_buffer: [0; keys::MAX_SEQUENCE_LEN],
448 key_buffer_len: 0,
449 capture: capture::VTCaptureInternal::None,
450 capture_state: CaptureState::None,
451 parser: VTPushParser::new(),
452 data_accumulator: Vec::with_capacity(256),
453 }
454 }
455
456 pub fn feed_with(&mut self, mut bytes: &[u8], mut cb: impl FnMut(InputEvent)) {
457 macro_rules! dispatch_vt {
460 ($action:expr, $cb:expr, $capture:expr, $capture_state:expr) => {
461 match $action {
462 HandleAction::Handled => {}
463 HandleAction::Capture(state, internal) => {
464 *$capture = internal;
465 *$capture_state = state;
466 }
467 HandleAction::Unhandled(event) => {
468 let input_event = match event {
469 VTEvent::Csi(csi) => Some(InputEvent::Csi(csi)),
470 VTEvent::OscEnd { data, .. } => Some(InputEvent::Osc(data)),
471 VTEvent::DcsEnd(data) => Some(InputEvent::Dcs(data)),
472 VTEvent::Ss2(ss2) => Some(InputEvent::Ss2(ss2)),
473 VTEvent::Ss3(ss3) => Some(InputEvent::Ss3(ss3)),
474 _ => None,
475 };
476 if let Some(e) = input_event {
477 $cb(e);
478 }
479 }
480 }
481 };
482 }
483
484 loop {
485 if let Some(captured) = self.capture.feed(&mut bytes) {
487 self.data_accumulator.extend_from_slice(captured);
488 match std::mem::take(&mut self.capture_state) {
489 CaptureState::None => {}
490 CaptureState::Paste => {
491 cb(InputEvent::Paste(PasteEvent::End, &self.data_accumulator));
492 self.data_accumulator.clear();
493 }
494 CaptureState::Mouse => {
495 let data = &self.data_accumulator;
496 if data.len() >= 3 {
497 let cb_byte = (data[0] as u16).saturating_sub(32);
499 let x = (data[1] as u16).saturating_sub(33);
500 let y = (data[2] as u16).saturating_sub(33);
501 let is_release = cb_byte & 0x03 == 3;
503 let (button, modifiers, is_motion) = decode_mouse_button_byte(cb_byte);
504 let kind = if is_release {
505 MouseEventKind::Release(MouseButton::Left)
507 } else if is_motion {
508 MouseEventKind::Drag(button)
509 } else {
510 MouseEventKind::Press(button)
511 };
512 cb(InputEvent::Mouse(MouseEvent {
513 kind,
514 x,
515 y,
516 modifiers,
517 }));
518 }
519 self.data_accumulator.clear();
520 }
521 }
522 continue;
523 }
524
525 if !self.parser.is_ground() {
527 let capture = &mut self.capture;
528 let capture_state = &mut self.capture_state;
529 let read = self.parser.feed_with_abortable(bytes, |event: VTEvent| {
530 let action = handle_vt_event(event, &mut cb);
531 dispatch_vt!(action, cb, capture, capture_state);
532 false
533 });
534 bytes = &bytes[read..];
535 continue;
536 }
537
538 if self.key_buffer_len > 0 {
540 let to_copy_into = &mut self.key_buffer[self.key_buffer_len
541 ..keys::MAX_SEQUENCE_LEN.min(self.key_buffer_len + bytes.len())];
542 to_copy_into.copy_from_slice(&bytes[..to_copy_into.len()]);
543 self.key_buffer_len += to_copy_into.len();
544 bytes = &bytes[to_copy_into.len()..];
545
546 match keys::find_sequence(&self.key_buffer[..self.key_buffer_len]) {
547 keys::MatchResult::Match { length, what } => {
548 self.key_buffer.rotate_left(length as _);
549 self.key_buffer_len -= length as usize;
550 handle_key_event(what, &mut cb);
551 }
552 keys::MatchResult::NoMatch { length } => {
553 self.key_buffer.rotate_left(length as _);
556 let capture = &mut self.capture;
557 let capture_state = &mut self.capture_state;
558 self.parser.feed_with_abortable(
560 &self.key_buffer[keys::MAX_SEQUENCE_LEN - length as usize..],
561 |event: VTEvent<'_>| {
562 let action = handle_vt_event(event, &mut cb);
563 dispatch_vt!(action, cb, capture, capture_state);
564 false
565 },
566 );
567 }
568 keys::MatchResult::PendingMatch => {
569 return;
572 }
573 }
574 }
575
576 if self.key_buffer_len == 0 {
579 match keys::find_sequence(bytes) {
580 keys::MatchResult::Match { length, what } => {
581 bytes = &bytes[length as usize..];
582 handle_key_event(what, &mut cb);
583 }
584 keys::MatchResult::NoMatch { length } => {
585 let capture = &mut self.capture;
588 let capture_state = &mut self.capture_state;
589 self.parser.feed_with_abortable(
590 &bytes[..length as usize],
591 |event: VTEvent<'_>| {
592 let action = handle_vt_event(event, &mut cb);
593 dispatch_vt!(action, cb, capture, capture_state);
594 false
595 },
596 );
597 bytes = &bytes[length as usize..];
598 }
599 keys::MatchResult::PendingMatch => {
600 return;
603 }
604 }
605 }
606 }
607 }
608}
609
610#[cfg(test)]
611mod tests {
612 use super::*;
613
614 #[test]
615 fn test_codegen() {
616 use keys::{find_sequence, find_sequence_idle};
617
618 macro_rules! m {
619 ($bytes:literal, $length:literal) => {
620 let seq = find_sequence($bytes);
621 assert!(
622 matches!(
623 seq,
624 keys::MatchResult::Match {
625 length: $length,
626 what: _
627 }
628 ),
629 "{:?} => {seq:?}, expected match when not idle",
630 $bytes
631 );
632 let seq = find_sequence_idle($bytes);
633 assert!(
634 matches!(
635 seq,
636 keys::MatchResult::Match {
637 length: $length,
638 what: _
639 }
640 ),
641 "{:?} => {seq:?}, expected match when idle",
642 $bytes
643 );
644 };
645 ($bytes:literal, no) => {
646 let seq = find_sequence($bytes);
647 assert!(
648 matches!(seq, keys::MatchResult::NoMatch { length: _ }),
649 "{:?} => {seq:?}, expected no match",
650 $bytes
651 );
652 };
653 ($bytes:literal, no, $length:literal) => {
654 let seq = find_sequence($bytes);
655 assert!(
656 matches!(seq, keys::MatchResult::PendingMatch),
657 "{:?} => {seq:?}, expected pending match when not idle",
658 $bytes
659 );
660 let seq = find_sequence_idle($bytes);
661 assert!(
662 matches!(
663 seq,
664 keys::MatchResult::Match {
665 length: $length,
666 what: _
667 }
668 ),
669 "{:?} => {seq:?}, expected match when idle",
670 $bytes
671 );
672 };
673 }
674
675 m!(b"\x1b[A", 3);
677 m!(b"\x1bA", 2);
678 m!(b"\x1bOA", 3);
679 m!(b"\x1b\x01", 2);
680 m!(b"\x1b\x1b[Z", 4);
681 m!(b"\x1b1", 2); m!(b"\x1b", no, 1); m!(b"\x1b\x1b", no, 2); m!(b"\x1bO", no, 2); m!(b"\x1b?", no, 2); m!(b"\x1b\x1b\x1b", 1); m!(b"\x1b\x1b\x1b\x1b", 1); m!(b"\x1b!\x1b", 2); m!(b"\x1b\x1b1", 1); m!(b"\x1b1\x1b", 2); m!(b"\x1b1m", 2); m!(b"\x1b\x1b[I", 1); m!(b"\x1b\x1bON", 1); m!(b"\x1b[", no, 2); m!(b"\x1b\x1b[", no, 1); m!(b"\x1b[I", no); m!(b"\x1b=", no);
708 m!(b"\x1b<", no);
709 m!(b"\x1b>", no);
710
711 m!(b"\x1bO\x1b", no); }
714
715 #[test]
716 fn test_find_sequence() {
717 let bytes = b"\x1ba\x1b[Aa\xf0\x9f\x9b\x9c\x1b[B".as_slice();
718 let mut input_parser = VTPushParserInput::new();
719 let mut events = vec![];
720 input_parser.feed_with(bytes, |event| {
721 events.push(format!("{event:?}"));
722 });
723 assert!(!events.is_empty());
724 }
725
726 fn collect_events(bytes: &[u8]) -> Vec<InputEvent<'static>> {
727 let mut events = vec![];
728 let mut input_parser = VTPushParserInput::new();
729 input_parser.feed_with(bytes, |event| {
730 let event: InputEvent<'static> = unsafe { std::mem::transmute(event) };
732 events.push(event);
733 });
734 events
735 }
736
737 #[test]
740 fn test_sgr_mouse_left_click() {
741 let events = collect_events(b"\x1b[<0;10;20M");
743 assert_eq!(events.len(), 1);
744 assert_eq!(
745 events[0],
746 InputEvent::Mouse(MouseEvent {
747 kind: MouseEventKind::Press(MouseButton::Left),
748 x: 9,
749 y: 19,
750 modifiers: Modifier::empty(),
751 })
752 );
753 }
754
755 #[test]
756 fn test_sgr_mouse_right_click() {
757 let events = collect_events(b"\x1b[<2;5;3M");
759 assert_eq!(events.len(), 1);
760 assert_eq!(
761 events[0],
762 InputEvent::Mouse(MouseEvent {
763 kind: MouseEventKind::Press(MouseButton::Right),
764 x: 4,
765 y: 2,
766 modifiers: Modifier::empty(),
767 })
768 );
769 }
770
771 #[test]
772 fn test_sgr_mouse_middle_click() {
773 let events = collect_events(b"\x1b[<1;1;1M");
775 assert_eq!(events.len(), 1);
776 assert_eq!(
777 events[0],
778 InputEvent::Mouse(MouseEvent {
779 kind: MouseEventKind::Press(MouseButton::Middle),
780 x: 0,
781 y: 0,
782 modifiers: Modifier::empty(),
783 })
784 );
785 }
786
787 #[test]
788 fn test_sgr_mouse_release() {
789 let events = collect_events(b"\x1b[<0;10;20m");
791 assert_eq!(events.len(), 1);
792 assert_eq!(
793 events[0],
794 InputEvent::Mouse(MouseEvent {
795 kind: MouseEventKind::Release(MouseButton::Left),
796 x: 9,
797 y: 19,
798 modifiers: Modifier::empty(),
799 })
800 );
801 }
802
803 #[test]
804 fn test_sgr_mouse_wheel_up() {
805 let events = collect_events(b"\x1b[<64;10;20M");
807 assert_eq!(events.len(), 1);
808 assert_eq!(
809 events[0],
810 InputEvent::Mouse(MouseEvent {
811 kind: MouseEventKind::Press(MouseButton::WheelUp),
812 x: 9,
813 y: 19,
814 modifiers: Modifier::empty(),
815 })
816 );
817 }
818
819 #[test]
820 fn test_sgr_mouse_wheel_down() {
821 let events = collect_events(b"\x1b[<65;10;20M");
823 assert_eq!(events.len(), 1);
824 assert_eq!(
825 events[0],
826 InputEvent::Mouse(MouseEvent {
827 kind: MouseEventKind::Press(MouseButton::WheelDown),
828 x: 9,
829 y: 19,
830 modifiers: Modifier::empty(),
831 })
832 );
833 }
834
835 #[test]
836 fn test_sgr_mouse_drag() {
837 let events = collect_events(b"\x1b[<32;15;25M");
839 assert_eq!(events.len(), 1);
840 assert_eq!(
841 events[0],
842 InputEvent::Mouse(MouseEvent {
843 kind: MouseEventKind::Drag(MouseButton::Left),
844 x: 14,
845 y: 24,
846 modifiers: Modifier::empty(),
847 })
848 );
849 }
850
851 #[test]
852 fn test_sgr_mouse_shift_click() {
853 let events = collect_events(b"\x1b[<4;10;20M");
855 assert_eq!(events.len(), 1);
856 assert_eq!(
857 events[0],
858 InputEvent::Mouse(MouseEvent {
859 kind: MouseEventKind::Press(MouseButton::Left),
860 x: 9,
861 y: 19,
862 modifiers: Modifier::SHIFT,
863 })
864 );
865 }
866
867 #[test]
868 fn test_sgr_mouse_ctrl_click() {
869 let events = collect_events(b"\x1b[<16;10;20M");
871 assert_eq!(events.len(), 1);
872 assert_eq!(
873 events[0],
874 InputEvent::Mouse(MouseEvent {
875 kind: MouseEventKind::Press(MouseButton::Left),
876 x: 9,
877 y: 19,
878 modifiers: Modifier::CTRL,
879 })
880 );
881 }
882
883 #[test]
884 fn test_sgr_mouse_alt_click() {
885 let events = collect_events(b"\x1b[<8;10;20M");
887 assert_eq!(events.len(), 1);
888 assert_eq!(
889 events[0],
890 InputEvent::Mouse(MouseEvent {
891 kind: MouseEventKind::Press(MouseButton::Left),
892 x: 9,
893 y: 19,
894 modifiers: Modifier::ALT,
895 })
896 );
897 }
898
899 #[test]
900 fn test_sgr_mouse_large_coordinates() {
901 let events = collect_events(b"\x1b[<0;500;300M");
903 assert_eq!(events.len(), 1);
904 assert_eq!(
905 events[0],
906 InputEvent::Mouse(MouseEvent {
907 kind: MouseEventKind::Press(MouseButton::Left),
908 x: 499,
909 y: 299,
910 modifiers: Modifier::empty(),
911 })
912 );
913 }
914
915 #[test]
918 fn test_urxvt_mouse_left_click() {
919 let events = collect_events(b"\x1b[0;10;20M");
921 assert_eq!(events.len(), 1);
922 assert_eq!(
923 events[0],
924 InputEvent::Mouse(MouseEvent {
925 kind: MouseEventKind::Press(MouseButton::Left),
926 x: 9,
927 y: 19,
928 modifiers: Modifier::empty(),
929 })
930 );
931 }
932
933 #[test]
936 fn test_x10_mouse_left_click() {
937 let events = collect_events(b"\x1b[M\x20\x2a\x34");
939 assert_eq!(events.len(), 1);
940 assert_eq!(
941 events[0],
942 InputEvent::Mouse(MouseEvent {
943 kind: MouseEventKind::Press(MouseButton::Left),
944 x: 9,
945 y: 19,
946 modifiers: Modifier::empty(),
947 })
948 );
949 }
950
951 #[test]
952 fn test_x10_mouse_right_click() {
953 let events = collect_events(b"\x1b[M\x22\x25\x23");
955 assert_eq!(events.len(), 1);
956 assert_eq!(
957 events[0],
958 InputEvent::Mouse(MouseEvent {
959 kind: MouseEventKind::Press(MouseButton::Right),
960 x: 4,
961 y: 2,
962 modifiers: Modifier::empty(),
963 })
964 );
965 }
966
967 #[test]
968 fn test_x10_mouse_release() {
969 let events = collect_events(b"\x1b[M\x23\x2a\x34");
971 assert_eq!(events.len(), 1);
972 assert_eq!(
973 events[0],
974 InputEvent::Mouse(MouseEvent {
975 kind: MouseEventKind::Release(MouseButton::Left),
976 x: 9,
977 y: 19,
978 modifiers: Modifier::empty(),
979 })
980 );
981 }
982
983 #[test]
984 fn test_x10_mouse_ctrl_click() {
985 let events = collect_events(b"\x1b[M\x30\x2a\x34");
987 assert_eq!(events.len(), 1);
988 assert_eq!(
989 events[0],
990 InputEvent::Mouse(MouseEvent {
991 kind: MouseEventKind::Press(MouseButton::Left),
992 x: 9,
993 y: 19,
994 modifiers: Modifier::CTRL,
995 })
996 );
997 }
998
999 #[test]
1000 fn test_sgr_mouse_followed_by_key() {
1001 let events = collect_events(b"\x1b[<0;10;20Ma");
1003 assert_eq!(events.len(), 2);
1004 assert_eq!(
1005 events[0],
1006 InputEvent::Mouse(MouseEvent {
1007 kind: MouseEventKind::Press(MouseButton::Left),
1008 x: 9,
1009 y: 19,
1010 modifiers: Modifier::empty(),
1011 })
1012 );
1013 assert_eq!(events[1], InputEvent::KeyChar('a', Modifier::empty()));
1014 }
1015}