1use crate::term::BufWrite as _;
2use unicode_width::UnicodeWidthChar as _;
3
4const MODE_APPLICATION_KEYPAD: u8 = 0b0000_0001;
5const MODE_APPLICATION_CURSOR: u8 = 0b0000_0010;
6const MODE_HIDE_CURSOR: u8 = 0b0000_0100;
7const MODE_ALTERNATE_SCREEN: u8 = 0b0000_1000;
8const MODE_BRACKETED_PASTE: u8 = 0b0001_0000;
9
10#[derive(Copy, Clone, Debug, Eq, PartialEq)]
12pub enum MouseProtocolMode {
13 None,
15
16 Press,
19
20 PressRelease,
23
24 ButtonMotion,
29
30 AnyMotion,
34 }
36
37impl Default for MouseProtocolMode {
38 fn default() -> Self {
39 Self::None
40 }
41}
42
43#[derive(Copy, Clone, Debug, Eq, PartialEq)]
45pub enum MouseProtocolEncoding {
46 Default,
48
49 Utf8,
51
52 Sgr,
54 }
56
57impl Default for MouseProtocolEncoding {
58 fn default() -> Self {
59 Self::Default
60 }
61}
62
63#[derive(Clone, Debug)]
65pub struct Screen {
66 grid: crate::grid::Grid,
67 alternate_grid: crate::grid::Grid,
68
69 attrs: crate::attrs::Attrs,
70 saved_attrs: crate::attrs::Attrs,
71
72 title: String,
73 icon_name: String,
74
75 modes: u8,
76 mouse_protocol_mode: MouseProtocolMode,
77 mouse_protocol_encoding: MouseProtocolEncoding,
78
79 audible_bell_count: usize,
80 visual_bell_count: usize,
81
82 errors: usize,
83}
84
85impl Screen {
86 pub(crate) fn new(size: crate::grid::Size, scrollback_len: usize) -> Self {
87 let mut grid = crate::grid::Grid::new(size, scrollback_len);
88 grid.allocate_rows();
89 Self {
90 grid,
91 alternate_grid: crate::grid::Grid::new(size, 0),
92
93 attrs: crate::attrs::Attrs::default(),
94 saved_attrs: crate::attrs::Attrs::default(),
95
96 title: String::default(),
97 icon_name: String::default(),
98
99 modes: 0,
100 mouse_protocol_mode: MouseProtocolMode::default(),
101 mouse_protocol_encoding: MouseProtocolEncoding::default(),
102
103 audible_bell_count: 0,
104 visual_bell_count: 0,
105
106 errors: 0,
107 }
108 }
109
110 pub(crate) fn set_size(&mut self, rows: u16, cols: u16) {
111 self.grid.set_size(crate::grid::Size { rows, cols });
112 self.alternate_grid
113 .set_size(crate::grid::Size { rows, cols });
114 }
115
116 pub(crate) fn clear(&mut self) {
117 self.grid.clear();
118 self.alternate_grid.clear();
119 }
120
121 #[must_use]
125 pub fn size(&self) -> (u16, u16) {
126 let size = self.grid().size();
127 (size.rows, size.cols)
128 }
129
130 #[must_use]
135 pub fn scrollback(&self) -> usize {
136 self.grid().scrollback()
137 }
138
139 pub fn scrollback_len(&self) -> usize {
140 self.grid().current_scrollback_len()
141 }
142
143 pub(crate) fn set_scrollback(&mut self, rows: usize) {
144 self.grid_mut().set_scrollback(rows);
145 }
146
147 #[must_use]
152 pub fn contents(&self) -> String {
153 let mut contents = String::new();
154 self.write_contents(&mut contents);
155 contents
156 }
157
158 fn write_contents(&self, contents: &mut String) {
159 self.grid().write_contents(contents);
160 }
161
162 pub fn rows(&self, start: u16, width: u16) -> impl Iterator<Item = String> + '_ {
170 self.grid().visible_rows().map(move |row| {
171 let mut contents = String::new();
172 row.write_contents(&mut contents, start, width, false);
173 contents
174 })
175 }
176
177 #[must_use]
184 pub fn contents_between(
185 &self,
186 start_row: u16,
187 start_col: u16,
188 end_row: u16,
189 end_col: u16,
190 ) -> String {
191 match start_row.cmp(&end_row) {
192 std::cmp::Ordering::Less => {
193 let (_, cols) = self.size();
194 let mut contents = String::new();
195 for (i, row) in self
196 .grid()
197 .visible_rows()
198 .enumerate()
199 .skip(usize::from(start_row))
200 .take(usize::from(end_row) - usize::from(start_row) + 1)
201 {
202 if i == usize::from(start_row) {
203 row.write_contents(&mut contents, start_col, cols - start_col, false);
204 if !row.wrapped() {
205 contents.push('\n');
206 }
207 } else if i == usize::from(end_row) {
208 row.write_contents(&mut contents, 0, end_col, false);
209 } else {
210 row.write_contents(&mut contents, 0, cols, false);
211 if !row.wrapped() {
212 contents.push('\n');
213 }
214 }
215 }
216 contents
217 }
218 std::cmp::Ordering::Equal => {
219 if start_col < end_col {
220 self.rows(start_col, end_col - start_col)
221 .nth(usize::from(start_row))
222 .unwrap_or_default()
223 } else {
224 String::new()
225 }
226 }
227 std::cmp::Ordering::Greater => String::new(),
228 }
229 }
230
231 #[must_use]
235 pub fn state_formatted(&self) -> Vec<u8> {
236 let mut contents = vec![];
237 self.write_contents_formatted(&mut contents);
238 self.write_input_mode_formatted(&mut contents);
239 self.write_title_formatted(&mut contents);
240 contents
241 }
242
243 #[must_use]
248 pub fn state_diff(&self, prev: &Self) -> Vec<u8> {
249 let mut contents = vec![];
250 self.write_contents_diff(&mut contents, prev);
251 self.write_input_mode_diff(&mut contents, prev);
252 self.write_title_diff(&mut contents, prev);
253 self.write_bells_diff(&mut contents, prev);
254 contents
255 }
256
257 #[must_use]
263 pub fn contents_formatted(&self) -> Vec<u8> {
264 let mut contents = vec![];
265 self.write_contents_formatted(&mut contents);
266 contents
267 }
268
269 fn write_contents_formatted(&self, contents: &mut Vec<u8>) {
270 crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
271 let prev_attrs = self.grid().write_contents_formatted(contents);
272 self.attrs.write_escape_code_diff(contents, &prev_attrs);
273 }
274
275 #[allow(clippy::missing_panics_doc)]
287 pub fn rows_formatted(&self, start: u16, width: u16) -> impl Iterator<Item = Vec<u8>> + '_ {
288 let mut wrapping = false;
289 self.grid().visible_rows().enumerate().map(move |(i, row)| {
290 let i = i.try_into().unwrap();
293 let mut contents = vec![];
294 row.write_contents_formatted(&mut contents, start, width, i, wrapping, None, None);
295 if start == 0 && width == self.grid.size().cols {
296 wrapping = row.wrapped();
297 }
298 contents
299 })
300 }
301
302 #[must_use]
313 pub fn contents_diff(&self, prev: &Self) -> Vec<u8> {
314 let mut contents = vec![];
315 self.write_contents_diff(&mut contents, prev);
316 contents
317 }
318
319 fn write_contents_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
320 if self.hide_cursor() != prev.hide_cursor() {
321 crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
322 }
323 let prev_attrs = self
324 .grid()
325 .write_contents_diff(contents, prev.grid(), prev.attrs);
326 self.attrs.write_escape_code_diff(contents, &prev_attrs);
327 }
328
329 #[allow(clippy::missing_panics_doc)]
339 pub fn rows_diff<'a>(
340 &'a self,
341 prev: &'a Self,
342 start: u16,
343 width: u16,
344 ) -> impl Iterator<Item = Vec<u8>> + 'a {
345 self.grid()
346 .visible_rows()
347 .zip(prev.grid().visible_rows())
348 .enumerate()
349 .map(move |(i, (row, prev_row))| {
350 let i = i.try_into().unwrap();
353 let mut contents = vec![];
354 row.write_contents_diff(
355 &mut contents,
356 prev_row,
357 start,
358 width,
359 i,
360 false,
361 false,
362 crate::grid::Pos { row: i, col: start },
363 crate::attrs::Attrs::default(),
364 );
365 contents
366 })
367 }
368
369 #[must_use]
378 pub fn input_mode_formatted(&self) -> Vec<u8> {
379 let mut contents = vec![];
380 self.write_input_mode_formatted(&mut contents);
381 contents
382 }
383
384 fn write_input_mode_formatted(&self, contents: &mut Vec<u8>) {
385 crate::term::ApplicationKeypad::new(self.mode(MODE_APPLICATION_KEYPAD)).write_buf(contents);
386 crate::term::ApplicationCursor::new(self.mode(MODE_APPLICATION_CURSOR)).write_buf(contents);
387 crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE)).write_buf(contents);
388 crate::term::MouseProtocolMode::new(self.mouse_protocol_mode, MouseProtocolMode::None)
389 .write_buf(contents);
390 crate::term::MouseProtocolEncoding::new(
391 self.mouse_protocol_encoding,
392 MouseProtocolEncoding::Default,
393 )
394 .write_buf(contents);
395 }
396
397 #[must_use]
401 pub fn input_mode_diff(&self, prev: &Self) -> Vec<u8> {
402 let mut contents = vec![];
403 self.write_input_mode_diff(&mut contents, prev);
404 contents
405 }
406
407 fn write_input_mode_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
408 if self.mode(MODE_APPLICATION_KEYPAD) != prev.mode(MODE_APPLICATION_KEYPAD) {
409 crate::term::ApplicationKeypad::new(self.mode(MODE_APPLICATION_KEYPAD))
410 .write_buf(contents);
411 }
412 if self.mode(MODE_APPLICATION_CURSOR) != prev.mode(MODE_APPLICATION_CURSOR) {
413 crate::term::ApplicationCursor::new(self.mode(MODE_APPLICATION_CURSOR))
414 .write_buf(contents);
415 }
416 if self.mode(MODE_BRACKETED_PASTE) != prev.mode(MODE_BRACKETED_PASTE) {
417 crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE)).write_buf(contents);
418 }
419 crate::term::MouseProtocolMode::new(self.mouse_protocol_mode, prev.mouse_protocol_mode)
420 .write_buf(contents);
421 crate::term::MouseProtocolEncoding::new(
422 self.mouse_protocol_encoding,
423 prev.mouse_protocol_encoding,
424 )
425 .write_buf(contents);
426 }
427
428 #[must_use]
431 pub fn title_formatted(&self) -> Vec<u8> {
432 let mut contents = vec![];
433 self.write_title_formatted(&mut contents);
434 contents
435 }
436
437 fn write_title_formatted(&self, contents: &mut Vec<u8>) {
438 crate::term::ChangeTitle::new(&self.icon_name, &self.title, "", "").write_buf(contents);
439 }
440
441 #[must_use]
445 pub fn title_diff(&self, prev: &Self) -> Vec<u8> {
446 let mut contents = vec![];
447 self.write_title_diff(&mut contents, prev);
448 contents
449 }
450
451 fn write_title_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
452 crate::term::ChangeTitle::new(&self.icon_name, &self.title, &prev.icon_name, &prev.title)
453 .write_buf(contents);
454 }
455
456 #[must_use]
460 pub fn bells_diff(&self, prev: &Self) -> Vec<u8> {
461 let mut contents = vec![];
462 self.write_bells_diff(&mut contents, prev);
463 contents
464 }
465
466 fn write_bells_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
467 if self.audible_bell_count != prev.audible_bell_count {
468 crate::term::AudibleBell::default().write_buf(contents);
469 }
470 if self.visual_bell_count != prev.visual_bell_count {
471 crate::term::VisualBell::default().write_buf(contents);
472 }
473 }
474
475 #[must_use]
492 pub fn attributes_formatted(&self) -> Vec<u8> {
493 let mut contents = vec![];
494 self.write_attributes_formatted(&mut contents);
495 contents
496 }
497
498 fn write_attributes_formatted(&self, contents: &mut Vec<u8>) {
499 crate::term::ClearAttrs::default().write_buf(contents);
500 self.attrs
501 .write_escape_code_diff(contents, &crate::attrs::Attrs::default());
502 }
503
504 #[must_use]
508 pub fn cursor_position(&self) -> (u16, u16) {
509 let pos = self.grid().pos();
510 (pos.row, pos.col)
511 }
512
513 #[must_use]
529 pub fn cursor_state_formatted(&self) -> Vec<u8> {
530 let mut contents = vec![];
531 self.write_cursor_state_formatted(&mut contents);
532 contents
533 }
534
535 fn write_cursor_state_formatted(&self, contents: &mut Vec<u8>) {
536 crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
537 self.grid()
538 .write_cursor_position_formatted(contents, None, None);
539
540 }
547
548 #[must_use]
551 pub fn cell(&self, row: u16, col: u16) -> Option<&crate::cell::Cell> {
552 self.grid().visible_cell(crate::grid::Pos { row, col })
553 }
554
555 #[must_use]
557 pub fn row_wrapped(&self, row: u16) -> bool {
558 self.grid()
559 .visible_row(row)
560 .map_or(false, crate::row::Row::wrapped)
561 }
562
563 #[must_use]
565 pub fn title(&self) -> &str {
566 &self.title
567 }
568
569 #[must_use]
571 pub fn icon_name(&self) -> &str {
572 &self.icon_name
573 }
574
575 #[must_use]
584 pub fn audible_bell_count(&self) -> usize {
585 self.audible_bell_count
586 }
587
588 #[must_use]
597 pub fn visual_bell_count(&self) -> usize {
598 self.visual_bell_count
599 }
600
601 #[must_use]
607 pub fn errors(&self) -> usize {
608 self.errors
609 }
610
611 #[must_use]
613 pub fn alternate_screen(&self) -> bool {
614 self.mode(MODE_ALTERNATE_SCREEN)
615 }
616
617 #[must_use]
619 pub fn application_keypad(&self) -> bool {
620 self.mode(MODE_APPLICATION_KEYPAD)
621 }
622
623 #[must_use]
625 pub fn application_cursor(&self) -> bool {
626 self.mode(MODE_APPLICATION_CURSOR)
627 }
628
629 #[must_use]
631 pub fn hide_cursor(&self) -> bool {
632 self.mode(MODE_HIDE_CURSOR)
633 }
634
635 #[must_use]
637 pub fn bracketed_paste(&self) -> bool {
638 self.mode(MODE_BRACKETED_PASTE)
639 }
640
641 #[must_use]
643 pub fn mouse_protocol_mode(&self) -> MouseProtocolMode {
644 self.mouse_protocol_mode
645 }
646
647 #[must_use]
649 pub fn mouse_protocol_encoding(&self) -> MouseProtocolEncoding {
650 self.mouse_protocol_encoding
651 }
652
653 #[must_use]
655 pub fn fgcolor(&self) -> crate::attrs::Color {
656 self.attrs.fgcolor
657 }
658
659 #[must_use]
661 pub fn bgcolor(&self) -> crate::attrs::Color {
662 self.attrs.bgcolor
663 }
664
665 #[must_use]
668 pub fn bold(&self) -> bool {
669 self.attrs.bold()
670 }
671
672 #[must_use]
675 pub fn italic(&self) -> bool {
676 self.attrs.italic()
677 }
678
679 #[must_use]
682 pub fn underline(&self) -> bool {
683 self.attrs.underline()
684 }
685
686 #[must_use]
689 pub fn inverse(&self) -> bool {
690 self.attrs.inverse()
691 }
692
693 fn grid(&self) -> &crate::grid::Grid {
694 if self.mode(MODE_ALTERNATE_SCREEN) {
695 &self.alternate_grid
696 } else {
697 &self.grid
698 }
699 }
700
701 fn grid_mut(&mut self) -> &mut crate::grid::Grid {
702 if self.mode(MODE_ALTERNATE_SCREEN) {
703 &mut self.alternate_grid
704 } else {
705 &mut self.grid
706 }
707 }
708
709 fn enter_alternate_grid(&mut self) {
710 self.grid_mut().set_scrollback(0);
711 self.set_mode(MODE_ALTERNATE_SCREEN);
712 self.alternate_grid.allocate_rows();
713 }
714
715 fn exit_alternate_grid(&mut self) {
716 self.clear_mode(MODE_ALTERNATE_SCREEN);
717 }
718
719 fn save_cursor(&mut self) {
720 self.grid_mut().save_cursor();
721 self.saved_attrs = self.attrs;
722 }
723
724 fn restore_cursor(&mut self) {
725 self.grid_mut().restore_cursor();
726 self.attrs = self.saved_attrs;
727 }
728
729 fn set_mode(&mut self, mode: u8) {
730 self.modes |= mode;
731 }
732
733 fn clear_mode(&mut self, mode: u8) {
734 self.modes &= !mode;
735 }
736
737 fn mode(&self, mode: u8) -> bool {
738 self.modes & mode != 0
739 }
740
741 fn set_mouse_mode(&mut self, mode: MouseProtocolMode) {
742 self.mouse_protocol_mode = mode;
743 }
744
745 fn clear_mouse_mode(&mut self, mode: MouseProtocolMode) {
746 if self.mouse_protocol_mode == mode {
747 self.mouse_protocol_mode = MouseProtocolMode::default();
748 }
749 }
750
751 fn set_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
752 self.mouse_protocol_encoding = encoding;
753 }
754
755 fn clear_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
756 if self.mouse_protocol_encoding == encoding {
757 self.mouse_protocol_encoding = MouseProtocolEncoding::default();
758 }
759 }
760}
761
762impl Screen {
763 fn text(&mut self, c: char) {
764 let pos = self.grid().pos();
765 let size = self.grid().size();
766 let attrs = self.attrs;
767
768 let width = c.width();
769 if width.is_none() && (u32::from(c)) < 256 {
770 return;
772 }
773 let width = width
774 .unwrap_or(1)
775 .try_into()
776 .unwrap();
778
779 let mut wrap = false;
788 if pos.col > size.cols - width {
789 let last_cell = self
790 .grid()
791 .drawing_cell(crate::grid::Pos {
792 row: pos.row,
793 col: size.cols - 1,
794 })
795 .unwrap();
799 if last_cell.has_contents() || last_cell.is_wide_continuation() {
800 wrap = true;
801 }
802 }
803 self.grid_mut().col_wrap(width, wrap);
804 let pos = self.grid().pos();
805
806 if width == 0 {
807 if pos.col > 0 {
808 let mut prev_cell = self
809 .grid_mut()
810 .drawing_cell_mut(crate::grid::Pos {
811 row: pos.row,
812 col: pos.col - 1,
813 })
814 .unwrap();
819 if prev_cell.is_wide_continuation() {
820 prev_cell = self
821 .grid_mut()
822 .drawing_cell_mut(crate::grid::Pos {
823 row: pos.row,
824 col: pos.col - 2,
825 })
826 .unwrap();
833 }
834 prev_cell.append(c);
835 } else if pos.row > 0 {
836 let prev_row = self
837 .grid()
838 .drawing_row(pos.row - 1)
839 .unwrap();
844 if prev_row.wrapped() {
845 let mut prev_cell = self
846 .grid_mut()
847 .drawing_cell_mut(crate::grid::Pos {
848 row: pos.row - 1,
849 col: size.cols - 1,
850 })
851 .unwrap();
857 if prev_cell.is_wide_continuation() {
858 prev_cell = self
859 .grid_mut()
860 .drawing_cell_mut(crate::grid::Pos {
861 row: pos.row - 1,
862 col: size.cols - 2,
863 })
864 .unwrap();
873 }
874 prev_cell.append(c);
875 }
876 }
877 } else {
878 if self
879 .grid()
880 .drawing_cell(pos)
881 .unwrap()
886 .is_wide_continuation()
887 {
888 let prev_cell = self
889 .grid_mut()
890 .drawing_cell_mut(crate::grid::Pos {
891 row: pos.row,
892 col: pos.col - 1,
893 })
894 .unwrap();
902 prev_cell.clear(attrs);
903 }
904
905 if self
906 .grid()
907 .drawing_cell(pos)
908 .unwrap()
913 .is_wide()
914 {
915 let next_cell = self
916 .grid_mut()
917 .drawing_cell_mut(crate::grid::Pos {
918 row: pos.row,
919 col: pos.col + 1,
920 })
921 .unwrap();
929 next_cell.set(' ', attrs);
930 }
931
932 let cell = self
933 .grid_mut()
934 .drawing_cell_mut(pos)
935 .unwrap();
940 cell.set(c, attrs);
941 self.grid_mut().col_inc(1);
942 if width > 1 {
943 let pos = self.grid().pos();
944 if self
945 .grid()
946 .drawing_cell(pos)
947 .unwrap()
955 .is_wide()
956 {
957 let next_next_pos = crate::grid::Pos {
958 row: pos.row,
959 col: pos.col + 1,
960 };
961 let next_next_cell = self
962 .grid_mut()
963 .drawing_cell_mut(next_next_pos)
964 .unwrap();
975 next_next_cell.clear(attrs);
976 if next_next_pos.col == size.cols - 1 {
977 self.grid_mut()
978 .drawing_row_mut(pos.row)
979 .unwrap()
981 .wrap(false);
982 }
983 }
984 let next_cell = self
985 .grid_mut()
986 .drawing_cell_mut(pos)
987 .unwrap();
995 next_cell.clear(crate::attrs::Attrs::default());
996 next_cell.set_wide_continuation(true);
997 self.grid_mut().col_inc(1);
998 }
999 }
1000 }
1001
1002 fn bel(&mut self) {
1005 self.audible_bell_count += 1;
1006 }
1007
1008 fn bs(&mut self) {
1009 self.grid_mut().col_dec(1);
1010 }
1011
1012 fn tab(&mut self) {
1013 self.grid_mut().col_tab();
1014 }
1015
1016 fn lf(&mut self) {
1017 self.grid_mut().row_inc_scroll(1);
1018 }
1019
1020 fn vt(&mut self) {
1021 self.lf();
1022 }
1023
1024 fn ff(&mut self) {
1025 self.lf();
1026 }
1027
1028 fn cr(&mut self) {
1029 self.grid_mut().col_set(0);
1030 }
1031
1032 fn decsc(&mut self) {
1036 self.save_cursor();
1037 }
1038
1039 fn decrc(&mut self) {
1041 self.restore_cursor();
1042 }
1043
1044 fn deckpam(&mut self) {
1046 self.set_mode(MODE_APPLICATION_KEYPAD);
1047 }
1048
1049 fn deckpnm(&mut self) {
1051 self.clear_mode(MODE_APPLICATION_KEYPAD);
1052 }
1053
1054 fn ri(&mut self) {
1056 self.grid_mut().row_dec_scroll(1);
1057 }
1058
1059 fn ris(&mut self) {
1061 let title = self.title.clone();
1062 let icon_name = self.icon_name.clone();
1063 let audible_bell_count = self.audible_bell_count;
1064 let visual_bell_count = self.visual_bell_count;
1065 let errors = self.errors;
1066
1067 *self = Self::new(self.grid.size(), self.grid.scrollback_len());
1068
1069 self.title = title;
1070 self.icon_name = icon_name;
1071 self.audible_bell_count = audible_bell_count;
1072 self.visual_bell_count = visual_bell_count;
1073 self.errors = errors;
1074 }
1075
1076 fn vb(&mut self) {
1078 self.visual_bell_count += 1;
1079 }
1080
1081 fn ich(&mut self, count: u16) {
1085 self.grid_mut().insert_cells(count);
1086 }
1087
1088 fn cuu(&mut self, offset: u16) {
1090 self.grid_mut().row_dec_clamp(offset);
1091 }
1092
1093 fn cud(&mut self, offset: u16) {
1095 self.grid_mut().row_inc_clamp(offset);
1096 }
1097
1098 fn cuf(&mut self, offset: u16) {
1100 self.grid_mut().col_inc_clamp(offset);
1101 }
1102
1103 fn cub(&mut self, offset: u16) {
1105 self.grid_mut().col_dec(offset);
1106 }
1107
1108 fn cha(&mut self, col: u16) {
1110 self.grid_mut().col_set(col - 1);
1111 }
1112
1113 fn cup(&mut self, (row, col): (u16, u16)) {
1115 self.grid_mut().set_pos(crate::grid::Pos {
1116 row: row - 1,
1117 col: col - 1,
1118 });
1119 }
1120
1121 fn ed(&mut self, mode: u16) {
1123 let attrs = self.attrs;
1124 match mode {
1125 0 => self.grid_mut().erase_all_forward(attrs),
1126 1 => self.grid_mut().erase_all_backward(attrs),
1127 2 => self.grid_mut().erase_all(attrs),
1128 n => {
1129 log::debug!("unhandled ED mode: {n}");
1130 }
1131 }
1132 }
1133
1134 fn decsed(&mut self, mode: u16) {
1136 self.ed(mode);
1137 }
1138
1139 fn el(&mut self, mode: u16) {
1141 let attrs = self.attrs;
1142 match mode {
1143 0 => self.grid_mut().erase_row_forward(attrs),
1144 1 => self.grid_mut().erase_row_backward(attrs),
1145 2 => self.grid_mut().erase_row(attrs),
1146 n => {
1147 log::debug!("unhandled EL mode: {n}");
1148 }
1149 }
1150 }
1151
1152 fn decsel(&mut self, mode: u16) {
1154 self.el(mode);
1155 }
1156
1157 fn il(&mut self, count: u16) {
1159 self.grid_mut().insert_lines(count);
1160 }
1161
1162 fn dl(&mut self, count: u16) {
1164 self.grid_mut().delete_lines(count);
1165 }
1166
1167 fn dch(&mut self, count: u16) {
1169 self.grid_mut().delete_cells(count);
1170 }
1171
1172 fn su(&mut self, count: u16) {
1174 self.grid_mut().scroll_up(count);
1175 }
1176
1177 fn sd(&mut self, count: u16) {
1179 self.grid_mut().scroll_down(count);
1180 }
1181
1182 fn ech(&mut self, count: u16) {
1184 let attrs = self.attrs;
1185 self.grid_mut().erase_cells(count, attrs);
1186 }
1187
1188 fn vpa(&mut self, row: u16) {
1190 self.grid_mut().row_set(row - 1);
1191 }
1192
1193 #[allow(clippy::unused_self)]
1195 fn sm(&mut self, params: &vte::Params) {
1196 if log::log_enabled!(log::Level::Debug) {
1198 log::debug!("unhandled SM mode: {}", param_str(params));
1199 }
1200 }
1201
1202 fn decset(&mut self, params: &vte::Params) {
1204 for param in params {
1205 match param {
1206 &[1] => self.set_mode(MODE_APPLICATION_CURSOR),
1207 &[6] => self.grid_mut().set_origin_mode(true),
1208 &[9] => self.set_mouse_mode(MouseProtocolMode::Press),
1209 &[25] => self.clear_mode(MODE_HIDE_CURSOR),
1210 &[47] => self.enter_alternate_grid(),
1211 &[1000] => {
1212 self.set_mouse_mode(MouseProtocolMode::PressRelease);
1213 }
1214 &[1002] => {
1215 self.set_mouse_mode(MouseProtocolMode::ButtonMotion);
1216 }
1217 &[1003] => self.set_mouse_mode(MouseProtocolMode::AnyMotion),
1218 &[1005] => {
1219 self.set_mouse_encoding(MouseProtocolEncoding::Utf8);
1220 }
1221 &[1006] => {
1222 self.set_mouse_encoding(MouseProtocolEncoding::Sgr);
1223 }
1224 &[1049] => {
1225 self.decsc();
1226 self.alternate_grid.clear();
1227 self.enter_alternate_grid();
1228 }
1229 &[2004] => self.set_mode(MODE_BRACKETED_PASTE),
1230 ns => {
1231 if log::log_enabled!(log::Level::Debug) {
1232 let n = if ns.len() == 1 {
1233 format!(
1234 "{}",
1235 ns[0]
1238 )
1239 } else {
1240 format!("{ns:?}")
1241 };
1242 log::debug!("unhandled DECSET mode: {n}");
1243 }
1244 }
1245 }
1246 }
1247 }
1248
1249 #[allow(clippy::unused_self)]
1251 fn rm(&mut self, params: &vte::Params) {
1252 if log::log_enabled!(log::Level::Debug) {
1254 log::debug!("unhandled RM mode: {}", param_str(params));
1255 }
1256 }
1257
1258 fn decrst(&mut self, params: &vte::Params) {
1260 for param in params {
1261 match param {
1262 &[1] => self.clear_mode(MODE_APPLICATION_CURSOR),
1263 &[6] => self.grid_mut().set_origin_mode(false),
1264 &[9] => self.clear_mouse_mode(MouseProtocolMode::Press),
1265 &[25] => self.set_mode(MODE_HIDE_CURSOR),
1266 &[47] => {
1267 self.exit_alternate_grid();
1268 }
1269 &[1000] => {
1270 self.clear_mouse_mode(MouseProtocolMode::PressRelease);
1271 }
1272 &[1002] => {
1273 self.clear_mouse_mode(MouseProtocolMode::ButtonMotion);
1274 }
1275 &[1003] => {
1276 self.clear_mouse_mode(MouseProtocolMode::AnyMotion);
1277 }
1278 &[1005] => {
1279 self.clear_mouse_encoding(MouseProtocolEncoding::Utf8);
1280 }
1281 &[1006] => {
1282 self.clear_mouse_encoding(MouseProtocolEncoding::Sgr);
1283 }
1284 &[1049] => {
1285 self.exit_alternate_grid();
1286 self.decrc();
1287 }
1288 &[2004] => self.clear_mode(MODE_BRACKETED_PASTE),
1289 ns => {
1290 if log::log_enabled!(log::Level::Debug) {
1291 let n = if ns.len() == 1 {
1292 format!(
1293 "{}",
1294 ns[0]
1297 )
1298 } else {
1299 format!("{ns:?}")
1300 };
1301 log::debug!("unhandled DECRST mode: {n}");
1302 }
1303 }
1304 }
1305 }
1306 }
1307
1308 fn sgr(&mut self, params: &vte::Params) {
1310 if params.is_empty() {
1314 self.attrs = crate::attrs::Attrs::default();
1315 return;
1316 }
1317
1318 let mut iter = params.iter();
1319
1320 macro_rules! next_param {
1321 () => {
1322 match iter.next() {
1323 Some(n) => n,
1324 _ => return,
1325 }
1326 };
1327 }
1328
1329 macro_rules! to_u8 {
1330 ($n:expr) => {
1331 if let Some(n) = u16_to_u8($n) {
1332 n
1333 } else {
1334 return;
1335 }
1336 };
1337 }
1338
1339 macro_rules! next_param_u8 {
1340 () => {
1341 if let &[n] = next_param!() {
1342 to_u8!(n)
1343 } else {
1344 return;
1345 }
1346 };
1347 }
1348
1349 loop {
1350 match next_param!() {
1351 &[0] => self.attrs = crate::attrs::Attrs::default(),
1352 &[1] => self.attrs.set_bold(true),
1353 &[3] => self.attrs.set_italic(true),
1354 &[4] => self.attrs.set_underline(true),
1355 &[7] => self.attrs.set_inverse(true),
1356 &[22] => self.attrs.set_bold(false),
1357 &[23] => self.attrs.set_italic(false),
1358 &[24] => self.attrs.set_underline(false),
1359 &[27] => self.attrs.set_inverse(false),
1360 &[n] if (30..=37).contains(&n) => {
1361 self.attrs.fgcolor = crate::attrs::Color::Idx(to_u8!(n) - 30);
1362 }
1363 &[38, 2, r, g, b] => {
1364 self.attrs.fgcolor = crate::attrs::Color::Rgb(to_u8!(r), to_u8!(g), to_u8!(b));
1365 }
1366 &[38, 5, i] => {
1367 self.attrs.fgcolor = crate::attrs::Color::Idx(to_u8!(i));
1368 }
1369 &[38] => match next_param!() {
1370 &[2] => {
1371 let r = next_param_u8!();
1372 let g = next_param_u8!();
1373 let b = next_param_u8!();
1374 self.attrs.fgcolor = crate::attrs::Color::Rgb(r, g, b);
1375 }
1376 &[5] => {
1377 self.attrs.fgcolor = crate::attrs::Color::Idx(next_param_u8!());
1378 }
1379 ns => {
1380 if log::log_enabled!(log::Level::Debug) {
1381 let n = if ns.len() == 1 {
1382 format!(
1383 "{}",
1384 ns[0]
1387 )
1388 } else {
1389 format!("{ns:?}")
1390 };
1391 log::debug!("unhandled SGR mode: 38 {n}");
1392 }
1393 return;
1394 }
1395 },
1396 &[39] => {
1397 self.attrs.fgcolor = crate::attrs::Color::Default;
1398 }
1399 &[n] if (40..=47).contains(&n) => {
1400 self.attrs.bgcolor = crate::attrs::Color::Idx(to_u8!(n) - 40);
1401 }
1402 &[48, 2, r, g, b] => {
1403 self.attrs.bgcolor = crate::attrs::Color::Rgb(to_u8!(r), to_u8!(g), to_u8!(b));
1404 }
1405 &[48, 5, i] => {
1406 self.attrs.bgcolor = crate::attrs::Color::Idx(to_u8!(i));
1407 }
1408 &[48] => match next_param!() {
1409 &[2] => {
1410 let r = next_param_u8!();
1411 let g = next_param_u8!();
1412 let b = next_param_u8!();
1413 self.attrs.bgcolor = crate::attrs::Color::Rgb(r, g, b);
1414 }
1415 &[5] => {
1416 self.attrs.bgcolor = crate::attrs::Color::Idx(next_param_u8!());
1417 }
1418 ns => {
1419 if log::log_enabled!(log::Level::Debug) {
1420 let n = if ns.len() == 1 {
1421 format!(
1422 "{}",
1423 ns[0]
1426 )
1427 } else {
1428 format!("{ns:?}")
1429 };
1430 log::debug!("unhandled SGR mode: 48 {n}");
1431 }
1432 return;
1433 }
1434 },
1435 &[49] => {
1436 self.attrs.bgcolor = crate::attrs::Color::Default;
1437 }
1438 &[n] if (90..=97).contains(&n) => {
1439 self.attrs.fgcolor = crate::attrs::Color::Idx(to_u8!(n) - 82);
1440 }
1441 &[n] if (100..=107).contains(&n) => {
1442 self.attrs.bgcolor = crate::attrs::Color::Idx(to_u8!(n) - 92);
1443 }
1444 ns => {
1445 if log::log_enabled!(log::Level::Debug) {
1446 let n = if ns.len() == 1 {
1447 format!(
1448 "{}",
1449 ns[0]
1452 )
1453 } else {
1454 format!("{ns:?}")
1455 };
1456 log::debug!("unhandled SGR mode: {n}");
1457 }
1458 }
1459 }
1460 }
1461 }
1462
1463 fn decstbm(&mut self, (top, bottom): (u16, u16)) {
1465 self.grid_mut().set_scroll_region(top - 1, bottom - 1);
1466 }
1467
1468 fn osc0(&mut self, s: &[u8]) {
1471 self.osc1(s);
1472 self.osc2(s);
1473 }
1474
1475 fn osc1(&mut self, s: &[u8]) {
1476 if let Ok(s) = std::str::from_utf8(s) {
1477 self.icon_name = s.to_string();
1478 }
1479 }
1480
1481 fn osc2(&mut self, s: &[u8]) {
1482 if let Ok(s) = std::str::from_utf8(s) {
1483 self.title = s.to_string();
1484 }
1485 }
1486}
1487
1488impl vte::Perform for Screen {
1489 fn print(&mut self, c: char) {
1490 if c == '\u{fffd}' || ('\u{80}'..'\u{a0}').contains(&c) {
1491 self.errors = self.errors.saturating_add(1);
1492 }
1493 self.text(c);
1494 }
1495
1496 fn execute(&mut self, b: u8) {
1497 match b {
1498 7 => self.bel(),
1499 8 => self.bs(),
1500 9 => self.tab(),
1501 10 => self.lf(),
1502 11 => self.vt(),
1503 12 => self.ff(),
1504 13 => self.cr(),
1505 14 | 15 => {}
1508 _ => {
1509 self.errors = self.errors.saturating_add(1);
1510 log::debug!("unhandled control character: {b}");
1511 }
1512 }
1513 }
1514
1515 fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, b: u8) {
1516 intermediates.first().map_or_else(
1517 || match b {
1518 b'7' => self.decsc(),
1519 b'8' => self.decrc(),
1520 b'=' => self.deckpam(),
1521 b'>' => self.deckpnm(),
1522 b'M' => self.ri(),
1523 b'c' => self.ris(),
1524 b'g' => self.vb(),
1525 _ => {
1526 log::debug!("unhandled escape code: ESC {b}");
1527 }
1528 },
1529 |i| {
1530 log::debug!("unhandled escape code: ESC {i} {b}");
1531 },
1532 );
1533 }
1534
1535 fn csi_dispatch(&mut self, params: &vte::Params, intermediates: &[u8], _ignore: bool, c: char) {
1536 match intermediates.first() {
1537 None => match c {
1538 '@' => self.ich(canonicalize_params_1(params, 1)),
1539 'A' => self.cuu(canonicalize_params_1(params, 1)),
1540 'B' => self.cud(canonicalize_params_1(params, 1)),
1541 'C' => self.cuf(canonicalize_params_1(params, 1)),
1542 'D' => self.cub(canonicalize_params_1(params, 1)),
1543 'G' => self.cha(canonicalize_params_1(params, 1)),
1544 'H' => self.cup(canonicalize_params_2(params, 1, 1)),
1545 'J' => self.ed(canonicalize_params_1(params, 0)),
1546 'K' => self.el(canonicalize_params_1(params, 0)),
1547 'L' => self.il(canonicalize_params_1(params, 1)),
1548 'M' => self.dl(canonicalize_params_1(params, 1)),
1549 'P' => self.dch(canonicalize_params_1(params, 1)),
1550 'S' => self.su(canonicalize_params_1(params, 1)),
1551 'T' => self.sd(canonicalize_params_1(params, 1)),
1552 'X' => self.ech(canonicalize_params_1(params, 1)),
1553 'd' => self.vpa(canonicalize_params_1(params, 1)),
1554 'h' => self.sm(params),
1555 'l' => self.rm(params),
1556 'm' => self.sgr(params),
1557 'r' => self.decstbm(canonicalize_params_decstbm(params, self.grid().size())),
1558 _ => {
1559 if log::log_enabled!(log::Level::Debug) {
1560 log::debug!("unhandled csi sequence: CSI {} {}", param_str(params), c);
1561 }
1562 }
1563 },
1564 Some(b'?') => match c {
1565 'J' => self.decsed(canonicalize_params_1(params, 0)),
1566 'K' => self.decsel(canonicalize_params_1(params, 0)),
1567 'h' => self.decset(params),
1568 'l' => self.decrst(params),
1569 _ => {
1570 if log::log_enabled!(log::Level::Debug) {
1571 log::debug!("unhandled csi sequence: CSI ? {} {}", param_str(params), c);
1572 }
1573 }
1574 },
1575 Some(i) => {
1576 if log::log_enabled!(log::Level::Debug) {
1577 log::debug!(
1578 "unhandled csi sequence: CSI {} {} {}",
1579 i,
1580 param_str(params),
1581 c
1582 );
1583 }
1584 }
1585 }
1586 }
1587
1588 fn osc_dispatch(&mut self, params: &[&[u8]], _bel_terminated: bool) {
1589 match (params.get(0), params.get(1)) {
1590 (Some(&b"0"), Some(s)) => self.osc0(s),
1591 (Some(&b"1"), Some(s)) => self.osc1(s),
1592 (Some(&b"2"), Some(s)) => self.osc2(s),
1593 _ => {
1594 if log::log_enabled!(log::Level::Debug) {
1595 log::debug!("unhandled osc sequence: OSC {}", osc_param_str(params),);
1596 }
1597 }
1598 }
1599 }
1600
1601 fn hook(&mut self, params: &vte::Params, intermediates: &[u8], _ignore: bool, action: char) {
1602 if log::log_enabled!(log::Level::Debug) {
1603 intermediates.first().map_or_else(
1604 || {
1605 log::debug!(
1606 "unhandled dcs sequence: DCS {} {}",
1607 param_str(params),
1608 action,
1609 );
1610 },
1611 |i| {
1612 log::debug!(
1613 "unhandled dcs sequence: DCS {} {} {}",
1614 i,
1615 param_str(params),
1616 action,
1617 );
1618 },
1619 );
1620 }
1621 }
1622}
1623
1624fn canonicalize_params_1(params: &vte::Params, default: u16) -> u16 {
1625 let first = params.iter().next().map_or(0, |x| *x.first().unwrap_or(&0));
1626 if first == 0 {
1627 default
1628 } else {
1629 first
1630 }
1631}
1632
1633fn canonicalize_params_2(params: &vte::Params, default1: u16, default2: u16) -> (u16, u16) {
1634 let mut iter = params.iter();
1635 let first = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
1636 let first = if first == 0 { default1 } else { first };
1637
1638 let second = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
1639 let second = if second == 0 { default2 } else { second };
1640
1641 (first, second)
1642}
1643
1644fn canonicalize_params_decstbm(params: &vte::Params, size: crate::grid::Size) -> (u16, u16) {
1645 let mut iter = params.iter();
1646 let top = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
1647 let top = if top == 0 { 1 } else { top };
1648
1649 let bottom = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
1650 let bottom = if bottom == 0 { size.rows } else { bottom };
1651
1652 (top, bottom)
1653}
1654
1655fn u16_to_u8(i: u16) -> Option<u8> {
1656 if i > u16::from(u8::max_value()) {
1657 None
1658 } else {
1659 Some(i.try_into().unwrap())
1661 }
1662}
1663
1664fn param_str(params: &vte::Params) -> String {
1665 let strs: Vec<_> = params
1666 .iter()
1667 .map(|subparams| {
1668 let subparam_strs: Vec<_> = subparams
1669 .iter()
1670 .map(std::string::ToString::to_string)
1671 .collect();
1672 subparam_strs.join(" : ")
1673 })
1674 .collect();
1675 strs.join(" ; ")
1676}
1677
1678fn osc_param_str(params: &[&[u8]]) -> String {
1679 let strs: Vec<_> = params
1680 .iter()
1681 .map(|b| format!("\"{}\"", std::string::String::from_utf8_lossy(b)))
1682 .collect();
1683 strs.join(" ; ")
1684}