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(
87 size: crate::grid::Size,
88 scrollback_len: usize,
89 ) -> Self {
90 let mut grid = crate::grid::Grid::new(size, scrollback_len);
91 grid.allocate_rows();
92 Self {
93 grid,
94 alternate_grid: crate::grid::Grid::new(size, 0),
95
96 attrs: crate::attrs::Attrs::default(),
97 saved_attrs: crate::attrs::Attrs::default(),
98
99 title: String::default(),
100 icon_name: String::default(),
101
102 modes: 0,
103 mouse_protocol_mode: MouseProtocolMode::default(),
104 mouse_protocol_encoding: MouseProtocolEncoding::default(),
105
106 audible_bell_count: 0,
107 visual_bell_count: 0,
108
109 errors: 0,
110 }
111 }
112
113 pub(crate) fn set_size(&mut self, rows: u16, cols: u16) {
114 self.grid.set_size(crate::grid::Size { rows, cols });
115 self.alternate_grid
116 .set_size(crate::grid::Size { rows, cols });
117 }
118
119 #[must_use]
123 pub fn size(&self) -> (u16, u16) {
124 let size = self.grid().size();
125 (size.rows, size.cols)
126 }
127
128 #[must_use]
133 pub fn scrollback(&self) -> usize {
134 self.grid().scrollback()
135 }
136
137 pub(crate) fn set_scrollback(&mut self, rows: usize) {
138 self.grid_mut().set_scrollback(rows);
139 }
140
141 #[must_use]
146 pub fn contents(&self) -> String {
147 let mut contents = String::new();
148 self.write_contents(&mut contents);
149 contents
150 }
151
152 fn write_contents(&self, contents: &mut String) {
153 self.grid().write_contents(contents);
154 }
155
156 pub fn rows(
164 &self,
165 start: u16,
166 width: u16,
167 ) -> impl Iterator<Item = String> + '_ {
168 self.grid().visible_rows().map(move |row| {
169 let mut contents = String::new();
170 row.write_contents(&mut contents, start, width, false);
171 contents
172 })
173 }
174
175 #[must_use]
182 pub fn contents_between(
183 &self,
184 start_row: u16,
185 start_col: u16,
186 end_row: u16,
187 end_col: u16,
188 ) -> String {
189 match start_row.cmp(&end_row) {
190 std::cmp::Ordering::Less => {
191 let (_, cols) = self.size();
192 let mut contents = String::new();
193 for (i, row) in self
194 .grid()
195 .visible_rows()
196 .enumerate()
197 .skip(usize::from(start_row))
198 .take(usize::from(end_row) - usize::from(start_row) + 1)
199 {
200 if i == usize::from(start_row) {
201 row.write_contents(
202 &mut contents,
203 start_col,
204 cols - start_col,
205 false,
206 );
207 if !row.wrapped() {
208 contents.push('\n');
209 }
210 } else if i == usize::from(end_row) {
211 row.write_contents(&mut contents, 0, end_col, false);
212 } else {
213 row.write_contents(&mut contents, 0, cols, false);
214 if !row.wrapped() {
215 contents.push('\n');
216 }
217 }
218 }
219 contents
220 }
221 std::cmp::Ordering::Equal => {
222 if start_col < end_col {
223 self.rows(start_col, end_col - start_col)
224 .nth(usize::from(start_row))
225 .unwrap_or_default()
226 } else {
227 String::new()
228 }
229 }
230 std::cmp::Ordering::Greater => String::new(),
231 }
232 }
233
234 #[must_use]
238 pub fn state_formatted(&self) -> Vec<u8> {
239 let mut contents = vec![];
240 self.write_contents_formatted(&mut contents);
241 self.write_input_mode_formatted(&mut contents);
242 self.write_title_formatted(&mut contents);
243 contents
244 }
245
246 #[must_use]
251 pub fn state_diff(&self, prev: &Self) -> Vec<u8> {
252 let mut contents = vec![];
253 self.write_contents_diff(&mut contents, prev);
254 self.write_input_mode_diff(&mut contents, prev);
255 self.write_title_diff(&mut contents, prev);
256 self.write_bells_diff(&mut contents, prev);
257 contents
258 }
259
260 #[must_use]
266 pub fn contents_formatted(&self) -> Vec<u8> {
267 let mut contents = vec![];
268 self.write_contents_formatted(&mut contents);
269 contents
270 }
271
272 fn write_contents_formatted(&self, contents: &mut Vec<u8>) {
273 crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
274 let prev_attrs = self.grid().write_contents_formatted(contents);
275 self.attrs.write_escape_code_diff(contents, &prev_attrs);
276 }
277
278 #[allow(clippy::missing_panics_doc)]
290 pub fn rows_formatted(
291 &self,
292 start: u16,
293 width: u16,
294 ) -> impl Iterator<Item = Vec<u8>> + '_ {
295 let mut wrapping = false;
296 self.grid().visible_rows().enumerate().map(move |(i, row)| {
297 let i = i.try_into().unwrap();
300 let mut contents = vec![];
301 row.write_contents_formatted(
302 &mut contents,
303 start,
304 width,
305 i,
306 wrapping,
307 None,
308 None,
309 );
310 if start == 0 && width == self.grid.size().cols {
311 wrapping = row.wrapped();
312 }
313 contents
314 })
315 }
316
317 #[must_use]
328 pub fn contents_diff(&self, prev: &Self) -> Vec<u8> {
329 let mut contents = vec![];
330 self.write_contents_diff(&mut contents, prev);
331 contents
332 }
333
334 fn write_contents_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
335 if self.hide_cursor() != prev.hide_cursor() {
336 crate::term::HideCursor::new(self.hide_cursor())
337 .write_buf(contents);
338 }
339 let prev_attrs = self.grid().write_contents_diff(
340 contents,
341 prev.grid(),
342 prev.attrs,
343 );
344 self.attrs.write_escape_code_diff(contents, &prev_attrs);
345 }
346
347 #[allow(clippy::missing_panics_doc)]
357 pub fn rows_diff<'a>(
358 &'a self,
359 prev: &'a Self,
360 start: u16,
361 width: u16,
362 ) -> impl Iterator<Item = Vec<u8>> + 'a {
363 self.grid()
364 .visible_rows()
365 .zip(prev.grid().visible_rows())
366 .enumerate()
367 .map(move |(i, (row, prev_row))| {
368 let i = i.try_into().unwrap();
371 let mut contents = vec![];
372 row.write_contents_diff(
373 &mut contents,
374 prev_row,
375 start,
376 width,
377 i,
378 false,
379 false,
380 crate::grid::Pos { row: i, col: start },
381 crate::attrs::Attrs::default(),
382 );
383 contents
384 })
385 }
386
387 #[must_use]
396 pub fn input_mode_formatted(&self) -> Vec<u8> {
397 let mut contents = vec![];
398 self.write_input_mode_formatted(&mut contents);
399 contents
400 }
401
402 fn write_input_mode_formatted(&self, contents: &mut Vec<u8>) {
403 crate::term::ApplicationKeypad::new(
404 self.mode(MODE_APPLICATION_KEYPAD),
405 )
406 .write_buf(contents);
407 crate::term::ApplicationCursor::new(
408 self.mode(MODE_APPLICATION_CURSOR),
409 )
410 .write_buf(contents);
411 crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
412 .write_buf(contents);
413 crate::term::MouseProtocolMode::new(
414 self.mouse_protocol_mode,
415 MouseProtocolMode::None,
416 )
417 .write_buf(contents);
418 crate::term::MouseProtocolEncoding::new(
419 self.mouse_protocol_encoding,
420 MouseProtocolEncoding::Default,
421 )
422 .write_buf(contents);
423 }
424
425 #[must_use]
429 pub fn input_mode_diff(&self, prev: &Self) -> Vec<u8> {
430 let mut contents = vec![];
431 self.write_input_mode_diff(&mut contents, prev);
432 contents
433 }
434
435 fn write_input_mode_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
436 if self.mode(MODE_APPLICATION_KEYPAD)
437 != prev.mode(MODE_APPLICATION_KEYPAD)
438 {
439 crate::term::ApplicationKeypad::new(
440 self.mode(MODE_APPLICATION_KEYPAD),
441 )
442 .write_buf(contents);
443 }
444 if self.mode(MODE_APPLICATION_CURSOR)
445 != prev.mode(MODE_APPLICATION_CURSOR)
446 {
447 crate::term::ApplicationCursor::new(
448 self.mode(MODE_APPLICATION_CURSOR),
449 )
450 .write_buf(contents);
451 }
452 if self.mode(MODE_BRACKETED_PASTE) != prev.mode(MODE_BRACKETED_PASTE)
453 {
454 crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
455 .write_buf(contents);
456 }
457 crate::term::MouseProtocolMode::new(
458 self.mouse_protocol_mode,
459 prev.mouse_protocol_mode,
460 )
461 .write_buf(contents);
462 crate::term::MouseProtocolEncoding::new(
463 self.mouse_protocol_encoding,
464 prev.mouse_protocol_encoding,
465 )
466 .write_buf(contents);
467 }
468
469 #[must_use]
472 pub fn title_formatted(&self) -> Vec<u8> {
473 let mut contents = vec![];
474 self.write_title_formatted(&mut contents);
475 contents
476 }
477
478 fn write_title_formatted(&self, contents: &mut Vec<u8>) {
479 crate::term::ChangeTitle::new(&self.icon_name, &self.title, "", "")
480 .write_buf(contents);
481 }
482
483 #[must_use]
487 pub fn title_diff(&self, prev: &Self) -> Vec<u8> {
488 let mut contents = vec![];
489 self.write_title_diff(&mut contents, prev);
490 contents
491 }
492
493 fn write_title_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
494 crate::term::ChangeTitle::new(
495 &self.icon_name,
496 &self.title,
497 &prev.icon_name,
498 &prev.title,
499 )
500 .write_buf(contents);
501 }
502
503 #[must_use]
507 pub fn bells_diff(&self, prev: &Self) -> Vec<u8> {
508 let mut contents = vec![];
509 self.write_bells_diff(&mut contents, prev);
510 contents
511 }
512
513 fn write_bells_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
514 if self.audible_bell_count != prev.audible_bell_count {
515 crate::term::AudibleBell::default().write_buf(contents);
516 }
517 if self.visual_bell_count != prev.visual_bell_count {
518 crate::term::VisualBell::default().write_buf(contents);
519 }
520 }
521
522 #[must_use]
539 pub fn attributes_formatted(&self) -> Vec<u8> {
540 let mut contents = vec![];
541 self.write_attributes_formatted(&mut contents);
542 contents
543 }
544
545 fn write_attributes_formatted(&self, contents: &mut Vec<u8>) {
546 crate::term::ClearAttrs::default().write_buf(contents);
547 self.attrs.write_escape_code_diff(
548 contents,
549 &crate::attrs::Attrs::default(),
550 );
551 }
552
553 #[must_use]
557 pub fn cursor_position(&self) -> (u16, u16) {
558 let pos = self.grid().pos();
559 (pos.row, pos.col)
560 }
561
562 #[must_use]
578 pub fn cursor_state_formatted(&self) -> Vec<u8> {
579 let mut contents = vec![];
580 self.write_cursor_state_formatted(&mut contents);
581 contents
582 }
583
584 fn write_cursor_state_formatted(&self, contents: &mut Vec<u8>) {
585 crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
586 self.grid()
587 .write_cursor_position_formatted(contents, None, None);
588
589 }
596
597 #[must_use]
600 pub fn cell(&self, row: u16, col: u16) -> Option<&crate::cell::Cell> {
601 self.grid().visible_cell(crate::grid::Pos { row, col })
602 }
603
604 #[must_use]
606 pub fn row_wrapped(&self, row: u16) -> bool {
607 self.grid()
608 .visible_row(row)
609 .map_or(false, crate::row::Row::wrapped)
610 }
611
612 #[must_use]
614 pub fn title(&self) -> &str {
615 &self.title
616 }
617
618 #[must_use]
620 pub fn icon_name(&self) -> &str {
621 &self.icon_name
622 }
623
624 #[must_use]
633 pub fn audible_bell_count(&self) -> usize {
634 self.audible_bell_count
635 }
636
637 #[must_use]
646 pub fn visual_bell_count(&self) -> usize {
647 self.visual_bell_count
648 }
649
650 #[must_use]
656 pub fn errors(&self) -> usize {
657 self.errors
658 }
659
660 #[must_use]
662 pub fn alternate_screen(&self) -> bool {
663 self.mode(MODE_ALTERNATE_SCREEN)
664 }
665
666 #[must_use]
668 pub fn application_keypad(&self) -> bool {
669 self.mode(MODE_APPLICATION_KEYPAD)
670 }
671
672 #[must_use]
674 pub fn application_cursor(&self) -> bool {
675 self.mode(MODE_APPLICATION_CURSOR)
676 }
677
678 #[must_use]
680 pub fn hide_cursor(&self) -> bool {
681 self.mode(MODE_HIDE_CURSOR)
682 }
683
684 #[must_use]
686 pub fn bracketed_paste(&self) -> bool {
687 self.mode(MODE_BRACKETED_PASTE)
688 }
689
690 #[must_use]
692 pub fn mouse_protocol_mode(&self) -> MouseProtocolMode {
693 self.mouse_protocol_mode
694 }
695
696 #[must_use]
698 pub fn mouse_protocol_encoding(&self) -> MouseProtocolEncoding {
699 self.mouse_protocol_encoding
700 }
701
702 #[must_use]
704 pub fn fgcolor(&self) -> crate::attrs::Color {
705 self.attrs.fgcolor
706 }
707
708 #[must_use]
710 pub fn bgcolor(&self) -> crate::attrs::Color {
711 self.attrs.bgcolor
712 }
713
714 #[must_use]
717 pub fn bold(&self) -> bool {
718 self.attrs.bold()
719 }
720
721 #[must_use]
724 pub fn italic(&self) -> bool {
725 self.attrs.italic()
726 }
727
728 #[must_use]
731 pub fn underline(&self) -> bool {
732 self.attrs.underline()
733 }
734
735 #[must_use]
738 pub fn inverse(&self) -> bool {
739 self.attrs.inverse()
740 }
741
742 fn grid(&self) -> &crate::grid::Grid {
743 if self.mode(MODE_ALTERNATE_SCREEN) {
744 &self.alternate_grid
745 } else {
746 &self.grid
747 }
748 }
749
750 fn grid_mut(&mut self) -> &mut crate::grid::Grid {
751 if self.mode(MODE_ALTERNATE_SCREEN) {
752 &mut self.alternate_grid
753 } else {
754 &mut self.grid
755 }
756 }
757
758 fn enter_alternate_grid(&mut self) {
759 self.grid_mut().set_scrollback(0);
760 self.set_mode(MODE_ALTERNATE_SCREEN);
761 self.alternate_grid.allocate_rows();
762 }
763
764 fn exit_alternate_grid(&mut self) {
765 self.clear_mode(MODE_ALTERNATE_SCREEN);
766 }
767
768 fn save_cursor(&mut self) {
769 self.grid_mut().save_cursor();
770 self.saved_attrs = self.attrs;
771 }
772
773 fn restore_cursor(&mut self) {
774 self.grid_mut().restore_cursor();
775 self.attrs = self.saved_attrs;
776 }
777
778 fn set_mode(&mut self, mode: u8) {
779 self.modes |= mode;
780 }
781
782 fn clear_mode(&mut self, mode: u8) {
783 self.modes &= !mode;
784 }
785
786 fn mode(&self, mode: u8) -> bool {
787 self.modes & mode != 0
788 }
789
790 fn set_mouse_mode(&mut self, mode: MouseProtocolMode) {
791 self.mouse_protocol_mode = mode;
792 }
793
794 fn clear_mouse_mode(&mut self, mode: MouseProtocolMode) {
795 if self.mouse_protocol_mode == mode {
796 self.mouse_protocol_mode = MouseProtocolMode::default();
797 }
798 }
799
800 fn set_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
801 self.mouse_protocol_encoding = encoding;
802 }
803
804 fn clear_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
805 if self.mouse_protocol_encoding == encoding {
806 self.mouse_protocol_encoding = MouseProtocolEncoding::default();
807 }
808 }
809}
810
811impl Screen {
812 fn text(&mut self, c: char) {
813 let pos = self.grid().pos();
814 let size = self.grid().size();
815 let attrs = self.attrs;
816
817 let width = c.width();
818 if width.is_none() && (u32::from(c)) < 256 {
819 return;
821 }
822 let width = width
823 .unwrap_or(1)
824 .try_into()
825 .unwrap();
827
828 let mut wrap = false;
837 if pos.col > size.cols - width {
838 let last_cell = self
839 .grid()
840 .drawing_cell(crate::grid::Pos {
841 row: pos.row,
842 col: size.cols - 1,
843 })
844 .unwrap();
848 if last_cell.has_contents() || last_cell.is_wide_continuation() {
849 wrap = true;
850 }
851 }
852 self.grid_mut().col_wrap(width, wrap);
853 let pos = self.grid().pos();
854
855 if width == 0 {
856 if pos.col > 0 {
857 let mut prev_cell = self
858 .grid_mut()
859 .drawing_cell_mut(crate::grid::Pos {
860 row: pos.row,
861 col: pos.col - 1,
862 })
863 .unwrap();
868 if prev_cell.is_wide_continuation() {
869 prev_cell = self
870 .grid_mut()
871 .drawing_cell_mut(crate::grid::Pos {
872 row: pos.row,
873 col: pos.col - 2,
874 })
875 .unwrap();
882 }
883 prev_cell.append(c);
884 } else if pos.row > 0 {
885 let prev_row = self
886 .grid()
887 .drawing_row(pos.row - 1)
888 .unwrap();
893 if prev_row.wrapped() {
894 let mut prev_cell = self
895 .grid_mut()
896 .drawing_cell_mut(crate::grid::Pos {
897 row: pos.row - 1,
898 col: size.cols - 1,
899 })
900 .unwrap();
906 if prev_cell.is_wide_continuation() {
907 prev_cell = self
908 .grid_mut()
909 .drawing_cell_mut(crate::grid::Pos {
910 row: pos.row - 1,
911 col: size.cols - 2,
912 })
913 .unwrap();
922 }
923 prev_cell.append(c);
924 }
925 }
926 } else {
927 if self
928 .grid()
929 .drawing_cell(pos)
930 .unwrap()
935 .is_wide_continuation()
936 {
937 let prev_cell = self
938 .grid_mut()
939 .drawing_cell_mut(crate::grid::Pos {
940 row: pos.row,
941 col: pos.col - 1,
942 })
943 .unwrap();
951 prev_cell.clear(attrs);
952 }
953
954 if self
955 .grid()
956 .drawing_cell(pos)
957 .unwrap()
962 .is_wide()
963 {
964 let next_cell = self
965 .grid_mut()
966 .drawing_cell_mut(crate::grid::Pos {
967 row: pos.row,
968 col: pos.col + 1,
969 })
970 .unwrap();
978 next_cell.set(' ', attrs);
979 }
980
981 let cell = self
982 .grid_mut()
983 .drawing_cell_mut(pos)
984 .unwrap();
989 cell.set(c, attrs);
990 self.grid_mut().col_inc(1);
991 if width > 1 {
992 let pos = self.grid().pos();
993 if self
994 .grid()
995 .drawing_cell(pos)
996 .unwrap()
1004 .is_wide()
1005 {
1006 let next_next_pos = crate::grid::Pos {
1007 row: pos.row,
1008 col: pos.col + 1,
1009 };
1010 let next_next_cell = self
1011 .grid_mut()
1012 .drawing_cell_mut(next_next_pos)
1013 .unwrap();
1024 next_next_cell.clear(attrs);
1025 if next_next_pos.col == size.cols - 1 {
1026 self.grid_mut()
1027 .drawing_row_mut(pos.row)
1028 .unwrap()
1030 .wrap(false);
1031 }
1032 }
1033 let next_cell = self
1034 .grid_mut()
1035 .drawing_cell_mut(pos)
1036 .unwrap();
1044 next_cell.clear(crate::attrs::Attrs::default());
1045 next_cell.set_wide_continuation(true);
1046 self.grid_mut().col_inc(1);
1047 }
1048 }
1049 }
1050
1051 fn bel(&mut self) {
1054 self.audible_bell_count += 1;
1055 }
1056
1057 fn bs(&mut self) {
1058 self.grid_mut().col_dec(1);
1059 }
1060
1061 fn tab(&mut self) {
1062 self.grid_mut().col_tab();
1063 }
1064
1065 fn lf(&mut self) {
1066 self.grid_mut().row_inc_scroll(1);
1067 }
1068
1069 fn vt(&mut self) {
1070 self.lf();
1071 }
1072
1073 fn ff(&mut self) {
1074 self.lf();
1075 }
1076
1077 fn cr(&mut self) {
1078 self.grid_mut().col_set(0);
1079 }
1080
1081 fn decsc(&mut self) {
1085 self.save_cursor();
1086 }
1087
1088 fn decrc(&mut self) {
1090 self.restore_cursor();
1091 }
1092
1093 fn deckpam(&mut self) {
1095 self.set_mode(MODE_APPLICATION_KEYPAD);
1096 }
1097
1098 fn deckpnm(&mut self) {
1100 self.clear_mode(MODE_APPLICATION_KEYPAD);
1101 }
1102
1103 fn ri(&mut self) {
1105 self.grid_mut().row_dec_scroll(1);
1106 }
1107
1108 fn ris(&mut self) {
1110 let title = self.title.clone();
1111 let icon_name = self.icon_name.clone();
1112 let audible_bell_count = self.audible_bell_count;
1113 let visual_bell_count = self.visual_bell_count;
1114 let errors = self.errors;
1115
1116 *self = Self::new(self.grid.size(), self.grid.scrollback_len());
1117
1118 self.title = title;
1119 self.icon_name = icon_name;
1120 self.audible_bell_count = audible_bell_count;
1121 self.visual_bell_count = visual_bell_count;
1122 self.errors = errors;
1123 }
1124
1125 fn vb(&mut self) {
1127 self.visual_bell_count += 1;
1128 }
1129
1130 fn ich(&mut self, count: u16) {
1134 self.grid_mut().insert_cells(count);
1135 }
1136
1137 fn cuu(&mut self, offset: u16) {
1139 self.grid_mut().row_dec_clamp(offset);
1140 }
1141
1142 fn cud(&mut self, offset: u16) {
1144 self.grid_mut().row_inc_clamp(offset);
1145 }
1146
1147 fn cuf(&mut self, offset: u16) {
1149 self.grid_mut().col_inc_clamp(offset);
1150 }
1151
1152 fn cub(&mut self, offset: u16) {
1154 self.grid_mut().col_dec(offset);
1155 }
1156
1157 fn cha(&mut self, col: u16) {
1159 self.grid_mut().col_set(col - 1);
1160 }
1161
1162 fn cup(&mut self, (row, col): (u16, u16)) {
1164 self.grid_mut().set_pos(crate::grid::Pos {
1165 row: row - 1,
1166 col: col - 1,
1167 });
1168 }
1169
1170 fn ed(&mut self, mode: u16) {
1172 let attrs = self.attrs;
1173 match mode {
1174 0 => self.grid_mut().erase_all_forward(attrs),
1175 1 => self.grid_mut().erase_all_backward(attrs),
1176 2 => self.grid_mut().erase_all(attrs),
1177 n => {
1178 log::debug!("unhandled ED mode: {n}");
1179 }
1180 }
1181 }
1182
1183 fn decsed(&mut self, mode: u16) {
1185 self.ed(mode);
1186 }
1187
1188 fn el(&mut self, mode: u16) {
1190 let attrs = self.attrs;
1191 match mode {
1192 0 => self.grid_mut().erase_row_forward(attrs),
1193 1 => self.grid_mut().erase_row_backward(attrs),
1194 2 => self.grid_mut().erase_row(attrs),
1195 n => {
1196 log::debug!("unhandled EL mode: {n}");
1197 }
1198 }
1199 }
1200
1201 fn decsel(&mut self, mode: u16) {
1203 self.el(mode);
1204 }
1205
1206 fn il(&mut self, count: u16) {
1208 self.grid_mut().insert_lines(count);
1209 }
1210
1211 fn dl(&mut self, count: u16) {
1213 self.grid_mut().delete_lines(count);
1214 }
1215
1216 fn dch(&mut self, count: u16) {
1218 self.grid_mut().delete_cells(count);
1219 }
1220
1221 fn su(&mut self, count: u16) {
1223 self.grid_mut().scroll_up(count);
1224 }
1225
1226 fn sd(&mut self, count: u16) {
1228 self.grid_mut().scroll_down(count);
1229 }
1230
1231 fn ech(&mut self, count: u16) {
1233 let attrs = self.attrs;
1234 self.grid_mut().erase_cells(count, attrs);
1235 }
1236
1237 fn vpa(&mut self, row: u16) {
1239 self.grid_mut().row_set(row - 1);
1240 }
1241
1242 #[allow(clippy::unused_self)]
1244 fn sm(&mut self, params: &vte::Params) {
1245 if log::log_enabled!(log::Level::Debug) {
1247 log::debug!("unhandled SM mode: {}", param_str(params));
1248 }
1249 }
1250
1251 fn decset(&mut self, params: &vte::Params) {
1253 for param in params {
1254 match param {
1255 &[1] => self.set_mode(MODE_APPLICATION_CURSOR),
1256 &[6] => self.grid_mut().set_origin_mode(true),
1257 &[9] => self.set_mouse_mode(MouseProtocolMode::Press),
1258 &[25] => self.clear_mode(MODE_HIDE_CURSOR),
1259 &[47] => self.enter_alternate_grid(),
1260 &[1000] => {
1261 self.set_mouse_mode(MouseProtocolMode::PressRelease);
1262 }
1263 &[1002] => {
1264 self.set_mouse_mode(MouseProtocolMode::ButtonMotion);
1265 }
1266 &[1003] => self.set_mouse_mode(MouseProtocolMode::AnyMotion),
1267 &[1005] => {
1268 self.set_mouse_encoding(MouseProtocolEncoding::Utf8);
1269 }
1270 &[1006] => {
1271 self.set_mouse_encoding(MouseProtocolEncoding::Sgr);
1272 }
1273 &[1049] => {
1274 self.decsc();
1275 self.alternate_grid.clear();
1276 self.enter_alternate_grid();
1277 }
1278 &[2004] => self.set_mode(MODE_BRACKETED_PASTE),
1279 ns => {
1280 if log::log_enabled!(log::Level::Debug) {
1281 let n = if ns.len() == 1 {
1282 format!(
1283 "{}",
1284 ns[0]
1287 )
1288 } else {
1289 format!("{ns:?}")
1290 };
1291 log::debug!("unhandled DECSET mode: {n}");
1292 }
1293 }
1294 }
1295 }
1296 }
1297
1298 #[allow(clippy::unused_self)]
1300 fn rm(&mut self, params: &vte::Params) {
1301 if log::log_enabled!(log::Level::Debug) {
1303 log::debug!("unhandled RM mode: {}", param_str(params));
1304 }
1305 }
1306
1307 fn decrst(&mut self, params: &vte::Params) {
1309 for param in params {
1310 match param {
1311 &[1] => self.clear_mode(MODE_APPLICATION_CURSOR),
1312 &[6] => self.grid_mut().set_origin_mode(false),
1313 &[9] => self.clear_mouse_mode(MouseProtocolMode::Press),
1314 &[25] => self.set_mode(MODE_HIDE_CURSOR),
1315 &[47] => {
1316 self.exit_alternate_grid();
1317 }
1318 &[1000] => {
1319 self.clear_mouse_mode(MouseProtocolMode::PressRelease);
1320 }
1321 &[1002] => {
1322 self.clear_mouse_mode(MouseProtocolMode::ButtonMotion);
1323 }
1324 &[1003] => {
1325 self.clear_mouse_mode(MouseProtocolMode::AnyMotion);
1326 }
1327 &[1005] => {
1328 self.clear_mouse_encoding(MouseProtocolEncoding::Utf8);
1329 }
1330 &[1006] => {
1331 self.clear_mouse_encoding(MouseProtocolEncoding::Sgr);
1332 }
1333 &[1049] => {
1334 self.exit_alternate_grid();
1335 self.decrc();
1336 }
1337 &[2004] => self.clear_mode(MODE_BRACKETED_PASTE),
1338 ns => {
1339 if log::log_enabled!(log::Level::Debug) {
1340 let n = if ns.len() == 1 {
1341 format!(
1342 "{}",
1343 ns[0]
1346 )
1347 } else {
1348 format!("{ns:?}")
1349 };
1350 log::debug!("unhandled DECRST mode: {n}");
1351 }
1352 }
1353 }
1354 }
1355 }
1356
1357 fn sgr(&mut self, params: &vte::Params) {
1359 if params.is_empty() {
1363 self.attrs = crate::attrs::Attrs::default();
1364 return;
1365 }
1366
1367 let mut iter = params.iter();
1368
1369 macro_rules! next_param {
1370 () => {
1371 match iter.next() {
1372 Some(n) => n,
1373 _ => return,
1374 }
1375 };
1376 }
1377
1378 macro_rules! to_u8 {
1379 ($n:expr) => {
1380 if let Some(n) = u16_to_u8($n) {
1381 n
1382 } else {
1383 return;
1384 }
1385 };
1386 }
1387
1388 macro_rules! next_param_u8 {
1389 () => {
1390 if let &[n] = next_param!() {
1391 to_u8!(n)
1392 } else {
1393 return;
1394 }
1395 };
1396 }
1397
1398 loop {
1399 match next_param!() {
1400 &[0] => self.attrs = crate::attrs::Attrs::default(),
1401 &[1] => self.attrs.set_bold(true),
1402 &[3] => self.attrs.set_italic(true),
1403 &[4] => self.attrs.set_underline(true),
1404 &[7] => self.attrs.set_inverse(true),
1405 &[22] => self.attrs.set_bold(false),
1406 &[23] => self.attrs.set_italic(false),
1407 &[24] => self.attrs.set_underline(false),
1408 &[27] => self.attrs.set_inverse(false),
1409 &[n] if (30..=37).contains(&n) => {
1410 self.attrs.fgcolor =
1411 crate::attrs::Color::Idx(to_u8!(n) - 30);
1412 }
1413 &[38, 2, r, g, b] => {
1414 self.attrs.fgcolor = crate::attrs::Color::Rgb(
1415 to_u8!(r),
1416 to_u8!(g),
1417 to_u8!(b),
1418 );
1419 }
1420 &[38, 5, i] => {
1421 self.attrs.fgcolor = crate::attrs::Color::Idx(to_u8!(i));
1422 }
1423 &[38] => match next_param!() {
1424 &[2] => {
1425 let r = next_param_u8!();
1426 let g = next_param_u8!();
1427 let b = next_param_u8!();
1428 self.attrs.fgcolor =
1429 crate::attrs::Color::Rgb(r, g, b);
1430 }
1431 &[5] => {
1432 self.attrs.fgcolor =
1433 crate::attrs::Color::Idx(next_param_u8!());
1434 }
1435 ns => {
1436 if log::log_enabled!(log::Level::Debug) {
1437 let n = if ns.len() == 1 {
1438 format!(
1439 "{}",
1440 ns[0]
1443 )
1444 } else {
1445 format!("{ns:?}")
1446 };
1447 log::debug!("unhandled SGR mode: 38 {n}");
1448 }
1449 return;
1450 }
1451 },
1452 &[39] => {
1453 self.attrs.fgcolor = crate::attrs::Color::Default;
1454 }
1455 &[n] if (40..=47).contains(&n) => {
1456 self.attrs.bgcolor =
1457 crate::attrs::Color::Idx(to_u8!(n) - 40);
1458 }
1459 &[48, 2, r, g, b] => {
1460 self.attrs.bgcolor = crate::attrs::Color::Rgb(
1461 to_u8!(r),
1462 to_u8!(g),
1463 to_u8!(b),
1464 );
1465 }
1466 &[48, 5, i] => {
1467 self.attrs.bgcolor = crate::attrs::Color::Idx(to_u8!(i));
1468 }
1469 &[48] => match next_param!() {
1470 &[2] => {
1471 let r = next_param_u8!();
1472 let g = next_param_u8!();
1473 let b = next_param_u8!();
1474 self.attrs.bgcolor =
1475 crate::attrs::Color::Rgb(r, g, b);
1476 }
1477 &[5] => {
1478 self.attrs.bgcolor =
1479 crate::attrs::Color::Idx(next_param_u8!());
1480 }
1481 ns => {
1482 if log::log_enabled!(log::Level::Debug) {
1483 let n = if ns.len() == 1 {
1484 format!(
1485 "{}",
1486 ns[0]
1489 )
1490 } else {
1491 format!("{ns:?}")
1492 };
1493 log::debug!("unhandled SGR mode: 48 {n}");
1494 }
1495 return;
1496 }
1497 },
1498 &[49] => {
1499 self.attrs.bgcolor = crate::attrs::Color::Default;
1500 }
1501 &[n] if (90..=97).contains(&n) => {
1502 self.attrs.fgcolor =
1503 crate::attrs::Color::Idx(to_u8!(n) - 82);
1504 }
1505 &[n] if (100..=107).contains(&n) => {
1506 self.attrs.bgcolor =
1507 crate::attrs::Color::Idx(to_u8!(n) - 92);
1508 }
1509 ns => {
1510 if log::log_enabled!(log::Level::Debug) {
1511 let n = if ns.len() == 1 {
1512 format!(
1513 "{}",
1514 ns[0]
1517 )
1518 } else {
1519 format!("{ns:?}")
1520 };
1521 log::debug!("unhandled SGR mode: {n}");
1522 }
1523 }
1524 }
1525 }
1526 }
1527
1528 fn decstbm(&mut self, (top, bottom): (u16, u16)) {
1530 self.grid_mut().set_scroll_region(top - 1, bottom - 1);
1531 }
1532
1533 fn osc0(&mut self, s: &[u8]) {
1536 self.osc1(s);
1537 self.osc2(s);
1538 }
1539
1540 fn osc1(&mut self, s: &[u8]) {
1541 if let Ok(s) = std::str::from_utf8(s) {
1542 self.icon_name = s.to_string();
1543 }
1544 }
1545
1546 fn osc2(&mut self, s: &[u8]) {
1547 if let Ok(s) = std::str::from_utf8(s) {
1548 self.title = s.to_string();
1549 }
1550 }
1551}
1552
1553impl vte::Perform for Screen {
1554 fn print(&mut self, c: char) {
1555 if c == '\u{fffd}' || ('\u{80}'..'\u{a0}').contains(&c) {
1556 self.errors = self.errors.saturating_add(1);
1557 }
1558 self.text(c);
1559 }
1560
1561 fn execute(&mut self, b: u8) {
1562 match b {
1563 7 => self.bel(),
1564 8 => self.bs(),
1565 9 => self.tab(),
1566 10 => self.lf(),
1567 11 => self.vt(),
1568 12 => self.ff(),
1569 13 => self.cr(),
1570 14 | 15 => {}
1573 _ => {
1574 self.errors = self.errors.saturating_add(1);
1575 log::debug!("unhandled control character: {b}");
1576 }
1577 }
1578 }
1579
1580 fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, b: u8) {
1581 intermediates.first().map_or_else(
1582 || match b {
1583 b'7' => self.decsc(),
1584 b'8' => self.decrc(),
1585 b'=' => self.deckpam(),
1586 b'>' => self.deckpnm(),
1587 b'M' => self.ri(),
1588 b'c' => self.ris(),
1589 b'g' => self.vb(),
1590 _ => {
1591 log::debug!("unhandled escape code: ESC {b}");
1592 }
1593 },
1594 |i| {
1595 log::debug!("unhandled escape code: ESC {i} {b}");
1596 },
1597 );
1598 }
1599
1600 fn csi_dispatch(
1601 &mut self,
1602 params: &vte::Params,
1603 intermediates: &[u8],
1604 _ignore: bool,
1605 c: char,
1606 ) {
1607 match intermediates.first() {
1608 None => match c {
1609 '@' => self.ich(canonicalize_params_1(params, 1)),
1610 'A' => self.cuu(canonicalize_params_1(params, 1)),
1611 'B' => self.cud(canonicalize_params_1(params, 1)),
1612 'C' => self.cuf(canonicalize_params_1(params, 1)),
1613 'D' => self.cub(canonicalize_params_1(params, 1)),
1614 'G' => self.cha(canonicalize_params_1(params, 1)),
1615 'H' => self.cup(canonicalize_params_2(params, 1, 1)),
1616 'J' => self.ed(canonicalize_params_1(params, 0)),
1617 'K' => self.el(canonicalize_params_1(params, 0)),
1618 'L' => self.il(canonicalize_params_1(params, 1)),
1619 'M' => self.dl(canonicalize_params_1(params, 1)),
1620 'P' => self.dch(canonicalize_params_1(params, 1)),
1621 'S' => self.su(canonicalize_params_1(params, 1)),
1622 'T' => self.sd(canonicalize_params_1(params, 1)),
1623 'X' => self.ech(canonicalize_params_1(params, 1)),
1624 'd' => self.vpa(canonicalize_params_1(params, 1)),
1625 'h' => self.sm(params),
1626 'l' => self.rm(params),
1627 'm' => self.sgr(params),
1628 'r' => self.decstbm(canonicalize_params_decstbm(
1629 params,
1630 self.grid().size(),
1631 )),
1632 _ => {
1633 if log::log_enabled!(log::Level::Debug) {
1634 log::debug!(
1635 "unhandled csi sequence: CSI {} {}",
1636 param_str(params),
1637 c
1638 );
1639 }
1640 }
1641 },
1642 Some(b'?') => match c {
1643 'J' => self.decsed(canonicalize_params_1(params, 0)),
1644 'K' => self.decsel(canonicalize_params_1(params, 0)),
1645 'h' => self.decset(params),
1646 'l' => self.decrst(params),
1647 _ => {
1648 if log::log_enabled!(log::Level::Debug) {
1649 log::debug!(
1650 "unhandled csi sequence: CSI ? {} {}",
1651 param_str(params),
1652 c
1653 );
1654 }
1655 }
1656 },
1657 Some(i) => {
1658 if log::log_enabled!(log::Level::Debug) {
1659 log::debug!(
1660 "unhandled csi sequence: CSI {} {} {}",
1661 i,
1662 param_str(params),
1663 c
1664 );
1665 }
1666 }
1667 }
1668 }
1669
1670 fn osc_dispatch(&mut self, params: &[&[u8]], _bel_terminated: bool) {
1671 match (params.get(0), params.get(1)) {
1672 (Some(&b"0"), Some(s)) => self.osc0(s),
1673 (Some(&b"1"), Some(s)) => self.osc1(s),
1674 (Some(&b"2"), Some(s)) => self.osc2(s),
1675 _ => {
1676 if log::log_enabled!(log::Level::Debug) {
1677 log::debug!(
1678 "unhandled osc sequence: OSC {}",
1679 osc_param_str(params),
1680 );
1681 }
1682 }
1683 }
1684 }
1685
1686 fn hook(
1687 &mut self,
1688 params: &vte::Params,
1689 intermediates: &[u8],
1690 _ignore: bool,
1691 action: char,
1692 ) {
1693 if log::log_enabled!(log::Level::Debug) {
1694 intermediates.first().map_or_else(
1695 || {
1696 log::debug!(
1697 "unhandled dcs sequence: DCS {} {}",
1698 param_str(params),
1699 action,
1700 );
1701 },
1702 |i| {
1703 log::debug!(
1704 "unhandled dcs sequence: DCS {} {} {}",
1705 i,
1706 param_str(params),
1707 action,
1708 );
1709 },
1710 );
1711 }
1712 }
1713}
1714
1715fn canonicalize_params_1(params: &vte::Params, default: u16) -> u16 {
1716 let first = params.iter().next().map_or(0, |x| *x.first().unwrap_or(&0));
1717 if first == 0 {
1718 default
1719 } else {
1720 first
1721 }
1722}
1723
1724fn canonicalize_params_2(
1725 params: &vte::Params,
1726 default1: u16,
1727 default2: u16,
1728) -> (u16, u16) {
1729 let mut iter = params.iter();
1730 let first = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
1731 let first = if first == 0 { default1 } else { first };
1732
1733 let second = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
1734 let second = if second == 0 { default2 } else { second };
1735
1736 (first, second)
1737}
1738
1739fn canonicalize_params_decstbm(
1740 params: &vte::Params,
1741 size: crate::grid::Size,
1742) -> (u16, u16) {
1743 let mut iter = params.iter();
1744 let top = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
1745 let top = if top == 0 { 1 } else { top };
1746
1747 let bottom = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
1748 let bottom = if bottom == 0 { size.rows } else { bottom };
1749
1750 (top, bottom)
1751}
1752
1753fn u16_to_u8(i: u16) -> Option<u8> {
1754 if i > u16::from(u8::max_value()) {
1755 None
1756 } else {
1757 Some(i.try_into().unwrap())
1759 }
1760}
1761
1762fn param_str(params: &vte::Params) -> String {
1763 let strs: Vec<_> = params
1764 .iter()
1765 .map(|subparams| {
1766 let subparam_strs: Vec<_> = subparams
1767 .iter()
1768 .map(std::string::ToString::to_string)
1769 .collect();
1770 subparam_strs.join(" : ")
1771 })
1772 .collect();
1773 strs.join(" ; ")
1774}
1775
1776fn osc_param_str(params: &[&[u8]]) -> String {
1777 let strs: Vec<_> = params
1778 .iter()
1779 .map(|b| format!("\"{}\"", std::string::String::from_utf8_lossy(b)))
1780 .collect();
1781 strs.join(" ; ")
1782}