1use std::io;
17use std::str;
18
19use log::{debug, trace};
20use serde::{Deserialize, Serialize};
21
22use crate::index::{Column, Line};
23use crate::term::color::Rgb;
24
25fn xparse_color(color: &[u8]) -> Option<Rgb> {
27 if !color.is_empty() && color[0] == b'#' {
28 parse_legacy_color(&color[1..])
29 } else if color.len() >= 4 && &color[..4] == b"rgb:" {
30 parse_rgb_color(&color[4..])
31 } else {
32 None
33 }
34}
35
36fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
38 let colors = str::from_utf8(color).ok()?.split('/').collect::<Vec<_>>();
39
40 if colors.len() != 3 {
41 return None;
42 }
43
44 let scale = |input: &str| {
46 let max = u32::pow(16, input.len() as u32) - 1;
47 let value = u32::from_str_radix(input, 16).ok()?;
48 Some((255 * value / max) as u8)
49 };
50
51 Some(Rgb { r: scale(colors[0])?, g: scale(colors[1])?, b: scale(colors[2])? })
52}
53
54fn parse_legacy_color(color: &[u8]) -> Option<Rgb> {
56 let item_len = color.len() / 3;
57
58 let color_from_slice = |slice: &[u8]| {
60 let col = usize::from_str_radix(str::from_utf8(slice).ok()?, 16).ok()? << 4;
61 Some((col >> (4 * slice.len().saturating_sub(1))) as u8)
62 };
63
64 Some(Rgb {
65 r: color_from_slice(&color[0..item_len])?,
66 g: color_from_slice(&color[item_len..item_len * 2])?,
67 b: color_from_slice(&color[item_len * 2..])?,
68 })
69}
70
71fn parse_number(input: &[u8]) -> Option<u8> {
72 if input.is_empty() {
73 return None;
74 }
75 let mut num: u8 = 0;
76 for c in input {
77 let c = *c as char;
78 if let Some(digit) = c.to_digit(10) {
79 num = match num.checked_mul(10).and_then(|v| v.checked_add(digit as u8)) {
80 Some(v) => v,
81 None => return None,
82 }
83 } else {
84 return None;
85 }
86 }
87 Some(num)
88}
89
90pub struct Processor {
92 state: ProcessorState,
93 parser: vte::Parser,
94}
95
96struct ProcessorState {
98 preceding_char: Option<char>,
99}
100
101struct Performer<'a, H: Handler + TermInfo, W: io::Write> {
106 state: &'a mut ProcessorState,
107 handler: &'a mut H,
108 writer: &'a mut W,
109}
110
111impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> {
112 #[inline]
114 pub fn new<'b>(
115 state: &'b mut ProcessorState,
116 handler: &'b mut H,
117 writer: &'b mut W,
118 ) -> Performer<'b, H, W> {
119 Performer { state, handler, writer }
120 }
121}
122
123impl Default for Processor {
124 fn default() -> Processor {
125 Processor { state: ProcessorState { preceding_char: None }, parser: vte::Parser::new() }
126 }
127}
128
129impl Processor {
130 pub fn new() -> Processor {
131 Default::default()
132 }
133
134 #[inline]
135 pub fn advance<H, W>(&mut self, handler: &mut H, byte: u8, writer: &mut W)
136 where
137 H: Handler + TermInfo,
138 W: io::Write,
139 {
140 let mut performer = Performer::new(&mut self.state, handler, writer);
141 self.parser.advance(&mut performer, byte);
142 }
143}
144
145pub trait TermInfo {
147 fn lines(&self) -> Line;
148 fn cols(&self) -> Column;
149}
150
151pub trait Handler {
156 fn set_title(&mut self, _: &str) {}
158
159 fn set_cursor_style(&mut self, _: Option<CursorStyle>) {}
161
162 fn input(&mut self, _c: char) {}
164
165 fn goto(&mut self, _: Line, _: Column) {}
167
168 fn goto_line(&mut self, _: Line) {}
170
171 fn goto_col(&mut self, _: Column) {}
173
174 fn insert_blank(&mut self, _: Column) {}
176
177 fn move_up(&mut self, _: Line) {}
179
180 fn move_down(&mut self, _: Line) {}
182
183 fn identify_terminal<W: io::Write>(&mut self, _: &mut W) {}
187
188 fn device_status<W: io::Write>(&mut self, _: &mut W, _: usize) {}
190
191 fn move_forward(&mut self, _: Column) {}
193
194 fn move_backward(&mut self, _: Column) {}
196
197 fn move_down_and_cr(&mut self, _: Line) {}
199
200 fn move_up_and_cr(&mut self, _: Line) {}
202
203 fn put_tab(&mut self, _count: i64) {}
205
206 fn backspace(&mut self) {}
208
209 fn carriage_return(&mut self) {}
211
212 fn linefeed(&mut self) {}
214
215 fn bell(&mut self) {}
219
220 fn substitute(&mut self) {}
222
223 fn newline(&mut self) {}
225
226 fn set_horizontal_tabstop(&mut self) {}
228
229 fn scroll_up(&mut self, _: Line) {}
231
232 fn scroll_down(&mut self, _: Line) {}
234
235 fn insert_blank_lines(&mut self, _: Line) {}
237
238 fn delete_lines(&mut self, _: Line) {}
240
241 fn erase_chars(&mut self, _: Column) {}
246
247 fn delete_chars(&mut self, _: Column) {}
252
253 fn move_backward_tabs(&mut self, _count: i64) {}
255
256 fn move_forward_tabs(&mut self, _count: i64) {}
258
259 fn save_cursor_position(&mut self) {}
261
262 fn restore_cursor_position(&mut self) {}
264
265 fn clear_line(&mut self, _mode: LineClearMode) {}
267
268 fn clear_screen(&mut self, _mode: ClearMode) {}
270
271 fn clear_tabs(&mut self, _mode: TabulationClearMode) {}
273
274 fn reset_state(&mut self) {}
276
277 fn reverse_index(&mut self) {}
283
284 fn terminal_attribute(&mut self, _attr: Attr) {}
286
287 fn set_mode(&mut self, _mode: Mode) {}
289
290 fn unset_mode(&mut self, _: Mode) {}
292
293 fn set_scrolling_region(&mut self, _top: usize, _bottom: usize) {}
295
296 fn set_keypad_application_mode(&mut self) {}
298
299 fn unset_keypad_application_mode(&mut self) {}
301
302 fn set_active_charset(&mut self, _: CharsetIndex) {}
307
308 fn configure_charset(&mut self, _: CharsetIndex, _: StandardCharset) {}
313
314 fn set_color(&mut self, _: usize, _: Rgb) {}
316
317 fn dynamic_color_sequence<W: io::Write>(&mut self, _: &mut W, _: u8, _: usize) {}
319
320 fn reset_color(&mut self, _: usize) {}
322
323 fn set_clipboard(&mut self, _: u8, _: &[u8]) {}
325
326 fn write_clipboard<W: io::Write>(&mut self, _: u8, _: &mut W) {}
328
329 fn decaln(&mut self) {}
331
332 fn push_title(&mut self) {}
334
335 fn pop_title(&mut self) {}
337}
338
339#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, Deserialize)]
341pub enum CursorStyle {
342 Block,
344
345 Underline,
347
348 Beam,
350
351 HollowBlock,
353
354 Hidden,
356}
357
358impl Default for CursorStyle {
359 fn default() -> CursorStyle {
360 CursorStyle::Block
361 }
362}
363
364#[derive(Debug, Eq, PartialEq)]
366pub enum Mode {
367 CursorKeys = 1,
369 DECCOLM = 3,
381 Insert = 4,
388 Origin = 6,
390 LineWrap = 7,
392 BlinkingCursor = 12,
394 LineFeedNewLine = 20,
399 ShowCursor = 25,
401 ReportMouseClicks = 1000,
403 ReportCellMouseMotion = 1002,
405 ReportAllMouseMotion = 1003,
407 ReportFocusInOut = 1004,
409 Utf8Mouse = 1005,
411 SgrMouse = 1006,
413 AlternateScroll = 1007,
415 SwapScreenAndSetRestoreCursor = 1049,
417 BracketedPaste = 2004,
419}
420
421impl Mode {
422 pub fn from_primitive(intermediate: Option<&u8>, num: i64) -> Option<Mode> {
426 let private = match intermediate {
427 Some(b'?') => true,
428 None => false,
429 _ => return None,
430 };
431
432 if private {
433 Some(match num {
434 1 => Mode::CursorKeys,
435 3 => Mode::DECCOLM,
436 6 => Mode::Origin,
437 7 => Mode::LineWrap,
438 12 => Mode::BlinkingCursor,
439 25 => Mode::ShowCursor,
440 1000 => Mode::ReportMouseClicks,
441 1002 => Mode::ReportCellMouseMotion,
442 1003 => Mode::ReportAllMouseMotion,
443 1004 => Mode::ReportFocusInOut,
444 1005 => Mode::Utf8Mouse,
445 1006 => Mode::SgrMouse,
446 1007 => Mode::AlternateScroll,
447 1049 => Mode::SwapScreenAndSetRestoreCursor,
448 2004 => Mode::BracketedPaste,
449 _ => {
450 trace!("[unimplemented] primitive mode: {}", num);
451 return None;
452 },
453 })
454 } else {
455 Some(match num {
456 4 => Mode::Insert,
457 20 => Mode::LineFeedNewLine,
458 _ => return None,
459 })
460 }
461 }
462}
463
464#[derive(Debug)]
468pub enum LineClearMode {
469 Right,
471 Left,
473 All,
475}
476
477#[derive(Debug)]
481pub enum ClearMode {
482 Below,
484 Above,
486 All,
488 Saved,
490}
491
492#[derive(Debug)]
494pub enum TabulationClearMode {
495 Current,
497 All,
499}
500
501#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
506pub enum NamedColor {
507 Black = 0,
509 Red,
511 Green,
513 Yellow,
515 Blue,
517 Magenta,
519 Cyan,
521 White,
523 BrightBlack,
525 BrightRed,
527 BrightGreen,
529 BrightYellow,
531 BrightBlue,
533 BrightMagenta,
535 BrightCyan,
537 BrightWhite,
539 Foreground = 256,
541 Background,
543 Cursor,
545 DimBlack,
547 DimRed,
549 DimGreen,
551 DimYellow,
553 DimBlue,
555 DimMagenta,
557 DimCyan,
559 DimWhite,
561 BrightForeground,
563 DimForeground,
565}
566
567impl NamedColor {
568 pub fn to_bright(self) -> Self {
569 match self {
570 NamedColor::Foreground => NamedColor::BrightForeground,
571 NamedColor::Black => NamedColor::BrightBlack,
572 NamedColor::Red => NamedColor::BrightRed,
573 NamedColor::Green => NamedColor::BrightGreen,
574 NamedColor::Yellow => NamedColor::BrightYellow,
575 NamedColor::Blue => NamedColor::BrightBlue,
576 NamedColor::Magenta => NamedColor::BrightMagenta,
577 NamedColor::Cyan => NamedColor::BrightCyan,
578 NamedColor::White => NamedColor::BrightWhite,
579 NamedColor::DimForeground => NamedColor::Foreground,
580 NamedColor::DimBlack => NamedColor::Black,
581 NamedColor::DimRed => NamedColor::Red,
582 NamedColor::DimGreen => NamedColor::Green,
583 NamedColor::DimYellow => NamedColor::Yellow,
584 NamedColor::DimBlue => NamedColor::Blue,
585 NamedColor::DimMagenta => NamedColor::Magenta,
586 NamedColor::DimCyan => NamedColor::Cyan,
587 NamedColor::DimWhite => NamedColor::White,
588 val => val,
589 }
590 }
591
592 pub fn to_dim(self) -> Self {
593 match self {
594 NamedColor::Black => NamedColor::DimBlack,
595 NamedColor::Red => NamedColor::DimRed,
596 NamedColor::Green => NamedColor::DimGreen,
597 NamedColor::Yellow => NamedColor::DimYellow,
598 NamedColor::Blue => NamedColor::DimBlue,
599 NamedColor::Magenta => NamedColor::DimMagenta,
600 NamedColor::Cyan => NamedColor::DimCyan,
601 NamedColor::White => NamedColor::DimWhite,
602 NamedColor::Foreground => NamedColor::DimForeground,
603 NamedColor::BrightBlack => NamedColor::Black,
604 NamedColor::BrightRed => NamedColor::Red,
605 NamedColor::BrightGreen => NamedColor::Green,
606 NamedColor::BrightYellow => NamedColor::Yellow,
607 NamedColor::BrightBlue => NamedColor::Blue,
608 NamedColor::BrightMagenta => NamedColor::Magenta,
609 NamedColor::BrightCyan => NamedColor::Cyan,
610 NamedColor::BrightWhite => NamedColor::White,
611 NamedColor::BrightForeground => NamedColor::Foreground,
612 val => val,
613 }
614 }
615}
616
617#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
618pub enum Color {
619 Named(NamedColor),
620 Spec(Rgb),
621 Indexed(u8),
622}
623
624#[derive(Debug, Eq, PartialEq)]
626pub enum Attr {
627 Reset,
629 Bold,
631 Dim,
633 Italic,
635 Underline,
637 BlinkSlow,
639 BlinkFast,
641 Reverse,
643 Hidden,
645 Strike,
647 CancelBold,
649 CancelBoldDim,
651 CancelItalic,
653 CancelUnderline,
655 CancelBlink,
657 CancelReverse,
659 CancelHidden,
661 CancelStrike,
663 Foreground(Color),
665 Background(Color),
667}
668
669#[derive(Clone, Copy, Debug, Eq, PartialEq)]
671pub enum CharsetIndex {
672 G0,
674 G1,
675 G2,
676 G3,
677}
678
679impl Default for CharsetIndex {
680 fn default() -> Self {
681 CharsetIndex::G0
682 }
683}
684
685#[derive(Clone, Copy, Debug, Eq, PartialEq)]
687pub enum StandardCharset {
688 Ascii,
689 SpecialCharacterAndLineDrawing,
690}
691
692impl Default for StandardCharset {
693 fn default() -> Self {
694 StandardCharset::Ascii
695 }
696}
697
698impl<'a, H, W> vte::Perform for Performer<'a, H, W>
699where
700 H: Handler + TermInfo + 'a,
701 W: io::Write + 'a,
702{
703 #[inline]
704 fn print(&mut self, c: char) {
705 self.handler.input(c);
706 self.state.preceding_char = Some(c);
707 }
708
709 #[inline]
710 fn execute(&mut self, byte: u8) {
711 match byte {
712 C0::HT => self.handler.put_tab(1),
713 C0::BS => self.handler.backspace(),
714 C0::CR => self.handler.carriage_return(),
715 C0::LF | C0::VT | C0::FF => self.handler.linefeed(),
716 C0::BEL => self.handler.bell(),
717 C0::SUB => self.handler.substitute(),
718 C0::SI => self.handler.set_active_charset(CharsetIndex::G0),
719 C0::SO => self.handler.set_active_charset(CharsetIndex::G1),
720 _ => debug!("[unhandled] execute byte={:02x}", byte),
721 }
722 }
723
724 #[inline]
725 fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool) {
726 debug!(
727 "[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}",
728 params, intermediates, ignore
729 );
730 }
731
732 #[inline]
733 fn put(&mut self, byte: u8) {
734 debug!("[unhandled put] byte={:?}", byte);
735 }
736
737 #[inline]
738 fn unhook(&mut self) {
739 debug!("[unhandled unhook]");
740 }
741
742 #[inline]
744 fn osc_dispatch(&mut self, params: &[&[u8]]) {
745 let writer = &mut self.writer;
746
747 fn unhandled(params: &[&[u8]]) {
748 let mut buf = String::new();
749 for items in params {
750 buf.push_str("[");
751 for item in *items {
752 buf.push_str(&format!("{:?},", *item as char));
753 }
754 buf.push_str("],");
755 }
756 debug!("[unhandled osc_dispatch]: [{}] at line {}", &buf, line!());
757 }
758
759 if params.is_empty() || params[0].is_empty() {
760 return;
761 }
762
763 match params[0] {
764 b"0" | b"2" => {
766 if params.len() >= 2 {
767 let title = params[1..]
768 .iter()
769 .flat_map(|x| str::from_utf8(x))
770 .collect::<Vec<&str>>()
771 .join(";");
772 self.handler.set_title(&title);
773 return;
774 }
775 unhandled(params);
776 },
777
778 b"1" => (),
781
782 b"4" => {
784 if params.len() > 1 && params.len() % 2 != 0 {
785 for chunk in params[1..].chunks(2) {
786 let index = parse_number(chunk[0]);
787 let color = xparse_color(chunk[1]);
788 if let (Some(i), Some(c)) = (index, color) {
789 self.handler.set_color(i as usize, c);
790 return;
791 }
792 }
793 }
794 unhandled(params);
795 },
796
797 b"10" | b"11" | b"12" => {
799 if params.len() >= 2 {
800 if let Some(mut dynamic_code) = parse_number(params[0]) {
801 for param in ¶ms[1..] {
802 let offset = dynamic_code as usize - 10;
804 let index = NamedColor::Foreground as usize + offset;
805
806 if index > NamedColor::Cursor as usize {
808 unhandled(params);
809 break;
810 }
811
812 if let Some(color) = xparse_color(param) {
813 self.handler.set_color(index, color);
814 } else if param == b"?" {
815 self.handler.dynamic_color_sequence(writer, dynamic_code, index);
816 } else {
817 unhandled(params);
818 }
819 dynamic_code += 1;
820 }
821 return;
822 }
823 }
824 unhandled(params);
825 },
826
827 b"50" => {
829 if params.len() >= 2
830 && params[1].len() >= 13
831 && params[1][0..12] == *b"CursorShape="
832 {
833 let style = match params[1][12] as char {
834 '0' => CursorStyle::Block,
835 '1' => CursorStyle::Beam,
836 '2' => CursorStyle::Underline,
837 _ => return unhandled(params),
838 };
839 self.handler.set_cursor_style(Some(style));
840 return;
841 }
842 unhandled(params);
843 },
844
845 b"52" => {
847 if params.len() < 3 {
848 return unhandled(params);
849 }
850
851 let clipboard = params[1].get(0).unwrap_or(&b'c');
852 match params[2] {
853 b"?" => self.handler.write_clipboard(*clipboard, writer),
854 base64 => self.handler.set_clipboard(*clipboard, base64),
855 }
856 },
857
858 b"104" => {
860 if params.len() == 1 {
862 for i in 0..256 {
863 self.handler.reset_color(i);
864 }
865 return;
866 }
867
868 for param in ¶ms[1..] {
870 match parse_number(param) {
871 Some(index) => self.handler.reset_color(index as usize),
872 None => unhandled(params),
873 }
874 }
875 },
876
877 b"110" => self.handler.reset_color(NamedColor::Foreground as usize),
879
880 b"111" => self.handler.reset_color(NamedColor::Background as usize),
882
883 b"112" => self.handler.reset_color(NamedColor::Cursor as usize),
885
886 _ => unhandled(params),
887 }
888 }
889
890 #[inline]
891 fn csi_dispatch(
892 &mut self,
893 args: &[i64],
894 intermediates: &[u8],
895 has_ignored_intermediates: bool,
896 action: char,
897 ) {
898 macro_rules! unhandled {
899 () => {{
900 debug!(
901 "[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}",
902 action, args, intermediates
903 );
904 }};
905 }
906
907 macro_rules! arg_or_default {
908 (idx: $idx:expr, default: $default:expr) => {
909 args.get($idx)
910 .and_then(|v| if *v == 0 { None } else { Some(*v) })
911 .unwrap_or($default)
912 };
913 }
914
915 if has_ignored_intermediates || intermediates.len() > 1 {
916 unhandled!();
917 return;
918 }
919
920 let handler = &mut self.handler;
921 let writer = &mut self.writer;
922
923 match (action, intermediates.get(0)) {
924 ('@', None) => {
925 handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize))
926 },
927 ('A', None) => {
928 handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize));
929 },
930 ('b', None) => {
931 if let Some(c) = self.state.preceding_char {
932 for _ in 0..arg_or_default!(idx: 0, default: 1) {
933 handler.input(c);
934 }
935 } else {
936 debug!("tried to repeat with no preceding char");
937 }
938 },
939 ('B', None) | ('e', None) => {
940 handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize))
941 },
942 ('c', None) if arg_or_default!(idx: 0, default: 0) == 0 => {
943 handler.identify_terminal(writer)
944 },
945 ('C', None) | ('a', None) => {
946 handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize))
947 },
948 ('D', None) => {
949 handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize))
950 },
951 ('E', None) => {
952 handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize))
953 },
954 ('F', None) => {
955 handler.move_up_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize))
956 },
957 ('g', None) => {
958 let mode = match arg_or_default!(idx: 0, default: 0) {
959 0 => TabulationClearMode::Current,
960 3 => TabulationClearMode::All,
961 _ => {
962 unhandled!();
963 return;
964 },
965 };
966
967 handler.clear_tabs(mode);
968 },
969 ('G', None) | ('`', None) => {
970 handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1))
971 },
972 ('H', None) | ('f', None) => {
973 let y = arg_or_default!(idx: 0, default: 1) as usize;
974 let x = arg_or_default!(idx: 1, default: 1) as usize;
975 handler.goto(Line(y - 1), Column(x - 1));
976 },
977 ('I', None) => handler.move_forward_tabs(arg_or_default!(idx: 0, default: 1)),
978 ('J', None) => {
979 let mode = match arg_or_default!(idx: 0, default: 0) {
980 0 => ClearMode::Below,
981 1 => ClearMode::Above,
982 2 => ClearMode::All,
983 3 => ClearMode::Saved,
984 _ => {
985 unhandled!();
986 return;
987 },
988 };
989
990 handler.clear_screen(mode);
991 },
992 ('K', None) => {
993 let mode = match arg_or_default!(idx: 0, default: 0) {
994 0 => LineClearMode::Right,
995 1 => LineClearMode::Left,
996 2 => LineClearMode::All,
997 _ => {
998 unhandled!();
999 return;
1000 },
1001 };
1002
1003 handler.clear_line(mode);
1004 },
1005 ('S', None) => handler.scroll_up(Line(arg_or_default!(idx: 0, default: 1) as usize)),
1006 ('t', None) => match arg_or_default!(idx: 0, default: 1) as usize {
1007 22 => handler.push_title(),
1008 23 => handler.pop_title(),
1009 _ => unhandled!(),
1010 },
1011 ('T', None) => handler.scroll_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
1012 ('L', None) => {
1013 handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize))
1014 },
1015 ('l', intermediate) => {
1016 for arg in args {
1017 match Mode::from_primitive(intermediate, *arg) {
1018 Some(mode) => handler.unset_mode(mode),
1019 None => {
1020 unhandled!();
1021 return;
1022 },
1023 }
1024 }
1025 },
1026 ('M', None) => handler.delete_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
1027 ('X', None) => {
1028 handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize))
1029 },
1030 ('P', None) => {
1031 handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize))
1032 },
1033 ('Z', None) => handler.move_backward_tabs(arg_or_default!(idx: 0, default: 1)),
1034 ('d', None) => {
1035 handler.goto_line(Line(arg_or_default!(idx: 0, default: 1) as usize - 1))
1036 },
1037 ('h', intermediate) => {
1038 for arg in args {
1039 match Mode::from_primitive(intermediate, *arg) {
1040 Some(mode) => handler.set_mode(mode),
1041 None => {
1042 unhandled!();
1043 return;
1044 },
1045 }
1046 }
1047 },
1048 ('m', None) => {
1049 if args.is_empty() {
1050 handler.terminal_attribute(Attr::Reset);
1051 } else {
1052 for attr in attrs_from_sgr_parameters(args) {
1053 match attr {
1054 Some(attr) => handler.terminal_attribute(attr),
1055 None => {
1056 unhandled!();
1057 return;
1058 },
1059 }
1060 }
1061 }
1062 },
1063 ('n', None) => {
1064 handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize)
1065 },
1066 ('q', Some(b' ')) => {
1067 let style = match arg_or_default!(idx: 0, default: 0) {
1069 0 => None,
1070 1 | 2 => Some(CursorStyle::Block),
1071 3 | 4 => Some(CursorStyle::Underline),
1072 5 | 6 => Some(CursorStyle::Beam),
1073 _ => {
1074 unhandled!();
1075 return;
1076 },
1077 };
1078
1079 handler.set_cursor_style(style);
1080 },
1081 ('r', None) => {
1082 let top = arg_or_default!(idx: 0, default: 1) as usize;
1083 let bottom = arg_or_default!(idx: 1, default: handler.lines().0 as _) as usize;
1084
1085 handler.set_scrolling_region(top, bottom);
1086 },
1087 ('s', None) => handler.save_cursor_position(),
1088 ('u', None) => handler.restore_cursor_position(),
1089 _ => unhandled!(),
1090 }
1091 }
1092
1093 #[inline]
1094 fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], _ignore: bool, byte: u8) {
1095 macro_rules! unhandled {
1096 () => {{
1097 debug!(
1098 "[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})",
1099 params, intermediates, byte as char, byte
1100 );
1101 }};
1102 }
1103
1104 macro_rules! configure_charset {
1105 ($charset:path, $intermediate:expr) => {{
1106 let index: CharsetIndex = match $intermediate {
1107 Some(b'(') => CharsetIndex::G0,
1108 Some(b')') => CharsetIndex::G1,
1109 Some(b'*') => CharsetIndex::G2,
1110 Some(b'+') => CharsetIndex::G3,
1111 _ => {
1112 unhandled!();
1113 return;
1114 },
1115 };
1116 self.handler.configure_charset(index, $charset)
1117 }};
1118 }
1119
1120 match (byte, intermediates.get(0)) {
1121 (b'B', intermediate) => configure_charset!(StandardCharset::Ascii, intermediate),
1122 (b'D', None) => self.handler.linefeed(),
1123 (b'E', None) => {
1124 self.handler.linefeed();
1125 self.handler.carriage_return();
1126 },
1127 (b'H', None) => self.handler.set_horizontal_tabstop(),
1128 (b'M', None) => self.handler.reverse_index(),
1129 (b'Z', None) => self.handler.identify_terminal(self.writer),
1130 (b'c', None) => self.handler.reset_state(),
1131 (b'0', intermediate) => {
1132 configure_charset!(StandardCharset::SpecialCharacterAndLineDrawing, intermediate)
1133 },
1134 (b'7', None) => self.handler.save_cursor_position(),
1135 (b'8', Some(b'#')) => self.handler.decaln(),
1136 (b'8', None) => self.handler.restore_cursor_position(),
1137 (b'=', None) => self.handler.set_keypad_application_mode(),
1138 (b'>', None) => self.handler.unset_keypad_application_mode(),
1139 (b'\\', None) => (),
1141 _ => unhandled!(),
1142 }
1143 }
1144}
1145
1146fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> {
1147 let mut i = 0; let mut attrs = Vec::with_capacity(parameters.len());
1150 loop {
1151 if i >= parameters.len() {
1152 break;
1154 }
1155
1156 let attr = match parameters[i] {
1157 0 => Some(Attr::Reset),
1158 1 => Some(Attr::Bold),
1159 2 => Some(Attr::Dim),
1160 3 => Some(Attr::Italic),
1161 4 => Some(Attr::Underline),
1162 5 => Some(Attr::BlinkSlow),
1163 6 => Some(Attr::BlinkFast),
1164 7 => Some(Attr::Reverse),
1165 8 => Some(Attr::Hidden),
1166 9 => Some(Attr::Strike),
1167 21 => Some(Attr::CancelBold),
1168 22 => Some(Attr::CancelBoldDim),
1169 23 => Some(Attr::CancelItalic),
1170 24 => Some(Attr::CancelUnderline),
1171 25 => Some(Attr::CancelBlink),
1172 27 => Some(Attr::CancelReverse),
1173 28 => Some(Attr::CancelHidden),
1174 29 => Some(Attr::CancelStrike),
1175 30 => Some(Attr::Foreground(Color::Named(NamedColor::Black))),
1176 31 => Some(Attr::Foreground(Color::Named(NamedColor::Red))),
1177 32 => Some(Attr::Foreground(Color::Named(NamedColor::Green))),
1178 33 => Some(Attr::Foreground(Color::Named(NamedColor::Yellow))),
1179 34 => Some(Attr::Foreground(Color::Named(NamedColor::Blue))),
1180 35 => Some(Attr::Foreground(Color::Named(NamedColor::Magenta))),
1181 36 => Some(Attr::Foreground(Color::Named(NamedColor::Cyan))),
1182 37 => Some(Attr::Foreground(Color::Named(NamedColor::White))),
1183 38 => {
1184 let mut start = 0;
1185 if let Some(color) = parse_sgr_color(¶meters[i..], &mut start) {
1186 i += start;
1187 Some(Attr::Foreground(color))
1188 } else {
1189 None
1190 }
1191 },
1192 39 => Some(Attr::Foreground(Color::Named(NamedColor::Foreground))),
1193 40 => Some(Attr::Background(Color::Named(NamedColor::Black))),
1194 41 => Some(Attr::Background(Color::Named(NamedColor::Red))),
1195 42 => Some(Attr::Background(Color::Named(NamedColor::Green))),
1196 43 => Some(Attr::Background(Color::Named(NamedColor::Yellow))),
1197 44 => Some(Attr::Background(Color::Named(NamedColor::Blue))),
1198 45 => Some(Attr::Background(Color::Named(NamedColor::Magenta))),
1199 46 => Some(Attr::Background(Color::Named(NamedColor::Cyan))),
1200 47 => Some(Attr::Background(Color::Named(NamedColor::White))),
1201 48 => {
1202 let mut start = 0;
1203 if let Some(color) = parse_sgr_color(¶meters[i..], &mut start) {
1204 i += start;
1205 Some(Attr::Background(color))
1206 } else {
1207 None
1208 }
1209 },
1210 49 => Some(Attr::Background(Color::Named(NamedColor::Background))),
1211 90 => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlack))),
1212 91 => Some(Attr::Foreground(Color::Named(NamedColor::BrightRed))),
1213 92 => Some(Attr::Foreground(Color::Named(NamedColor::BrightGreen))),
1214 93 => Some(Attr::Foreground(Color::Named(NamedColor::BrightYellow))),
1215 94 => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlue))),
1216 95 => Some(Attr::Foreground(Color::Named(NamedColor::BrightMagenta))),
1217 96 => Some(Attr::Foreground(Color::Named(NamedColor::BrightCyan))),
1218 97 => Some(Attr::Foreground(Color::Named(NamedColor::BrightWhite))),
1219 100 => Some(Attr::Background(Color::Named(NamedColor::BrightBlack))),
1220 101 => Some(Attr::Background(Color::Named(NamedColor::BrightRed))),
1221 102 => Some(Attr::Background(Color::Named(NamedColor::BrightGreen))),
1222 103 => Some(Attr::Background(Color::Named(NamedColor::BrightYellow))),
1223 104 => Some(Attr::Background(Color::Named(NamedColor::BrightBlue))),
1224 105 => Some(Attr::Background(Color::Named(NamedColor::BrightMagenta))),
1225 106 => Some(Attr::Background(Color::Named(NamedColor::BrightCyan))),
1226 107 => Some(Attr::Background(Color::Named(NamedColor::BrightWhite))),
1227 _ => None,
1228 };
1229
1230 attrs.push(attr);
1231
1232 i += 1; }
1234 attrs
1235}
1236
1237fn parse_sgr_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
1239 if attrs.len() < 2 {
1240 return None;
1241 }
1242
1243 match attrs[*i + 1] {
1244 2 => {
1245 if attrs.len() < 5 {
1247 debug!("Expected RGB color spec; got {:?}", attrs);
1248 return None;
1249 }
1250
1251 let r = attrs[*i + 2];
1252 let g = attrs[*i + 3];
1253 let b = attrs[*i + 4];
1254
1255 *i += 4;
1256
1257 let range = 0..256;
1258 if !range.contains(&r) || !range.contains(&g) || !range.contains(&b) {
1259 debug!("Invalid RGB color spec: ({}, {}, {})", r, g, b);
1260 return None;
1261 }
1262
1263 Some(Color::Spec(Rgb { r: r as u8, g: g as u8, b: b as u8 }))
1264 },
1265 5 => {
1266 if attrs.len() < 3 {
1267 debug!("Expected color index; got {:?}", attrs);
1268 None
1269 } else {
1270 *i += 2;
1271 let idx = attrs[*i];
1272 match idx {
1273 0..=255 => Some(Color::Indexed(idx as u8)),
1274 _ => {
1275 debug!("Invalid color index: {}", idx);
1276 None
1277 },
1278 }
1279 }
1280 },
1281 _ => {
1282 debug!("Unexpected color attr: {}", attrs[*i + 1]);
1283 None
1284 },
1285 }
1286}
1287
1288#[allow(non_snake_case)]
1290pub mod C0 {
1291 pub const NUL: u8 = 0x00;
1293 pub const SOH: u8 = 0x01;
1295 pub const STX: u8 = 0x02;
1297 pub const ETX: u8 = 0x03;
1299 pub const EOT: u8 = 0x04;
1301 pub const ENQ: u8 = 0x05;
1303 pub const ACK: u8 = 0x06;
1305 pub const BEL: u8 = 0x07;
1307 pub const BS: u8 = 0x08;
1309 pub const HT: u8 = 0x09;
1311 pub const LF: u8 = 0x0A;
1313 pub const VT: u8 = 0x0B;
1315 pub const FF: u8 = 0x0C;
1317 pub const CR: u8 = 0x0D;
1319 pub const SO: u8 = 0x0E;
1321 pub const SI: u8 = 0x0F;
1323 pub const DLE: u8 = 0x10;
1325 pub const XON: u8 = 0x11;
1327 pub const DC2: u8 = 0x12;
1329 pub const XOFF: u8 = 0x13;
1331 pub const DC4: u8 = 0x14;
1333 pub const NAK: u8 = 0x15;
1335 pub const SYN: u8 = 0x16;
1337 pub const ETB: u8 = 0x17;
1339 pub const CAN: u8 = 0x18;
1341 pub const EM: u8 = 0x19;
1343 pub const SUB: u8 = 0x1A;
1345 pub const ESC: u8 = 0x1B;
1347 pub const FS: u8 = 0x1C;
1349 pub const GS: u8 = 0x1D;
1351 pub const RS: u8 = 0x1E;
1353 pub const US: u8 = 0x1F;
1355 pub const DEL: u8 = 0x7f;
1357}
1358
1359#[cfg(test)]
1363mod tests {
1364 use super::{
1365 parse_number, xparse_color, Attr, CharsetIndex, Color, Handler, Processor, StandardCharset,
1366 TermInfo,
1367 };
1368 use crate::index::{Column, Line};
1369 use crate::term::color::Rgb;
1370 use std::io;
1371
1372 struct MockHandler {
1373 index: CharsetIndex,
1374 charset: StandardCharset,
1375 attr: Option<Attr>,
1376 identity_reported: bool,
1377 }
1378
1379 impl Handler for MockHandler {
1380 fn terminal_attribute(&mut self, attr: Attr) {
1381 self.attr = Some(attr);
1382 }
1383
1384 fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
1385 self.index = index;
1386 self.charset = charset;
1387 }
1388
1389 fn set_active_charset(&mut self, index: CharsetIndex) {
1390 self.index = index;
1391 }
1392
1393 fn identify_terminal<W: io::Write>(&mut self, _: &mut W) {
1394 self.identity_reported = true;
1395 }
1396
1397 fn reset_state(&mut self) {
1398 *self = Self::default();
1399 }
1400 }
1401
1402 impl TermInfo for MockHandler {
1403 fn lines(&self) -> Line {
1404 Line(200)
1405 }
1406
1407 fn cols(&self) -> Column {
1408 Column(90)
1409 }
1410 }
1411
1412 impl Default for MockHandler {
1413 fn default() -> MockHandler {
1414 MockHandler {
1415 index: CharsetIndex::G0,
1416 charset: StandardCharset::Ascii,
1417 attr: None,
1418 identity_reported: false,
1419 }
1420 }
1421 }
1422
1423 #[test]
1424 fn parse_control_attribute() {
1425 static BYTES: &[u8] = &[0x1b, b'[', b'1', b'm'];
1426
1427 let mut parser = Processor::new();
1428 let mut handler = MockHandler::default();
1429
1430 for byte in &BYTES[..] {
1431 parser.advance(&mut handler, *byte, &mut io::sink());
1432 }
1433
1434 assert_eq!(handler.attr, Some(Attr::Bold));
1435 }
1436
1437 #[test]
1438 fn parse_terminal_identity_csi() {
1439 let bytes: &[u8] = &[0x1b, b'[', b'1', b'c'];
1440
1441 let mut parser = Processor::new();
1442 let mut handler = MockHandler::default();
1443
1444 for byte in &bytes[..] {
1445 parser.advance(&mut handler, *byte, &mut io::sink());
1446 }
1447
1448 assert!(!handler.identity_reported);
1449 handler.reset_state();
1450
1451 let bytes: &[u8] = &[0x1b, b'[', b'c'];
1452
1453 for byte in &bytes[..] {
1454 parser.advance(&mut handler, *byte, &mut io::sink());
1455 }
1456
1457 assert!(handler.identity_reported);
1458 handler.reset_state();
1459
1460 let bytes: &[u8] = &[0x1b, b'[', b'0', b'c'];
1461
1462 for byte in &bytes[..] {
1463 parser.advance(&mut handler, *byte, &mut io::sink());
1464 }
1465
1466 assert!(handler.identity_reported);
1467 }
1468
1469 #[test]
1470 fn parse_terminal_identity_esc() {
1471 let bytes: &[u8] = &[0x1b, b'Z'];
1472
1473 let mut parser = Processor::new();
1474 let mut handler = MockHandler::default();
1475
1476 for byte in &bytes[..] {
1477 parser.advance(&mut handler, *byte, &mut io::sink());
1478 }
1479
1480 assert!(handler.identity_reported);
1481 handler.reset_state();
1482
1483 let bytes: &[u8] = &[0x1b, b'#', b'Z'];
1484
1485 let mut parser = Processor::new();
1486 let mut handler = MockHandler::default();
1487
1488 for byte in &bytes[..] {
1489 parser.advance(&mut handler, *byte, &mut io::sink());
1490 }
1491
1492 assert!(!handler.identity_reported);
1493 handler.reset_state();
1494 }
1495
1496 #[test]
1497 fn parse_truecolor_attr() {
1498 static BYTES: &[u8] = &[
1499 0x1b, b'[', b'3', b'8', b';', b'2', b';', b'1', b'2', b'8', b';', b'6', b'6', b';',
1500 b'2', b'5', b'5', b'm',
1501 ];
1502
1503 let mut parser = Processor::new();
1504 let mut handler = MockHandler::default();
1505
1506 for byte in &BYTES[..] {
1507 parser.advance(&mut handler, *byte, &mut io::sink());
1508 }
1509
1510 let spec = Rgb { r: 128, g: 66, b: 255 };
1511
1512 assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec))));
1513 }
1514
1515 #[test]
1517 fn parse_zsh_startup() {
1518 static BYTES: &[u8] = &[
1519 0x1b, b'[', b'1', b'm', 0x1b, b'[', b'7', b'm', b'%', 0x1b, b'[', b'2', b'7', b'm',
1520 0x1b, b'[', b'1', b'm', 0x1b, b'[', b'0', b'm', b' ', b' ', b' ', b' ', b' ', b' ',
1521 b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
1522 b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
1523 b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
1524 b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
1525 b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
1526 b' ', b' ', b' ', b'\r', b' ', b'\r', b'\r', 0x1b, b'[', b'0', b'm', 0x1b, b'[', b'2',
1527 b'7', b'm', 0x1b, b'[', b'2', b'4', b'm', 0x1b, b'[', b'J', b'j', b'w', b'i', b'l',
1528 b'm', b'@', b'j', b'w', b'i', b'l', b'm', b'-', b'd', b'e', b's', b'k', b' ', 0x1b,
1529 b'[', b'0', b'1', b';', b'3', b'2', b'm', 0xe2, 0x9e, 0x9c, b' ', 0x1b, b'[', b'0',
1530 b'1', b';', b'3', b'2', b'm', b' ', 0x1b, b'[', b'3', b'6', b'm', b'~', b'/', b'c',
1531 b'o', b'd', b'e',
1532 ];
1533
1534 let mut handler = MockHandler::default();
1535 let mut parser = Processor::new();
1536
1537 for byte in &BYTES[..] {
1538 parser.advance(&mut handler, *byte, &mut io::sink());
1539 }
1540 }
1541
1542 #[test]
1543 fn parse_designate_g0_as_line_drawing() {
1544 static BYTES: &[u8] = &[0x1b, b'(', b'0'];
1545 let mut parser = Processor::new();
1546 let mut handler = MockHandler::default();
1547
1548 for byte in &BYTES[..] {
1549 parser.advance(&mut handler, *byte, &mut io::sink());
1550 }
1551
1552 assert_eq!(handler.index, CharsetIndex::G0);
1553 assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing);
1554 }
1555
1556 #[test]
1557 fn parse_designate_g1_as_line_drawing_and_invoke() {
1558 static BYTES: &[u8] = &[0x1b, b')', b'0', 0x0e];
1559 let mut parser = Processor::new();
1560 let mut handler = MockHandler::default();
1561
1562 for byte in &BYTES[..3] {
1563 parser.advance(&mut handler, *byte, &mut io::sink());
1564 }
1565
1566 assert_eq!(handler.index, CharsetIndex::G1);
1567 assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing);
1568
1569 let mut handler = MockHandler::default();
1570 parser.advance(&mut handler, BYTES[3], &mut io::sink());
1571
1572 assert_eq!(handler.index, CharsetIndex::G1);
1573 }
1574
1575 #[test]
1576 fn parse_valid_rgb_colors() {
1577 assert_eq!(xparse_color(b"rgb:f/e/d"), Some(Rgb { r: 0xff, g: 0xee, b: 0xdd }));
1578 assert_eq!(xparse_color(b"rgb:11/aa/ff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
1579 assert_eq!(xparse_color(b"rgb:f/ed1/cb23"), Some(Rgb { r: 0xff, g: 0xec, b: 0xca }));
1580 assert_eq!(xparse_color(b"rgb:ffff/0/0"), Some(Rgb { r: 0xff, g: 0x0, b: 0x0 }));
1581 }
1582
1583 #[test]
1584 fn parse_valid_legacy_rgb_colors() {
1585 assert_eq!(xparse_color(b"#1af"), Some(Rgb { r: 0x10, g: 0xa0, b: 0xf0 }));
1586 assert_eq!(xparse_color(b"#11aaff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
1587 assert_eq!(xparse_color(b"#110aa0ff0"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
1588 assert_eq!(xparse_color(b"#1100aa00ff00"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
1589 }
1590
1591 #[test]
1592 fn parse_invalid_rgb_colors() {
1593 assert_eq!(xparse_color(b"rgb:0//"), None);
1594 assert_eq!(xparse_color(b"rgb://///"), None);
1595 }
1596
1597 #[test]
1598 fn parse_invalid_legacy_rgb_colors() {
1599 assert_eq!(xparse_color(b"#"), None);
1600 assert_eq!(xparse_color(b"#f"), None);
1601 }
1602
1603 #[test]
1604 fn parse_invalid_number() {
1605 assert_eq!(parse_number(b"1abc"), None);
1606 }
1607
1608 #[test]
1609 fn parse_valid_number() {
1610 assert_eq!(parse_number(b"123"), Some(123));
1611 }
1612
1613 #[test]
1614 fn parse_number_too_large() {
1615 assert_eq!(parse_number(b"321"), None);
1616 }
1617}