1use crate::term::BufWrite as _;
2use unicode_width::UnicodeWidthChar as _;
3
4fn parse_osc7_uri(raw: &str) -> String {
8 let stripped = if let Some(rest) = raw.strip_prefix("file://") {
9 if let Some(slash) = rest.find('/') {
11 &rest[slash..]
12 } else {
13 rest
14 }
15 } else {
16 raw
17 };
18 percent_decode(stripped)
19}
20
21fn percent_decode(input: &str) -> String {
23 let mut out = String::with_capacity(input.len());
24 let bytes = input.as_bytes();
25 let mut i = 0;
26 while i < bytes.len() {
27 if bytes[i] == b'%' && i + 2 < bytes.len() {
28 if let (Some(hi), Some(lo)) = (
29 hex_val(bytes[i + 1]),
30 hex_val(bytes[i + 2]),
31 ) {
32 out.push(char::from(hi << 4 | lo));
33 i += 3;
34 continue;
35 }
36 }
37 out.push(char::from(bytes[i]));
38 i += 1;
39 }
40 out
41}
42
43fn hex_val(b: u8) -> Option<u8> {
44 match b {
45 b'0'..=b'9' => Some(b - b'0'),
46 b'a'..=b'f' => Some(b - b'a' + 10),
47 b'A'..=b'F' => Some(b - b'A' + 10),
48 _ => None,
49 }
50}
51
52const MODE_APPLICATION_KEYPAD: u8 = 0b0000_0001;
53const MODE_APPLICATION_CURSOR: u8 = 0b0000_0010;
54const MODE_HIDE_CURSOR: u8 = 0b0000_0100;
55const MODE_ALTERNATE_SCREEN: u8 = 0b0000_1000;
56const MODE_BRACKETED_PASTE: u8 = 0b0001_0000;
57
58#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
60pub enum MouseProtocolMode {
61 #[default]
63 None,
64
65 Press,
68
69 PressRelease,
72
73 ButtonMotion,
78
79 AnyMotion,
83 }
85
86#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
88pub enum MouseProtocolEncoding {
89 #[default]
91 Default,
92
93 Utf8,
95
96 Sgr,
98 }
100
101#[derive(Clone, Debug)]
103pub struct Screen {
104 grid: crate::grid::Grid,
105 alternate_grid: crate::grid::Grid,
106
107 attrs: crate::attrs::Attrs,
108 saved_attrs: crate::attrs::Attrs,
109
110 modes: u8,
111 mouse_protocol_mode: MouseProtocolMode,
112 mouse_protocol_encoding: MouseProtocolEncoding,
113
114 osc_title: String,
116
117 osc7_path: Option<String>,
120
121 pub(crate) squelch_cleared: bool,
125
126 pub(crate) squelch_clear_pending: bool,
129
130 pub(crate) audible_bell_count: u32,
134}
135
136impl Screen {
137 pub(crate) fn new(
138 size: crate::grid::Size,
139 scrollback_len: usize,
140 ) -> Self {
141 let mut grid = crate::grid::Grid::new(size, scrollback_len);
142 grid.allocate_rows();
143 Self {
144 grid,
145 alternate_grid: crate::grid::Grid::new(size, 0),
146
147 attrs: crate::attrs::Attrs::default(),
148 saved_attrs: crate::attrs::Attrs::default(),
149
150 modes: 0,
151 mouse_protocol_mode: MouseProtocolMode::default(),
152 mouse_protocol_encoding: MouseProtocolEncoding::default(),
153 osc_title: String::new(),
154 osc7_path: None,
155 squelch_cleared: false,
156 squelch_clear_pending: false,
157 audible_bell_count: 0,
158 }
159 }
160
161 pub fn set_size(&mut self, rows: u16, cols: u16) {
163 self.grid.set_size(crate::grid::Size { rows, cols });
164 self.alternate_grid
165 .set_size(crate::grid::Size { rows, cols });
166 }
167
168 #[must_use]
172 pub fn size(&self) -> (u16, u16) {
173 let size = self.grid().size();
174 (size.rows, size.cols)
175 }
176
177 pub fn set_scrollback(&mut self, rows: usize) {
188 self.grid_mut().set_scrollback(rows);
189 }
190
191 #[must_use]
196 pub fn scrollback(&self) -> usize {
197 self.grid().scrollback()
198 }
199
200 #[must_use]
205 pub fn contents(&self) -> String {
206 let mut contents = String::new();
207 self.write_contents(&mut contents);
208 contents
209 }
210
211 fn write_contents(&self, contents: &mut String) {
212 self.grid().write_contents(contents);
213 }
214
215 pub fn rows(
223 &self,
224 start: u16,
225 width: u16,
226 ) -> impl Iterator<Item = String> + '_ {
227 self.grid().visible_rows().map(move |row| {
228 let mut contents = String::new();
229 row.write_contents(&mut contents, start, width, false);
230 contents
231 })
232 }
233
234 #[must_use]
241 pub fn contents_between(
242 &self,
243 start_row: u16,
244 start_col: u16,
245 end_row: u16,
246 end_col: u16,
247 ) -> String {
248 match start_row.cmp(&end_row) {
249 std::cmp::Ordering::Less => {
250 let (_, cols) = self.size();
251 let mut contents = String::new();
252 for (i, row) in self
253 .grid()
254 .visible_rows()
255 .enumerate()
256 .skip(usize::from(start_row))
257 .take(usize::from(end_row) - usize::from(start_row) + 1)
258 {
259 if i == usize::from(start_row) {
260 row.write_contents(
261 &mut contents,
262 start_col,
263 cols - start_col,
264 false,
265 );
266 if !row.wrapped() {
267 contents.push('\n');
268 }
269 } else if i == usize::from(end_row) {
270 row.write_contents(&mut contents, 0, end_col, false);
271 } else {
272 row.write_contents(&mut contents, 0, cols, false);
273 if !row.wrapped() {
274 contents.push('\n');
275 }
276 }
277 }
278 contents
279 }
280 std::cmp::Ordering::Equal => {
281 if start_col < end_col {
282 self.rows(start_col, end_col - start_col)
283 .nth(usize::from(start_row))
284 .unwrap_or_default()
285 } else {
286 String::new()
287 }
288 }
289 std::cmp::Ordering::Greater => String::new(),
290 }
291 }
292
293 #[must_use]
298 pub fn state_formatted(&self) -> Vec<u8> {
299 let mut contents = vec![];
300 self.write_contents_formatted(&mut contents);
301 self.write_input_mode_formatted(&mut contents);
302 contents
303 }
304
305 #[must_use]
310 pub fn state_diff(&self, prev: &Self) -> Vec<u8> {
311 let mut contents = vec![];
312 self.write_contents_diff(&mut contents, prev);
313 self.write_input_mode_diff(&mut contents, prev);
314 contents
315 }
316
317 #[must_use]
323 pub fn contents_formatted(&self) -> Vec<u8> {
324 let mut contents = vec![];
325 self.write_contents_formatted(&mut contents);
326 contents
327 }
328
329 fn write_contents_formatted(&self, contents: &mut Vec<u8>) {
330 crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
331 let prev_attrs = self.grid().write_contents_formatted(contents);
332 self.attrs.write_escape_code_diff(contents, &prev_attrs);
333 }
334
335 #[allow(clippy::missing_panics_doc)]
347 pub fn rows_formatted(
348 &self,
349 start: u16,
350 width: u16,
351 ) -> impl Iterator<Item = Vec<u8>> + '_ {
352 let mut wrapping = false;
353 self.grid().visible_rows().enumerate().map(move |(i, row)| {
354 let i = i.try_into().unwrap();
357 let mut contents = vec![];
358 row.write_contents_formatted(
359 &mut contents,
360 start,
361 width,
362 i,
363 wrapping,
364 None,
365 None,
366 );
367 if start == 0 && width == self.grid.size().cols {
368 wrapping = row.wrapped();
369 }
370 contents
371 })
372 }
373
374 #[must_use]
385 pub fn contents_diff(&self, prev: &Self) -> Vec<u8> {
386 let mut contents = vec![];
387 self.write_contents_diff(&mut contents, prev);
388 contents
389 }
390
391 fn write_contents_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
392 if self.hide_cursor() != prev.hide_cursor() {
393 crate::term::HideCursor::new(self.hide_cursor())
394 .write_buf(contents);
395 }
396 let prev_attrs = self.grid().write_contents_diff(
397 contents,
398 prev.grid(),
399 prev.attrs,
400 );
401 self.attrs.write_escape_code_diff(contents, &prev_attrs);
402 }
403
404 #[allow(clippy::missing_panics_doc)]
414 pub fn rows_diff<'a>(
415 &'a self,
416 prev: &'a Self,
417 start: u16,
418 width: u16,
419 ) -> impl Iterator<Item = Vec<u8>> + 'a {
420 self.grid()
421 .visible_rows()
422 .zip(prev.grid().visible_rows())
423 .enumerate()
424 .map(move |(i, (row, prev_row))| {
425 let i = i.try_into().unwrap();
428 let mut contents = vec![];
429 row.write_contents_diff(
430 &mut contents,
431 prev_row,
432 start,
433 width,
434 i,
435 false,
436 false,
437 crate::grid::Pos { row: i, col: start },
438 crate::attrs::Attrs::default(),
439 );
440 contents
441 })
442 }
443
444 #[must_use]
453 pub fn input_mode_formatted(&self) -> Vec<u8> {
454 let mut contents = vec![];
455 self.write_input_mode_formatted(&mut contents);
456 contents
457 }
458
459 fn write_input_mode_formatted(&self, contents: &mut Vec<u8>) {
460 crate::term::ApplicationKeypad::new(
461 self.mode(MODE_APPLICATION_KEYPAD),
462 )
463 .write_buf(contents);
464 crate::term::ApplicationCursor::new(
465 self.mode(MODE_APPLICATION_CURSOR),
466 )
467 .write_buf(contents);
468 crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
469 .write_buf(contents);
470 crate::term::MouseProtocolMode::new(
471 self.mouse_protocol_mode,
472 MouseProtocolMode::None,
473 )
474 .write_buf(contents);
475 crate::term::MouseProtocolEncoding::new(
476 self.mouse_protocol_encoding,
477 MouseProtocolEncoding::Default,
478 )
479 .write_buf(contents);
480 }
481
482 #[must_use]
486 pub fn input_mode_diff(&self, prev: &Self) -> Vec<u8> {
487 let mut contents = vec![];
488 self.write_input_mode_diff(&mut contents, prev);
489 contents
490 }
491
492 fn write_input_mode_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
493 if self.mode(MODE_APPLICATION_KEYPAD)
494 != prev.mode(MODE_APPLICATION_KEYPAD)
495 {
496 crate::term::ApplicationKeypad::new(
497 self.mode(MODE_APPLICATION_KEYPAD),
498 )
499 .write_buf(contents);
500 }
501 if self.mode(MODE_APPLICATION_CURSOR)
502 != prev.mode(MODE_APPLICATION_CURSOR)
503 {
504 crate::term::ApplicationCursor::new(
505 self.mode(MODE_APPLICATION_CURSOR),
506 )
507 .write_buf(contents);
508 }
509 if self.mode(MODE_BRACKETED_PASTE) != prev.mode(MODE_BRACKETED_PASTE)
510 {
511 crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
512 .write_buf(contents);
513 }
514 crate::term::MouseProtocolMode::new(
515 self.mouse_protocol_mode,
516 prev.mouse_protocol_mode,
517 )
518 .write_buf(contents);
519 crate::term::MouseProtocolEncoding::new(
520 self.mouse_protocol_encoding,
521 prev.mouse_protocol_encoding,
522 )
523 .write_buf(contents);
524 }
525
526 #[must_use]
545 pub fn attributes_formatted(&self) -> Vec<u8> {
546 let mut contents = vec![];
547 self.write_attributes_formatted(&mut contents);
548 contents
549 }
550
551 fn write_attributes_formatted(&self, contents: &mut Vec<u8>) {
552 crate::term::ClearAttrs.write_buf(contents);
553 self.attrs.write_escape_code_diff(
554 contents,
555 &crate::attrs::Attrs::default(),
556 );
557 }
558
559 #[must_use]
563 pub fn cursor_position(&self) -> (u16, u16) {
564 let pos = self.grid().pos();
565 (pos.row, pos.col)
566 }
567
568 #[must_use]
586 pub fn cursor_state_formatted(&self) -> Vec<u8> {
587 let mut contents = vec![];
588 self.write_cursor_state_formatted(&mut contents);
589 contents
590 }
591
592 fn write_cursor_state_formatted(&self, contents: &mut Vec<u8>) {
593 crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
594 self.grid()
595 .write_cursor_position_formatted(contents, None, None);
596
597 }
604
605 #[must_use]
608 pub fn cell(&self, row: u16, col: u16) -> Option<&crate::Cell> {
609 self.grid().visible_cell(crate::grid::Pos { row, col })
610 }
611
612 #[must_use]
614 pub fn row_wrapped(&self, row: u16) -> bool {
615 self.grid()
616 .visible_row(row)
617 .is_some_and(crate::row::Row::wrapped)
618 }
619
620 #[must_use]
622 pub fn alternate_screen(&self) -> bool {
623 self.mode(MODE_ALTERNATE_SCREEN)
624 }
625
626 #[must_use]
628 pub fn application_keypad(&self) -> bool {
629 self.mode(MODE_APPLICATION_KEYPAD)
630 }
631
632 #[must_use]
634 pub fn application_cursor(&self) -> bool {
635 self.mode(MODE_APPLICATION_CURSOR)
636 }
637
638 #[must_use]
640 pub fn hide_cursor(&self) -> bool {
641 self.mode(MODE_HIDE_CURSOR)
642 }
643
644 #[must_use]
646 pub fn bracketed_paste(&self) -> bool {
647 self.mode(MODE_BRACKETED_PASTE)
648 }
649
650 #[must_use]
652 pub fn mouse_protocol_mode(&self) -> MouseProtocolMode {
653 self.mouse_protocol_mode
654 }
655
656 #[must_use]
658 pub fn mouse_protocol_encoding(&self) -> MouseProtocolEncoding {
659 self.mouse_protocol_encoding
660 }
661
662 #[must_use]
664 pub fn title(&self) -> &str {
665 &self.osc_title
666 }
667
668 pub fn set_title(&mut self, raw: &[u8]) {
670 if let Ok(s) = std::str::from_utf8(raw) {
671 self.osc_title = s.to_string();
672 }
673 }
674
675 #[must_use]
677 pub fn path(&self) -> Option<&str> {
678 self.osc7_path.as_deref()
679 }
680
681 pub fn set_path(&mut self, raw: &[u8]) {
684 if let Ok(s) = std::str::from_utf8(raw) {
685 let path = parse_osc7_uri(s);
686 if !path.is_empty() {
687 self.osc7_path = Some(path);
688 }
689 }
690 }
691
692 #[must_use]
697 pub fn squelch_cleared(&self) -> bool {
698 self.squelch_cleared
699 }
700
701 pub fn take_squelch_cleared(&mut self) -> bool {
703 let v = self.squelch_cleared;
704 self.squelch_cleared = false;
705 v
706 }
707
708 pub fn take_audible_bell(&mut self) -> bool {
711 let v = self.audible_bell_count;
712 self.audible_bell_count = 0;
713 v > 0
714 }
715
716 pub fn set_squelch_clear_pending(&mut self, v: bool) {
719 self.squelch_clear_pending = v;
720 }
721
722 fn check_squelch_signal(&mut self) {
724 if self.squelch_clear_pending {
725 self.squelch_cleared = true;
726 self.squelch_clear_pending = false;
727 }
728 }
729
730 #[must_use]
732 pub fn fgcolor(&self) -> crate::Color {
733 self.attrs.fgcolor
734 }
735
736 #[must_use]
738 pub fn bgcolor(&self) -> crate::Color {
739 self.attrs.bgcolor
740 }
741
742 #[must_use]
745 pub fn bold(&self) -> bool {
746 self.attrs.bold()
747 }
748
749 #[must_use]
752 pub fn dim(&self) -> bool {
753 self.attrs.dim()
754 }
755
756 #[must_use]
759 pub fn italic(&self) -> bool {
760 self.attrs.italic()
761 }
762
763 #[must_use]
766 pub fn underline(&self) -> bool {
767 self.attrs.underline()
768 }
769
770 #[must_use]
773 pub fn inverse(&self) -> bool {
774 self.attrs.inverse()
775 }
776
777 pub(crate) fn grid(&self) -> &crate::grid::Grid {
778 if self.mode(MODE_ALTERNATE_SCREEN) {
779 &self.alternate_grid
780 } else {
781 &self.grid
782 }
783 }
784
785 fn grid_mut(&mut self) -> &mut crate::grid::Grid {
786 if self.mode(MODE_ALTERNATE_SCREEN) {
787 &mut self.alternate_grid
788 } else {
789 &mut self.grid
790 }
791 }
792
793 fn enter_alternate_grid(&mut self) {
794 self.grid_mut().set_scrollback(0);
795 self.set_mode(MODE_ALTERNATE_SCREEN);
796 self.alternate_grid.allocate_rows();
797 }
798
799 fn exit_alternate_grid(&mut self) {
800 self.clear_mode(MODE_ALTERNATE_SCREEN);
801 }
802
803 fn save_cursor(&mut self) {
804 self.grid_mut().save_cursor();
805 self.saved_attrs = self.attrs;
806 }
807
808 fn restore_cursor(&mut self) {
809 self.grid_mut().restore_cursor();
810 self.attrs = self.saved_attrs;
811 }
812
813 fn set_mode(&mut self, mode: u8) {
814 self.modes |= mode;
815 }
816
817 fn clear_mode(&mut self, mode: u8) {
818 self.modes &= !mode;
819 }
820
821 fn mode(&self, mode: u8) -> bool {
822 self.modes & mode != 0
823 }
824
825 fn set_mouse_mode(&mut self, mode: MouseProtocolMode) {
826 self.mouse_protocol_mode = mode;
827 }
828
829 fn clear_mouse_mode(&mut self, mode: MouseProtocolMode) {
830 if self.mouse_protocol_mode == mode {
831 self.mouse_protocol_mode = MouseProtocolMode::default();
832 }
833 }
834
835 fn set_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
836 self.mouse_protocol_encoding = encoding;
837 }
838
839 fn clear_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
840 if self.mouse_protocol_encoding == encoding {
841 self.mouse_protocol_encoding = MouseProtocolEncoding::default();
842 }
843 }
844}
845
846impl Screen {
847 pub(crate) fn text(&mut self, c: char) {
848 let pos = self.grid().pos();
849 let size = self.grid().size();
850 let attrs = self.attrs;
851
852 let width = c.width();
853 if width.is_none() && (u32::from(c)) < 256 {
854 return;
856 }
857 let width = width
858 .unwrap_or(1)
859 .try_into()
860 .unwrap();
862
863 let mut wrap = false;
872 if pos.col > size.cols - width {
873 let last_cell = self
874 .grid()
875 .drawing_cell(crate::grid::Pos {
876 row: pos.row,
877 col: size.cols - 1,
878 })
879 .unwrap();
883 if last_cell.has_contents() || last_cell.is_wide_continuation() {
884 wrap = true;
885 }
886 }
887 self.grid_mut().col_wrap(width, wrap);
888 let pos = self.grid().pos();
889
890 if width == 0 {
891 if pos.col > 0 {
892 let mut prev_cell = self
893 .grid_mut()
894 .drawing_cell_mut(crate::grid::Pos {
895 row: pos.row,
896 col: pos.col - 1,
897 })
898 .unwrap();
903 if prev_cell.is_wide_continuation() {
904 prev_cell = self
905 .grid_mut()
906 .drawing_cell_mut(crate::grid::Pos {
907 row: pos.row,
908 col: pos.col - 2,
909 })
910 .unwrap();
917 }
918 prev_cell.append(c);
919 } else if pos.row > 0 {
920 let prev_row = self
921 .grid()
922 .drawing_row(pos.row - 1)
923 .unwrap();
928 if prev_row.wrapped() {
929 let mut prev_cell = self
930 .grid_mut()
931 .drawing_cell_mut(crate::grid::Pos {
932 row: pos.row - 1,
933 col: size.cols - 1,
934 })
935 .unwrap();
941 if prev_cell.is_wide_continuation() {
942 prev_cell = self
943 .grid_mut()
944 .drawing_cell_mut(crate::grid::Pos {
945 row: pos.row - 1,
946 col: size.cols - 2,
947 })
948 .unwrap();
957 }
958 prev_cell.append(c);
959 }
960 }
961 } else {
962 if let Some(cell_ref) = self.grid().drawing_cell(pos) {
966 if cell_ref.is_wide_continuation() {
967 if let Some(prev_cell) = self
968 .grid_mut()
969 .drawing_cell_mut(crate::grid::Pos {
970 row: pos.row,
971 col: pos.col - 1,
972 })
973 {
974 prev_cell.clear(attrs);
975 }
976 }
977 }
978
979 let is_wide_at_pos = self
980 .grid()
981 .drawing_cell(pos)
982 .map_or(false, |c| c.is_wide());
983 if is_wide_at_pos {
984 if let Some(next_cell) = self
985 .grid_mut()
986 .drawing_cell_mut(crate::grid::Pos {
987 row: pos.row,
988 col: pos.col + 1,
989 })
990 {
991 next_cell.set(' ', attrs);
992 }
993 }
994
995 if let Some(cell) = self
996 .grid_mut()
997 .drawing_cell_mut(pos)
998 {
999 cell.set(c, attrs);
1000 } else {
1001 return;
1002 }
1003 self.grid_mut().col_inc(1);
1004 if width > 1 {
1005 let pos = self.grid().pos();
1006 let is_wide_here = self
1007 .grid()
1008 .drawing_cell(pos)
1009 .map_or(false, |c| c.is_wide());
1010 if is_wide_here {
1011 let next_next_pos = crate::grid::Pos {
1012 row: pos.row,
1013 col: pos.col + 1,
1014 };
1015 if let Some(next_next_cell) = self
1016 .grid_mut()
1017 .drawing_cell_mut(next_next_pos)
1018 {
1019 next_next_cell.clear(attrs);
1020 if next_next_pos.col == size.cols - 1 {
1021 if let Some(row) = self.grid_mut()
1022 .drawing_row_mut(pos.row)
1023 {
1024 row.wrap(false);
1025 }
1026 }
1027 }
1028 }
1029 if let Some(next_cell) = self
1030 .grid_mut()
1031 .drawing_cell_mut(pos)
1032 {
1033 next_cell.clear(crate::attrs::Attrs::default());
1034 next_cell.set_wide_continuation(true);
1035 }
1036 self.grid_mut().col_inc(1);
1037 }
1038 }
1039 }
1040
1041 pub(crate) fn bs(&mut self) {
1044 self.grid_mut().col_dec(1);
1045 }
1046
1047 pub(crate) fn tab(&mut self) {
1048 self.grid_mut().col_tab();
1049 }
1050
1051 pub(crate) fn lf(&mut self) {
1052 self.grid_mut().row_inc_scroll(1);
1053 }
1054
1055 pub(crate) fn vt(&mut self) {
1056 self.lf();
1057 }
1058
1059 pub(crate) fn ff(&mut self) {
1060 self.lf();
1061 }
1062
1063 pub(crate) fn cr(&mut self) {
1064 self.grid_mut().col_set(0);
1065 }
1066
1067 pub(crate) fn decsc(&mut self) {
1071 self.save_cursor();
1072 }
1073
1074 pub(crate) fn decrc(&mut self) {
1076 self.restore_cursor();
1077 }
1078
1079 pub(crate) fn deckpam(&mut self) {
1081 self.set_mode(MODE_APPLICATION_KEYPAD);
1082 }
1083
1084 pub(crate) fn deckpnm(&mut self) {
1086 self.clear_mode(MODE_APPLICATION_KEYPAD);
1087 }
1088
1089 pub(crate) fn ri(&mut self) {
1091 self.grid_mut().row_dec_scroll(1);
1092 }
1093
1094 pub(crate) fn ris(&mut self) {
1096 *self = Self::new(self.grid.size(), self.grid.scrollback_len());
1097 }
1098
1099 pub(crate) fn ich(&mut self, count: u16) {
1103 self.grid_mut().insert_cells(count);
1104 }
1105
1106 pub(crate) fn cuu(&mut self, offset: u16) {
1108 self.grid_mut().row_dec_clamp(offset);
1109 }
1110
1111 pub(crate) fn cud(&mut self, offset: u16) {
1113 self.grid_mut().row_inc_clamp(offset);
1114 }
1115
1116 pub(crate) fn cuf(&mut self, offset: u16) {
1118 self.grid_mut().col_inc_clamp(offset);
1119 }
1120
1121 pub(crate) fn cub(&mut self, offset: u16) {
1123 self.grid_mut().col_dec(offset);
1124 }
1125
1126 pub(crate) fn cnl(&mut self, offset: u16) {
1128 self.grid_mut().col_set(0);
1129 self.grid_mut().row_inc_clamp(offset);
1130 }
1131
1132 pub(crate) fn cpl(&mut self, offset: u16) {
1134 self.grid_mut().col_set(0);
1135 self.grid_mut().row_dec_clamp(offset);
1136 }
1137
1138 pub(crate) fn cha(&mut self, col: u16) {
1140 self.grid_mut().col_set(col - 1);
1141 }
1142
1143 pub(crate) fn cup(&mut self, (row, col): (u16, u16)) {
1145 self.grid_mut().set_pos(crate::grid::Pos {
1146 row: row - 1,
1147 col: col - 1,
1148 });
1149 }
1150
1151 pub(crate) fn ed(
1153 &mut self,
1154 mode: u16,
1155 mut unhandled: impl FnMut(&mut Self),
1156 ) {
1157 let attrs = self.attrs;
1158 match mode {
1159 0 => self.grid_mut().erase_all_forward(attrs),
1160 1 => self.grid_mut().erase_all_backward(attrs),
1161 2 => {
1162 self.grid_mut().erase_all(attrs);
1163 self.check_squelch_signal();
1164 }
1165 3 => {
1166 self.grid_mut().clear_scrollback();
1167 self.check_squelch_signal();
1168 }
1169 _ => unhandled(self),
1170 }
1171 }
1172
1173 pub(crate) fn decsed(
1175 &mut self,
1176 mode: u16,
1177 unhandled: impl FnMut(&mut Self),
1178 ) {
1179 self.ed(mode, unhandled);
1180 }
1181
1182 pub(crate) fn el(
1184 &mut self,
1185 mode: u16,
1186 mut unhandled: impl FnMut(&mut Self),
1187 ) {
1188 let attrs = self.attrs;
1189 match mode {
1190 0 => self.grid_mut().erase_row_forward(attrs),
1191 1 => self.grid_mut().erase_row_backward(attrs),
1192 2 => self.grid_mut().erase_row(attrs),
1193 _ => unhandled(self),
1194 }
1195 }
1196
1197 pub(crate) fn decsel(
1199 &mut self,
1200 mode: u16,
1201 unhandled: impl FnMut(&mut Self),
1202 ) {
1203 self.el(mode, unhandled);
1204 }
1205
1206 pub(crate) fn il(&mut self, count: u16) {
1208 self.grid_mut().insert_lines(count);
1209 }
1210
1211 pub(crate) fn dl(&mut self, count: u16) {
1213 self.grid_mut().delete_lines(count);
1214 }
1215
1216 pub(crate) fn dch(&mut self, count: u16) {
1218 self.grid_mut().delete_cells(count);
1219 }
1220
1221 pub(crate) fn su(&mut self, count: u16) {
1223 self.grid_mut().scroll_up(count);
1224 }
1225
1226 pub(crate) fn sd(&mut self, count: u16) {
1228 self.grid_mut().scroll_down(count);
1229 }
1230
1231 pub(crate) fn ech(&mut self, count: u16) {
1233 let attrs = self.attrs;
1234 self.grid_mut().erase_cells(count, attrs);
1235 }
1236
1237 pub(crate) fn vpa(&mut self, row: u16) {
1239 self.grid_mut().row_set(row - 1);
1240 }
1241
1242 pub(crate) fn decset(
1244 &mut self,
1245 params: &vte::Params,
1246 mut unhandled: impl FnMut(&mut Self),
1247 ) {
1248 for param in params {
1249 match param {
1250 [1] => self.set_mode(MODE_APPLICATION_CURSOR),
1251 [6] => self.grid_mut().set_origin_mode(true),
1252 [9] => self.set_mouse_mode(MouseProtocolMode::Press),
1253 [25] => self.clear_mode(MODE_HIDE_CURSOR),
1254 [47] => self.enter_alternate_grid(),
1255 [1000] => {
1256 self.set_mouse_mode(MouseProtocolMode::PressRelease);
1257 }
1258 [1002] => {
1259 self.set_mouse_mode(MouseProtocolMode::ButtonMotion);
1260 }
1261 [1003] => self.set_mouse_mode(MouseProtocolMode::AnyMotion),
1262 [1005] => {
1263 self.set_mouse_encoding(MouseProtocolEncoding::Utf8);
1264 }
1265 [1006] => {
1266 self.set_mouse_encoding(MouseProtocolEncoding::Sgr);
1267 }
1268 [1049] => {
1269 self.decsc();
1270 self.alternate_grid.clear();
1271 self.enter_alternate_grid();
1272 }
1273 [2004] => self.set_mode(MODE_BRACKETED_PASTE),
1274 _ => unhandled(self),
1275 }
1276 }
1277 }
1278
1279 pub(crate) fn decrst(
1281 &mut self,
1282 params: &vte::Params,
1283 mut unhandled: impl FnMut(&mut Self),
1284 ) {
1285 for param in params {
1286 match param {
1287 [1] => self.clear_mode(MODE_APPLICATION_CURSOR),
1288 [6] => self.grid_mut().set_origin_mode(false),
1289 [9] => self.clear_mouse_mode(MouseProtocolMode::Press),
1290 [25] => self.set_mode(MODE_HIDE_CURSOR),
1291 [47] => {
1292 self.exit_alternate_grid();
1293 }
1294 [1000] => {
1295 self.clear_mouse_mode(MouseProtocolMode::PressRelease);
1296 }
1297 [1002] => {
1298 self.clear_mouse_mode(MouseProtocolMode::ButtonMotion);
1299 }
1300 [1003] => {
1301 self.clear_mouse_mode(MouseProtocolMode::AnyMotion);
1302 }
1303 [1005] => {
1304 self.clear_mouse_encoding(MouseProtocolEncoding::Utf8);
1305 }
1306 [1006] => {
1307 self.clear_mouse_encoding(MouseProtocolEncoding::Sgr);
1308 }
1309 [1049] => {
1310 self.exit_alternate_grid();
1311 self.decrc();
1312 }
1313 [2004] => self.clear_mode(MODE_BRACKETED_PASTE),
1314 _ => unhandled(self),
1315 }
1316 }
1317 }
1318
1319 pub(crate) fn sgr(
1321 &mut self,
1322 params: &vte::Params,
1323 mut unhandled: impl FnMut(&mut Self),
1324 ) {
1325 if params.is_empty() {
1329 self.attrs = crate::attrs::Attrs::default();
1330 return;
1331 }
1332
1333 let mut iter = params.iter();
1334
1335 macro_rules! next_param {
1336 () => {
1337 match iter.next() {
1338 Some(n) => n,
1339 _ => return,
1340 }
1341 };
1342 }
1343
1344 macro_rules! to_u8 {
1345 ($n:expr) => {
1346 if let Some(n) = u16_to_u8($n) {
1347 n
1348 } else {
1349 return;
1350 }
1351 };
1352 }
1353
1354 macro_rules! next_param_u8 {
1355 () => {
1356 if let &[n] = next_param!() {
1357 to_u8!(n)
1358 } else {
1359 return;
1360 }
1361 };
1362 }
1363
1364 loop {
1365 match next_param!() {
1366 [0] => self.attrs = crate::attrs::Attrs::default(),
1367 [1] => self.attrs.set_bold(),
1368 [2] => self.attrs.set_dim(),
1369 [3] => self.attrs.set_italic(true),
1370 [4] => self.attrs.set_underline(true),
1371 [5] | [6] => self.attrs.set_blink(true),
1372 [7] => self.attrs.set_inverse(true),
1373 [8] => self.attrs.set_hidden(true),
1374 [9] => self.attrs.set_strikethrough(true),
1375 [22] => self.attrs.set_normal_intensity(),
1376 [23] => self.attrs.set_italic(false),
1377 [24] => self.attrs.set_underline(false),
1378 [25] => self.attrs.set_blink(false),
1379 [27] => self.attrs.set_inverse(false),
1380 [28] => self.attrs.set_hidden(false),
1381 [29] => self.attrs.set_strikethrough(false),
1382 [n] if (30..=37).contains(n) => {
1383 self.attrs.fgcolor = crate::Color::Idx(to_u8!(*n) - 30);
1384 }
1385 [38, 2, r, g, b] => {
1386 self.attrs.fgcolor =
1387 crate::Color::Rgb(to_u8!(*r), to_u8!(*g), to_u8!(*b));
1388 }
1389 [38, 5, i] => {
1390 self.attrs.fgcolor = crate::Color::Idx(to_u8!(*i));
1391 }
1392 [38] => match next_param!() {
1393 [2] => {
1394 let r = next_param_u8!();
1395 let g = next_param_u8!();
1396 let b = next_param_u8!();
1397 self.attrs.fgcolor = crate::Color::Rgb(r, g, b);
1398 }
1399 [5] => {
1400 self.attrs.fgcolor =
1401 crate::Color::Idx(next_param_u8!());
1402 }
1403 _ => {
1404 unhandled(self);
1405 return;
1406 }
1407 },
1408 [39] => {
1409 self.attrs.fgcolor = crate::Color::Default;
1410 }
1411 [n] if (40..=47).contains(n) => {
1412 self.attrs.bgcolor = crate::Color::Idx(to_u8!(*n) - 40);
1413 }
1414 [48, 2, r, g, b] => {
1415 self.attrs.bgcolor =
1416 crate::Color::Rgb(to_u8!(*r), to_u8!(*g), to_u8!(*b));
1417 }
1418 [48, 5, i] => {
1419 self.attrs.bgcolor = crate::Color::Idx(to_u8!(*i));
1420 }
1421 [48] => match next_param!() {
1422 [2] => {
1423 let r = next_param_u8!();
1424 let g = next_param_u8!();
1425 let b = next_param_u8!();
1426 self.attrs.bgcolor = crate::Color::Rgb(r, g, b);
1427 }
1428 [5] => {
1429 self.attrs.bgcolor =
1430 crate::Color::Idx(next_param_u8!());
1431 }
1432 _ => {
1433 unhandled(self);
1434 return;
1435 }
1436 },
1437 [49] => {
1438 self.attrs.bgcolor = crate::Color::Default;
1439 }
1440 [n] if (90..=97).contains(n) => {
1441 self.attrs.fgcolor = crate::Color::Idx(to_u8!(*n) - 82);
1442 }
1443 [n] if (100..=107).contains(n) => {
1444 self.attrs.bgcolor = crate::Color::Idx(to_u8!(*n) - 92);
1445 }
1446 _ => unhandled(self),
1447 }
1448 }
1449 }
1450
1451 pub(crate) fn decstbm(&mut self, (top, bottom): (u16, u16)) {
1453 self.grid_mut().set_scroll_region(top - 1, bottom - 1);
1454 }
1455}
1456
1457fn u16_to_u8(i: u16) -> Option<u8> {
1458 if i > u16::from(u8::MAX) {
1459 None
1460 } else {
1461 Some(i.try_into().unwrap())
1463 }
1464}
1465
1466#[cfg(test)]
1467#[path = "../../../tests-rs/test_vt100_screen.rs"]
1468mod tests;
1469
1470#[cfg(test)]
1471#[path = "../../../tests-rs/test_issue155_sgr_attrs.rs"]
1472mod test_issue155_sgr_attrs;
1473