vt100/
screen.rs

1use std::fmt::Debug;
2
3use crate::{attrs::Attrs, term::BufWrite as _, TermReplySender};
4use compact_str::ToCompactString;
5use termwiz::escape::{
6  csi::{
7    CsiParam, Cursor, CursorStyle, DecPrivateMode, DecPrivateModeCode, Edit,
8    EraseInDisplay, EraseInLine, Sgr, TerminalMode, TerminalModeCode, Window,
9  },
10  Action, ControlCode, DeviceControlMode, Esc, EscCode, OneBased,
11  OperatingSystemCommand, CSI,
12};
13use unicode_width::UnicodeWidthChar as _;
14
15const MODE_APPLICATION_KEYPAD: u8 = 0b0000_0001;
16const MODE_APPLICATION_CURSOR: u8 = 0b0000_0010;
17const MODE_HIDE_CURSOR: u8 = 0b0000_0100;
18const MODE_ALTERNATE_SCREEN: u8 = 0b0000_1000;
19const MODE_BRACKETED_PASTE: u8 = 0b0001_0000;
20
21#[derive(Clone, Debug)]
22pub enum CharSet {
23  Ascii,
24  Uk,
25  DecLineDrawing,
26}
27
28/// The xterm mouse handling mode currently in use.
29#[derive(Copy, Clone, Debug, Eq, PartialEq)]
30pub enum MouseProtocolMode {
31  /// Mouse handling is disabled.
32  None,
33
34  /// Mouse button events should be reported on button press. Also known as
35  /// X10 mouse mode.
36  Press,
37
38  /// Mouse button events should be reported on button press and release.
39  /// Also known as VT200 mouse mode.
40  PressRelease,
41
42  // Highlight,
43  /// Mouse button events should be reported on button press and release, as
44  /// well as when the mouse moves between cells while a button is held
45  /// down.
46  ButtonMotion,
47
48  /// Mouse button events should be reported on button press and release,
49  /// and mouse motion events should be reported when the mouse moves
50  /// between cells regardless of whether a button is held down or not.
51  AnyMotion,
52  // DecLocator,
53}
54
55impl Default for MouseProtocolMode {
56  fn default() -> Self {
57    Self::None
58  }
59}
60
61/// The encoding to use for the enabled `MouseProtocolMode`.
62#[derive(Copy, Clone, Debug, Eq, PartialEq)]
63pub enum MouseProtocolEncoding {
64  /// Default single-printable-byte encoding.
65  Default,
66
67  /// UTF-8-based encoding.
68  Utf8,
69
70  /// SGR-like encoding.
71  Sgr,
72  // Urxvt,
73}
74
75impl Default for MouseProtocolEncoding {
76  fn default() -> Self {
77    Self::Default
78  }
79}
80
81/// Represents the overall terminal state.
82#[derive(Clone, Debug)]
83pub struct Screen<Reply: TermReplySender> {
84  reply_sender: Reply,
85
86  grid: crate::grid::Grid,
87  alternate_grid: crate::grid::Grid,
88
89  attrs: crate::attrs::Attrs,
90  saved_attrs: crate::attrs::Attrs,
91
92  title: String,
93  icon_name: String,
94
95  cursor_style: CursorStyle,
96
97  modes: u8,
98  mouse_protocol_mode: MouseProtocolMode,
99  mouse_protocol_encoding: MouseProtocolEncoding,
100
101  g0: CharSet,
102  g1: CharSet,
103  shift_out: bool,
104
105  /// If true, writing a character inserts a new cell
106  insert: bool,
107
108  audible_bell_count: usize,
109  visual_bell_count: usize,
110
111  errors: usize,
112}
113
114impl<Reply: TermReplySender> Screen<Reply> {
115  #[must_use]
116  pub fn get_selected_text(
117    &self,
118    low_x: i32,
119    low_y: i32,
120    high_x: i32,
121    high_y: i32,
122  ) -> String {
123    self.grid().get_selected_text(low_x, low_y, high_x, high_y)
124  }
125
126  pub(crate) fn new(
127    size: crate::grid::Size,
128    scrollback_len: usize,
129    reply_sender: Reply,
130  ) -> Self {
131    let mut grid = crate::grid::Grid::new(size, scrollback_len);
132    grid.allocate_rows();
133    Self {
134      reply_sender,
135      grid,
136      alternate_grid: crate::grid::Grid::new(size, 0),
137
138      attrs: crate::attrs::Attrs::default(),
139      saved_attrs: crate::attrs::Attrs::default(),
140
141      title: String::default(),
142      icon_name: String::default(),
143
144      cursor_style: CursorStyle::Default,
145
146      modes: 0,
147      mouse_protocol_mode: MouseProtocolMode::default(),
148      mouse_protocol_encoding: MouseProtocolEncoding::default(),
149
150      g0: CharSet::Ascii,
151      g1: CharSet::Ascii,
152      shift_out: false,
153
154      insert: false,
155
156      audible_bell_count: 0,
157      visual_bell_count: 0,
158
159      errors: 0,
160    }
161  }
162
163  pub(crate) fn set_size(&mut self, rows: u16, cols: u16) {
164    self.grid.set_size(crate::grid::Size { rows, cols });
165    self
166      .alternate_grid
167      .set_size(crate::grid::Size { rows, cols });
168  }
169
170  /// Returns the current size of the terminal.
171  ///
172  /// The return value will be (rows, cols).
173  #[must_use]
174  pub fn size(&self) -> (u16, u16) {
175    let size = self.grid().size();
176    (size.rows, size.cols)
177  }
178
179  /// Returns the current position in the scrollback.
180  ///
181  /// This position indicates the offset from the top of the screen, and is
182  /// `0` when the normal screen is in view.
183  #[must_use]
184  pub fn scrollback(&self) -> usize {
185    self.grid().scrollback()
186  }
187
188  #[must_use]
189  pub fn scrollback_len(&self) -> usize {
190    self.grid().scrollback_len()
191  }
192
193  pub fn set_scrollback(&mut self, rows: usize) {
194    self.grid_mut().set_scrollback(rows);
195  }
196
197  /// Returns the text contents of the terminal.
198  ///
199  /// This will not include any formatting information, and will be in plain
200  /// text format.
201  #[must_use]
202  pub fn contents(&self) -> String {
203    let mut contents = String::new();
204    self.write_contents(&mut contents);
205    contents
206  }
207
208  fn write_contents(&self, contents: &mut String) {
209    self.grid().write_contents(contents);
210  }
211
212  /// Returns the text contents of the terminal by row, restricted to the
213  /// given subset of columns.
214  ///
215  /// This will not include any formatting information, and will be in plain
216  /// text format.
217  ///
218  /// Newlines will not be included.
219  pub fn rows(
220    &self,
221    start: u16,
222    width: u16,
223  ) -> impl Iterator<Item = String> + '_ {
224    self.grid().visible_rows().map(move |row| {
225      let mut contents = String::new();
226      row.write_contents(&mut contents, start, width, false);
227      contents
228    })
229  }
230
231  /// Returns the text contents of the terminal logically between two cells.
232  /// This will include the remainder of the starting row after `start_col`,
233  /// followed by the entire contents of the rows between `start_row` and
234  /// `end_row`, followed by the beginning of the `end_row` up until
235  /// `end_col`. This is useful for things like determining the contents of
236  /// a clipboard selection.
237  #[must_use]
238  pub fn contents_between(
239    &self,
240    start_row: u16,
241    start_col: u16,
242    end_row: u16,
243    end_col: u16,
244  ) -> String {
245    match start_row.cmp(&end_row) {
246      std::cmp::Ordering::Less => {
247        let (_, cols) = self.size();
248        let mut contents = String::new();
249        for (i, row) in self
250          .grid()
251          .visible_rows()
252          .enumerate()
253          .skip(usize::from(start_row))
254          .take(usize::from(end_row) - usize::from(start_row) + 1)
255        {
256          if i == usize::from(start_row) {
257            row.write_contents(
258              &mut contents,
259              start_col,
260              cols - start_col,
261              false,
262            );
263            if !row.wrapped() {
264              contents.push('\n');
265            }
266          } else if i == usize::from(end_row) {
267            row.write_contents(&mut contents, 0, end_col, false);
268          } else {
269            row.write_contents(&mut contents, 0, cols, false);
270            if !row.wrapped() {
271              contents.push('\n');
272            }
273          }
274        }
275        contents
276      }
277      std::cmp::Ordering::Equal => {
278        if start_col < end_col {
279          self
280            .rows(start_col, end_col - start_col)
281            .nth(usize::from(start_row))
282            .unwrap_or_else(String::new)
283        } else {
284          String::new()
285        }
286      }
287      std::cmp::Ordering::Greater => String::new(),
288    }
289  }
290
291  /// Return escape codes sufficient to reproduce the entire contents of the
292  /// current terminal state. This is a convenience wrapper around
293  /// `contents_formatted`, `input_mode_formatted`, and `title_formatted`.
294  #[must_use]
295  pub fn state_formatted(&self) -> Vec<u8> {
296    let mut contents = vec![];
297    self.write_contents_formatted(&mut contents);
298    self.write_input_mode_formatted(&mut contents);
299    self.write_title_formatted(&mut contents);
300    contents
301  }
302
303  /// Return escape codes sufficient to turn the terminal state of the
304  /// screen `prev` into the current terminal state. This is a convenience
305  /// wrapper around `contents_diff`, `input_mode_diff`, `title_diff`, and
306  /// `bells_diff`.
307  #[must_use]
308  pub fn state_diff(&self, prev: &Self) -> Vec<u8> {
309    let mut contents = vec![];
310    self.write_contents_diff(&mut contents, prev);
311    self.write_input_mode_diff(&mut contents, prev);
312    self.write_title_diff(&mut contents, prev);
313    self.write_bells_diff(&mut contents, prev);
314    contents
315  }
316
317  /// Returns the formatted visible contents of the terminal.
318  ///
319  /// Formatting information will be included inline as terminal escape
320  /// codes. The result will be suitable for feeding directly to a raw
321  /// terminal parser, and will result in the same visual output.
322  #[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  /// Returns the formatted visible contents of the terminal by row,
336  /// restricted to the given subset of columns.
337  ///
338  /// Formatting information will be included inline as terminal escape
339  /// codes. The result will be suitable for feeding directly to a raw
340  /// terminal parser, and will result in the same visual output.
341  ///
342  /// You are responsible for positioning the cursor before printing each
343  /// row, and the final cursor position after displaying each row is
344  /// unspecified.
345  // the unwraps in this method shouldn't be reachable
346  #[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      // number of rows in a grid is stored in a u16 (see Size), so
355      // visible_rows can never return enough rows to overflow here
356      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  /// Returns a terminal byte stream sufficient to turn the visible contents
375  /// of the screen described by `prev` into the visible contents of the
376  /// screen described by `self`.
377  ///
378  /// The result of rendering `prev.contents_formatted()` followed by
379  /// `self.contents_diff(prev)` should be equivalent to the result of
380  /// rendering `self.contents_formatted()`. This is primarily useful when
381  /// you already have a terminal parser whose state is described by `prev`,
382  /// since the diff will likely require less memory and cause less
383  /// flickering than redrawing the entire screen contents.
384  #[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()).write_buf(contents);
394    }
395    let prev_attrs =
396      self
397        .grid()
398        .write_contents_diff(contents, prev.grid(), prev.attrs);
399    self.attrs.write_escape_code_diff(contents, &prev_attrs);
400  }
401
402  /// Returns a sequence of terminal byte streams sufficient to turn the
403  /// visible contents of the subset of each row from `prev` (as described
404  /// by `start` and `width`) into the visible contents of the corresponding
405  /// row subset in `self`.
406  ///
407  /// You are responsible for positioning the cursor before printing each
408  /// row, and the final cursor position after displaying each row is
409  /// unspecified.
410  // the unwraps in this method shouldn't be reachable
411  #[allow(clippy::missing_panics_doc)]
412  pub fn rows_diff<'a>(
413    &'a self,
414    prev: &'a Self,
415    start: u16,
416    width: u16,
417  ) -> impl Iterator<Item = Vec<u8>> + 'a {
418    self
419      .grid()
420      .visible_rows()
421      .zip(prev.grid().visible_rows())
422      .enumerate()
423      .map(move |(i, (row, prev_row))| {
424        // number of rows in a grid is stored in a u16 (see Size), so
425        // visible_rows can never return enough rows to overflow here
426        let i = i.try_into().unwrap();
427        let mut contents = vec![];
428        row.write_contents_diff(
429          &mut contents,
430          prev_row,
431          start,
432          width,
433          i,
434          false,
435          false,
436          crate::grid::Pos { row: i, col: start },
437          crate::attrs::Attrs::default(),
438        );
439        contents
440      })
441  }
442
443  /// Returns terminal escape sequences sufficient to set the current
444  /// terminal's input modes.
445  ///
446  /// Supported modes are:
447  /// * application keypad
448  /// * application cursor
449  /// * bracketed paste
450  /// * xterm mouse support
451  #[must_use]
452  pub fn input_mode_formatted(&self) -> Vec<u8> {
453    let mut contents = vec![];
454    self.write_input_mode_formatted(&mut contents);
455    contents
456  }
457
458  fn write_input_mode_formatted(&self, contents: &mut Vec<u8>) {
459    crate::term::ApplicationKeypad::new(self.mode(MODE_APPLICATION_KEYPAD))
460      .write_buf(contents);
461    crate::term::ApplicationCursor::new(self.mode(MODE_APPLICATION_CURSOR))
462      .write_buf(contents);
463    crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
464      .write_buf(contents);
465    crate::term::MouseProtocolMode::new(
466      self.mouse_protocol_mode,
467      MouseProtocolMode::None,
468    )
469    .write_buf(contents);
470    crate::term::MouseProtocolEncoding::new(
471      self.mouse_protocol_encoding,
472      MouseProtocolEncoding::Default,
473    )
474    .write_buf(contents);
475  }
476
477  /// Returns terminal escape sequences sufficient to change the previous
478  /// terminal's input modes to the input modes enabled in the current
479  /// terminal.
480  #[must_use]
481  pub fn input_mode_diff(&self, prev: &Self) -> Vec<u8> {
482    let mut contents = vec![];
483    self.write_input_mode_diff(&mut contents, prev);
484    contents
485  }
486
487  fn write_input_mode_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
488    if self.mode(MODE_APPLICATION_KEYPAD) != prev.mode(MODE_APPLICATION_KEYPAD)
489    {
490      crate::term::ApplicationKeypad::new(self.mode(MODE_APPLICATION_KEYPAD))
491        .write_buf(contents);
492    }
493    if self.mode(MODE_APPLICATION_CURSOR) != prev.mode(MODE_APPLICATION_CURSOR)
494    {
495      crate::term::ApplicationCursor::new(self.mode(MODE_APPLICATION_CURSOR))
496        .write_buf(contents);
497    }
498    if self.mode(MODE_BRACKETED_PASTE) != prev.mode(MODE_BRACKETED_PASTE) {
499      crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
500        .write_buf(contents);
501    }
502    crate::term::MouseProtocolMode::new(
503      self.mouse_protocol_mode,
504      prev.mouse_protocol_mode,
505    )
506    .write_buf(contents);
507    crate::term::MouseProtocolEncoding::new(
508      self.mouse_protocol_encoding,
509      prev.mouse_protocol_encoding,
510    )
511    .write_buf(contents);
512  }
513
514  /// Returns terminal escape sequences sufficient to set the current
515  /// terminal's window title.
516  #[must_use]
517  pub fn title_formatted(&self) -> Vec<u8> {
518    let mut contents = vec![];
519    self.write_title_formatted(&mut contents);
520    contents
521  }
522
523  fn write_title_formatted(&self, contents: &mut Vec<u8>) {
524    crate::term::ChangeTitle::new(&self.icon_name, &self.title, "", "")
525      .write_buf(contents);
526  }
527
528  /// Returns terminal escape sequences sufficient to change the previous
529  /// terminal's window title to the window title set in the current
530  /// terminal.
531  #[must_use]
532  pub fn title_diff(&self, prev: &Self) -> Vec<u8> {
533    let mut contents = vec![];
534    self.write_title_diff(&mut contents, prev);
535    contents
536  }
537
538  fn write_title_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
539    crate::term::ChangeTitle::new(
540      &self.icon_name,
541      &self.title,
542      &prev.icon_name,
543      &prev.title,
544    )
545    .write_buf(contents);
546  }
547
548  /// Returns terminal escape sequences sufficient to cause audible and
549  /// visual bells to occur if they have been received since the terminal
550  /// described by `prev`.
551  #[must_use]
552  pub fn bells_diff(&self, prev: &Self) -> Vec<u8> {
553    let mut contents = vec![];
554    self.write_bells_diff(&mut contents, prev);
555    contents
556  }
557
558  fn write_bells_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
559    if self.audible_bell_count != prev.audible_bell_count {
560      crate::term::AudibleBell.write_buf(contents);
561    }
562    if self.visual_bell_count != prev.visual_bell_count {
563      crate::term::VisualBell.write_buf(contents);
564    }
565  }
566
567  /// Returns terminal escape sequences sufficient to set the current
568  /// terminal's drawing attributes.
569  ///
570  /// Supported drawing attributes are:
571  /// * fgcolor
572  /// * bgcolor
573  /// * bold
574  /// * italic
575  /// * underline
576  /// * inverse
577  ///
578  /// This is not typically necessary, since `contents_formatted` will leave
579  /// the current active drawing attributes in the correct state, but this
580  /// can be useful in the case of drawing additional things on top of a
581  /// terminal output, since you will need to restore the terminal state
582  /// without the terminal contents necessarily being the same.
583  #[must_use]
584  pub fn attributes_formatted(&self) -> Vec<u8> {
585    let mut contents = vec![];
586    self.write_attributes_formatted(&mut contents);
587    contents
588  }
589
590  fn write_attributes_formatted(&self, contents: &mut Vec<u8>) {
591    crate::term::ClearAttrs.write_buf(contents);
592    self
593      .attrs
594      .write_escape_code_diff(contents, &crate::attrs::Attrs::default());
595  }
596
597  /// Returns the current cursor position of the terminal.
598  ///
599  /// The return value will be (row, col).
600  #[must_use]
601  pub fn cursor_position(&self) -> (u16, u16) {
602    let pos = self.grid().pos();
603    (pos.row, pos.col)
604  }
605
606  /// Returns terminal escape sequences sufficient to set the current
607  /// cursor state of the terminal.
608  ///
609  /// This is not typically necessary, since `contents_formatted` will leave
610  /// the cursor in the correct state, but this can be useful in the case of
611  /// drawing additional things on top of a terminal output, since you will
612  /// need to restore the terminal state without the terminal contents
613  /// necessarily being the same.
614  ///
615  /// Note that the bytes returned by this function may alter the active
616  /// drawing attributes, because it may require redrawing existing cells in
617  /// order to position the cursor correctly (for instance, in the case
618  /// where the cursor is past the end of a row). Therefore, you should
619  /// ensure to reset the active drawing attributes if necessary after
620  /// processing this data, for instance by using `attributes_formatted`.
621  #[must_use]
622  pub fn cursor_state_formatted(&self) -> Vec<u8> {
623    let mut contents = vec![];
624    self.write_cursor_state_formatted(&mut contents);
625    contents
626  }
627
628  fn write_cursor_state_formatted(&self, contents: &mut Vec<u8>) {
629    crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
630    self
631      .grid()
632      .write_cursor_position_formatted(contents, None, None);
633
634    // we don't just call write_attributes_formatted here, because that
635    // would still be confusing - consider the case where the user sets
636    // their own unrelated drawing attributes (on a different parser
637    // instance) and then calls cursor_state_formatted. just documenting
638    // it and letting the user handle it on their own is more
639    // straightforward.
640  }
641
642  /// Returns the `Cell` object at the given location in the terminal, if it
643  /// exists.
644  #[must_use]
645  pub fn cell(&self, row: u16, col: u16) -> Option<&crate::cell::Cell> {
646    self.grid().visible_cell(crate::grid::Pos { row, col })
647  }
648
649  /// Returns whether the text in row `row` should wrap to the next line.
650  #[must_use]
651  pub fn row_wrapped(&self, row: u16) -> bool {
652    self
653      .grid()
654      .visible_row(row)
655      .is_some_and(crate::row::Row::wrapped)
656  }
657
658  /// Returns the terminal's window title.
659  #[must_use]
660  pub fn title(&self) -> &str {
661    &self.title
662  }
663
664  /// Returns the terminal's icon name.
665  #[must_use]
666  pub fn icon_name(&self) -> &str {
667    &self.icon_name
668  }
669
670  #[must_use]
671  pub fn cursor_style(&self) -> CursorStyle {
672    self.cursor_style
673  }
674
675  /// Returns a value which changes every time an audible bell is received.
676  ///
677  /// Typically you would store this number after each call to `process`,
678  /// and trigger an audible bell whenever it changes.
679  ///
680  /// You shouldn't rely on the exact value returned here, since the exact
681  /// value will not be maintained by `contents_formatted` or
682  /// `contents_diff`.
683  #[must_use]
684  pub fn audible_bell_count(&self) -> usize {
685    self.audible_bell_count
686  }
687
688  /// Returns a value which changes every time an visual bell is received.
689  ///
690  /// Typically you would store this number after each call to `process`,
691  /// and trigger an visual bell whenever it changes.
692  ///
693  /// You shouldn't rely on the exact value returned here, since the exact
694  /// value will not be maintained by `contents_formatted` or
695  /// `contents_diff`.
696  #[must_use]
697  pub fn visual_bell_count(&self) -> usize {
698    self.visual_bell_count
699  }
700
701  /// Returns the number of parsing errors seen so far.
702  ///
703  /// Currently this only tracks invalid UTF-8 and control characters other
704  /// than `0x07`-`0x0f`. This can give an idea of whether the input stream
705  /// being fed to the parser is reasonable or not.
706  #[must_use]
707  pub fn errors(&self) -> usize {
708    self.errors
709  }
710
711  /// Returns whether the alternate screen is currently in use.
712  #[must_use]
713  pub fn alternate_screen(&self) -> bool {
714    self.mode(MODE_ALTERNATE_SCREEN)
715  }
716
717  /// Returns whether the terminal should be in application keypad mode.
718  #[must_use]
719  pub fn application_keypad(&self) -> bool {
720    self.mode(MODE_APPLICATION_KEYPAD)
721  }
722
723  /// Returns whether the terminal should be in application cursor mode.
724  #[must_use]
725  pub fn application_cursor(&self) -> bool {
726    self.mode(MODE_APPLICATION_CURSOR)
727  }
728
729  /// Returns whether the terminal should be in hide cursor mode.
730  #[must_use]
731  pub fn hide_cursor(&self) -> bool {
732    self.mode(MODE_HIDE_CURSOR)
733  }
734
735  /// Returns whether the terminal should be in bracketed paste mode.
736  #[must_use]
737  pub fn bracketed_paste(&self) -> bool {
738    self.mode(MODE_BRACKETED_PASTE)
739  }
740
741  /// Returns the currently active `MouseProtocolMode`
742  #[must_use]
743  pub fn mouse_protocol_mode(&self) -> MouseProtocolMode {
744    self.mouse_protocol_mode
745  }
746
747  /// Returns the currently active `MouseProtocolEncoding`
748  #[must_use]
749  pub fn mouse_protocol_encoding(&self) -> MouseProtocolEncoding {
750    self.mouse_protocol_encoding
751  }
752
753  /// Returns the currently active foreground color.
754  #[must_use]
755  pub fn fgcolor(&self) -> crate::attrs::Color {
756    self.attrs.fgcolor
757  }
758
759  /// Returns the currently active background color.
760  #[must_use]
761  pub fn bgcolor(&self) -> crate::attrs::Color {
762    self.attrs.bgcolor
763  }
764
765  /// Returns whether newly drawn text should be rendered with the bold text
766  /// attribute.
767  #[must_use]
768  pub fn bold(&self) -> bool {
769    self.attrs.bold()
770  }
771
772  /// Returns whether newly drawn text should be rendered with the italic
773  /// text attribute.
774  #[must_use]
775  pub fn italic(&self) -> bool {
776    self.attrs.italic()
777  }
778
779  /// Returns whether newly drawn text should be rendered with the
780  /// underlined text attribute.
781  #[must_use]
782  pub fn underline(&self) -> bool {
783    self.attrs.underline()
784  }
785
786  /// Returns whether newly drawn text should be rendered with the inverse
787  /// text attribute.
788  #[must_use]
789  pub fn inverse(&self) -> bool {
790    self.attrs.inverse()
791  }
792
793  fn grid(&self) -> &crate::grid::Grid {
794    if self.mode(MODE_ALTERNATE_SCREEN) {
795      &self.alternate_grid
796    } else {
797      &self.grid
798    }
799  }
800
801  fn grid_mut(&mut self) -> &mut crate::grid::Grid {
802    if self.mode(MODE_ALTERNATE_SCREEN) {
803      &mut self.alternate_grid
804    } else {
805      &mut self.grid
806    }
807  }
808
809  fn enter_alternate_grid(&mut self) {
810    self.grid_mut().set_scrollback(0);
811    self.set_mode(MODE_ALTERNATE_SCREEN);
812    self.alternate_grid.allocate_rows();
813  }
814
815  fn exit_alternate_grid(&mut self) {
816    self.clear_mode(MODE_ALTERNATE_SCREEN);
817  }
818
819  fn save_cursor(&mut self) {
820    self.grid_mut().save_cursor();
821    self.saved_attrs = self.attrs;
822  }
823
824  fn restore_cursor(&mut self) {
825    self.grid_mut().restore_cursor();
826    self.attrs = self.saved_attrs;
827  }
828
829  fn set_mode(&mut self, mode: u8) {
830    self.modes |= mode;
831  }
832
833  fn clear_mode(&mut self, mode: u8) {
834    self.modes &= !mode;
835  }
836
837  fn mode(&self, mode: u8) -> bool {
838    self.modes & mode != 0
839  }
840
841  fn set_mouse_mode(&mut self, mode: MouseProtocolMode) {
842    self.mouse_protocol_mode = mode;
843  }
844
845  fn clear_mouse_mode(&mut self, mode: MouseProtocolMode) {
846    if self.mouse_protocol_mode == mode {
847      self.mouse_protocol_mode = MouseProtocolMode::default();
848    }
849  }
850
851  fn set_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
852    self.mouse_protocol_encoding = encoding;
853  }
854
855  fn clear_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
856    if self.mouse_protocol_encoding == encoding {
857      self.mouse_protocol_encoding = MouseProtocolEncoding::default();
858    }
859  }
860
861  #[must_use]
862  pub fn is_wide_continuation(&self, row: u16, col: u16) -> bool {
863    self
864      .grid()
865      .is_wide_continuation(crate::grid::Pos { row, col })
866  }
867}
868
869impl<Reply: TermReplySender + Clone> Screen<Reply> {
870  fn text(&mut self, c: char) {
871    let pos = self.grid().pos();
872    let size = self.grid().size();
873    let attrs = self.attrs;
874
875    let width = c.width();
876    if width.is_none() && (u32::from(c)) < 256 {
877      // don't even try to draw control characters
878      return;
879    }
880    let width = width
881      .unwrap_or(1)
882      .try_into()
883      // width() can only return 0, 1, or 2
884      .unwrap();
885
886    // it doesn't make any sense to wrap if the last column in a row
887    // didn't already have contents. don't try to handle the case where a
888    // character wraps because there was only one column left in the
889    // previous row - literally everything handles this case differently,
890    // and this is tmux behavior (and also the simplest). i'm open to
891    // reconsidering this behavior, but only with a really good reason
892    // (xterm handles this by introducing the concept of triple width
893    // cells, which i really don't want to do).
894    let mut wrap = false;
895    if pos.col > size.cols - width {
896      let last_cell_pos = crate::grid::Pos {
897        row: pos.row,
898        col: size.cols - 1,
899      };
900      let last_cell = self
901        .grid()
902        .drawing_cell(last_cell_pos)
903        // pos.row is valid, since it comes directly from
904        // self.grid().pos() which we assume to always have a valid
905        // row value. size.cols - 1 is also always a valid column.
906        .unwrap();
907      if last_cell.has_contents()
908        || self.grid().is_wide_continuation(last_cell_pos)
909      {
910        wrap = true;
911      }
912    }
913    self.grid_mut().col_wrap(width, wrap);
914    let pos = self.grid().pos();
915
916    if width == 0 {
917      if pos.col > 0 {
918        let prev_cell_pos = crate::grid::Pos {
919          row: pos.row,
920          col: pos.col - 1,
921        };
922        let prev_cell_pos = if self.grid().is_wide_continuation(prev_cell_pos) {
923          crate::grid::Pos {
924            row: pos.row,
925            col: pos.col - 2,
926          }
927        } else {
928          prev_cell_pos
929        };
930        let prev_cell = self
931          .grid_mut()
932          .drawing_cell_mut(prev_cell_pos)
933          // pos.row is valid, since it comes directly from
934          // self.grid().pos() which we assume to always have a
935          // valid row value. pos.col - 1 is valid because we just
936          // checked for pos.col > 0.
937          // pos.col - 2 is valid because pos.col - 1 is a wide continuation
938          .unwrap();
939        prev_cell.append(c);
940      } else if pos.row > 0 {
941        let prev_row = self
942          .grid()
943          .drawing_row(pos.row - 1)
944          // pos.row is valid, since it comes directly from
945          // self.grid().pos() which we assume to always have a
946          // valid row value. pos.row - 1 is valid because we just
947          // checked for pos.row > 0.
948          .unwrap();
949        if prev_row.wrapped() {
950          let prev_cell_pos = crate::grid::Pos {
951            row: pos.row - 1,
952            col: size.cols - 1,
953          };
954          let prev_cell_pos = if self.grid().is_wide_continuation(prev_cell_pos)
955          {
956            crate::grid::Pos {
957              row: pos.row - 1,
958              col: size.cols - 2,
959            }
960          } else {
961            prev_cell_pos
962          };
963          let prev_cell = self
964            .grid_mut()
965            .drawing_cell_mut(prev_cell_pos)
966            // pos.row is valid, since it comes directly from
967            // self.grid().pos() which we assume to always
968            // have a valid row value. pos.row - 1 is valid
969            // because we just checked for pos.row > 0. col of
970            // size.cols - 2 is valid because the cell at
971            // size.cols - 1 is a wide continuation character,
972            // so it must have the first half of the wide
973            // character before it.
974            .unwrap();
975          prev_cell.append(c);
976        }
977      }
978    } else {
979      if self.grid().is_wide_continuation(pos) {
980        let prev_cell = self
981          .grid_mut()
982          .drawing_cell_mut(crate::grid::Pos {
983            row: pos.row,
984            col: pos.col - 1,
985          })
986          // pos.row is valid because we assume self.grid().pos() to
987          // always have a valid row value. pos.col is valid because
988          // we called col_wrap() immediately before this, which
989          // ensures that self.grid().pos().col has a valid value.
990          // pos.col - 1 is valid because the cell at pos.col is a
991          // wide continuation character, so it must have the first
992          // half of the wide character before it.
993          .unwrap();
994        prev_cell.clear(attrs);
995      }
996
997      if self
998        .grid()
999        .drawing_cell(pos)
1000        // pos.row is valid because we assume self.grid().pos() to
1001        // always have a valid row value. pos.col is valid because we
1002        // called col_wrap() immediately before this, which ensures
1003        // that self.grid().pos().col has a valid value.
1004        .unwrap()
1005        .is_wide()
1006      {
1007        if let Some(next_cell) =
1008          self.grid_mut().drawing_cell_mut(crate::grid::Pos {
1009            row: pos.row,
1010            col: pos.col + 1,
1011          })
1012        {
1013          next_cell.set(' ', attrs);
1014        }
1015      }
1016
1017      let cell = self
1018        .grid_mut()
1019        .drawing_cell_mut(pos)
1020        // pos.row is valid because we assume self.grid().pos() to
1021        // always have a valid row value. pos.col is valid because we
1022        // called col_wrap() immediately before this, which ensures
1023        // that self.grid().pos().col has a valid value.
1024        .unwrap();
1025      cell.set(c, attrs);
1026      self.grid_mut().col_inc(1);
1027      if width > 1 {
1028        let pos = self.grid().pos();
1029        if self
1030          .grid()
1031          .drawing_cell(pos)
1032          // pos.row is valid because we assume self.grid().pos() to
1033          // always have a valid row value. pos.col is valid because
1034          // we called col_wrap() earlier, which ensures that
1035          // self.grid().pos().col has a valid value. this is true
1036          // even though we just called col_inc, because this branch
1037          // only happens if width > 1, and col_wrap takes width
1038          // into account.
1039          .unwrap()
1040          .is_wide()
1041        {
1042          let next_next_pos = crate::grid::Pos {
1043            row: pos.row,
1044            col: pos.col + 1,
1045          };
1046          let next_next_cell = self
1047            .grid_mut()
1048            .drawing_cell_mut(next_next_pos)
1049            // pos.row is valid because we assume
1050            // self.grid().pos() to always have a valid row value.
1051            // pos.col is valid because we called col_wrap()
1052            // earlier, which ensures that self.grid().pos().col
1053            // has a valid value. this is true even though we just
1054            // called col_inc, because this branch only happens if
1055            // width > 1, and col_wrap takes width into account.
1056            // pos.col + 1 is valid because the cell at pos.col is
1057            // wide, and so it must have the second half of the
1058            // wide character after it.
1059            .unwrap();
1060          next_next_cell.clear(attrs);
1061          if next_next_pos.col == size.cols - 1 {
1062            self
1063              .grid_mut()
1064              .drawing_row_mut(pos.row)
1065              // we assume self.grid().pos().row is always valid
1066              .unwrap()
1067              .wrap(false);
1068          }
1069        }
1070        let next_cell = self
1071          .grid_mut()
1072          .drawing_cell_mut(pos)
1073          // pos.row is valid because we assume self.grid().pos() to
1074          // always have a valid row value. pos.col is valid because
1075          // we called col_wrap() earlier, which ensures that
1076          // self.grid().pos().col has a valid value. this is true
1077          // even though we just called col_inc, because this branch
1078          // only happens if width > 1, and col_wrap takes width
1079          // into account.
1080          .unwrap();
1081        next_cell.clear(crate::attrs::Attrs::default());
1082        self.grid_mut().col_inc(1);
1083      }
1084    }
1085  }
1086
1087  // control codes
1088
1089  fn bel(&mut self) {
1090    self.audible_bell_count += 1;
1091  }
1092
1093  fn tab(&mut self) {
1094    self.grid_mut().col_tab();
1095  }
1096
1097  // escape codes
1098
1099  // ESC 7
1100  fn decsc(&mut self) {
1101    self.save_cursor();
1102  }
1103
1104  // ESC 8
1105  fn decrc(&mut self) {
1106    self.restore_cursor();
1107  }
1108
1109  // ESC =
1110  fn deckpam(&mut self) {
1111    self.set_mode(MODE_APPLICATION_KEYPAD);
1112  }
1113
1114  // ESC M
1115  fn ri(&mut self) {
1116    self.grid_mut().row_dec_scroll(1);
1117  }
1118
1119  // ESC c
1120  fn ris(&mut self) {
1121    let title = self.title.clone();
1122    let icon_name = self.icon_name.clone();
1123    let audible_bell_count = self.audible_bell_count;
1124    let visual_bell_count = self.visual_bell_count;
1125    let errors = self.errors;
1126
1127    *self = Self::new(
1128      self.grid.size(),
1129      self.grid.scrollback_len(),
1130      self.reply_sender.clone(),
1131    );
1132
1133    self.title = title;
1134    self.icon_name = icon_name;
1135    self.audible_bell_count = audible_bell_count;
1136    self.visual_bell_count = visual_bell_count;
1137    self.errors = errors;
1138  }
1139
1140  // ESC g
1141  fn vb(&mut self) {
1142    self.visual_bell_count += 1;
1143  }
1144
1145  // csi codes
1146
1147  // CSI @
1148  fn ich(&mut self, count: u16) {
1149    self.grid_mut().insert_cells(count);
1150  }
1151
1152  // CSI J
1153  fn ed(&mut self, mode: u16) {
1154    let attrs = self.attrs;
1155    match mode {
1156      0 => self.grid_mut().erase_all_forward(attrs),
1157      1 => self.grid_mut().erase_all_backward(attrs),
1158      2 => self.grid_mut().erase_all(attrs),
1159      n => {
1160        log::debug!("unhandled ED mode: {n}");
1161      }
1162    }
1163  }
1164
1165  // CSI ? J
1166  fn decsed(&mut self, mode: u16) {
1167    self.ed(mode);
1168  }
1169
1170  // CSI K
1171  fn el(&mut self, mode: u16) {
1172    let attrs = self.attrs;
1173    match mode {
1174      0 => self.grid_mut().erase_row_forward(attrs),
1175      1 => self.grid_mut().erase_row_backward(attrs),
1176      2 => self.grid_mut().erase_row(attrs),
1177      n => {
1178        log::debug!("unhandled EL mode: {n}");
1179      }
1180    }
1181  }
1182
1183  // CSI ? K
1184  fn decsel(&mut self, mode: u16) {
1185    self.el(mode);
1186  }
1187}
1188
1189macro_rules! skip {
1190  ($fmt:expr) => {
1191    {
1192      use std::fmt::Write;
1193      let mut output = String::new();
1194      write!(output, $fmt).unwrap();
1195      log::debug!("Skip seq: {}", output);
1196    }
1197  };
1198  ($fmt:expr, $($arg:tt)*) => {
1199    {
1200      use std::fmt::Write;
1201      let mut output = String::new();
1202      write!(output, $fmt, $($arg)*).unwrap();
1203      log::debug!("Skip seq: {}", output);
1204    }
1205  };
1206}
1207
1208#[allow(clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
1209impl<Reply: TermReplySender + Clone> Screen<Reply> {
1210  pub fn handle_action(&mut self, action: Action) {
1211    match action {
1212      Action::Print(c) => self.text(c),
1213      Action::PrintString(s) => s.chars().for_each(|c| self.text(c)),
1214      Action::Control(code) => self.handle_control(code),
1215      Action::DeviceControl(mode) => self.handle_device_control(mode),
1216      Action::OperatingSystemCommand(cmd) => self.handle_os_command(*cmd),
1217      Action::CSI(csi) => self.handle_csi(csi),
1218      Action::Esc(esc) => self.handle_esc(esc),
1219      Action::Sixel(_) => (),
1220      Action::XtGetTcap(names) => self.handle_xt_get_tcap(names),
1221      Action::KittyImage(_) => (),
1222    }
1223  }
1224
1225  fn handle_control(&mut self, code: ControlCode) {
1226    match code {
1227      ControlCode::Null => {}
1228      ControlCode::StartOfHeading => skip!("StartOfHeading"),
1229      ControlCode::StartOfText => skip!("StartOfText"),
1230      ControlCode::EndOfText => skip!("EndOfText"),
1231      ControlCode::EndOfTransmission => skip!("EndOfTransmission"),
1232      ControlCode::Enquiry => skip!("Enquiry"),
1233      ControlCode::Acknowledge => skip!("Acknowledge"),
1234      ControlCode::Bell => self.bel(),
1235      ControlCode::Backspace => self.grid_mut().col_dec(1),
1236      ControlCode::HorizontalTab => self.tab(),
1237      ControlCode::LineFeed => {
1238        self.grid_mut().row_inc_scroll(1);
1239      }
1240      ControlCode::VerticalTab => {
1241        self.grid_mut().row_inc_scroll(1);
1242      }
1243      ControlCode::FormFeed => {
1244        self.grid_mut().row_inc_scroll(1);
1245      }
1246      ControlCode::CarriageReturn => self.grid_mut().col_set(0),
1247      ControlCode::ShiftOut => self.shift_out = true,
1248      ControlCode::ShiftIn => self.shift_out = false,
1249      ControlCode::DataLinkEscape => skip!("DataLinkEscape"),
1250      ControlCode::DeviceControlOne => skip!("DeviceControlOne"),
1251      ControlCode::DeviceControlTwo => skip!("DeviceControlTwo"),
1252      ControlCode::DeviceControlThree => skip!("DeviceControlThree"),
1253      ControlCode::DeviceControlFour => skip!("DeviceControlFour"),
1254      ControlCode::NegativeAcknowledge => skip!("NegativeAcknowledge"),
1255      ControlCode::SynchronousIdle => skip!("SynchronousIdle"),
1256      ControlCode::EndOfTransmissionBlock => {
1257        skip!("EndOfTransmissionBlock")
1258      }
1259      ControlCode::Cancel => skip!("Cancel"),
1260      ControlCode::EndOfMedium => skip!("EndOfMedium"),
1261      ControlCode::Substitute => skip!("Substitute"),
1262      ControlCode::Escape => skip!("Escape"),
1263      ControlCode::FileSeparator => skip!("FileSeparator"),
1264      ControlCode::GroupSeparator => skip!("GroupSeparator"),
1265      ControlCode::RecordSeparator => skip!("RecordSeparator"),
1266      ControlCode::UnitSeparator => skip!("UnitSeparator"),
1267      ControlCode::BPH => skip!("BPH"),
1268      ControlCode::NBH => skip!("NBH"),
1269      ControlCode::IND => skip!("IND"),
1270      ControlCode::NEL => skip!("NEL"),
1271      ControlCode::SSA => skip!("SSA"),
1272      ControlCode::ESA => skip!("ESA"),
1273      ControlCode::HTS => skip!("HTS"),
1274      ControlCode::HTJ => skip!("HTJ"),
1275      ControlCode::VTS => skip!("VTS"),
1276      ControlCode::PLD => skip!("PLD"),
1277      ControlCode::PLU => skip!("PLU"),
1278      ControlCode::RI => self.ri(),
1279      ControlCode::SS2 => skip!("SS2"),
1280      ControlCode::SS3 => skip!("SS3"),
1281      ControlCode::DCS => skip!("DCS"),
1282      ControlCode::PU1 => skip!("PU1"),
1283      ControlCode::PU2 => skip!("PU2"),
1284      ControlCode::STS => skip!("STS"),
1285      ControlCode::CCH => skip!("CCH"),
1286      ControlCode::MW => skip!("MW"),
1287      ControlCode::SPA => skip!("SPA"),
1288      ControlCode::EPA => skip!("EPA"),
1289      ControlCode::SOS => skip!("SOS"),
1290      ControlCode::SCI => skip!("SCI"),
1291      ControlCode::CSI => skip!("CSI"),
1292      ControlCode::ST => skip!("ST"),
1293      ControlCode::OSC => skip!("OSC"),
1294      ControlCode::PM => skip!("PM"),
1295      ControlCode::APC => skip!("APC"),
1296    }
1297  }
1298
1299  fn handle_device_control(&mut self, _mode: DeviceControlMode) {
1300    skip!("DeviceControl");
1301  }
1302
1303  fn handle_os_command(&mut self, cmd: OperatingSystemCommand) {
1304    match cmd {
1305      OperatingSystemCommand::SetIconNameAndWindowTitle(icon_and_title) => {
1306        self.icon_name.clone_from(&icon_and_title);
1307        self.icon_name = icon_and_title.clone();
1308        self.title = icon_and_title;
1309      }
1310      OperatingSystemCommand::SetWindowTitle(title) => self.title = title,
1311      OperatingSystemCommand::SetWindowTitleSun(_) => {
1312        skip!("SetWindowTitleSun")
1313      }
1314      OperatingSystemCommand::SetIconName(icon) => self.icon_name = icon,
1315      OperatingSystemCommand::SetIconNameSun(_) => skip!("SetIconNameSun"),
1316      OperatingSystemCommand::SetHyperlink(_) => skip!("SetHyperlink"),
1317      OperatingSystemCommand::ClearSelection(_) => skip!("ClearSelection"),
1318      OperatingSystemCommand::QuerySelection(_) => skip!("QuerySelection"),
1319      OperatingSystemCommand::SetSelection(_, _) => skip!("SetSelection"),
1320      OperatingSystemCommand::SystemNotification(_) => {
1321        skip!("SystemNotification")
1322      }
1323      OperatingSystemCommand::ITermProprietary(_) => skip!("ITermProprietary"),
1324      OperatingSystemCommand::FinalTermSemanticPrompt(p) => {
1325        skip!("FinalTermSemanticPrompt {:?}", p)
1326      }
1327      OperatingSystemCommand::ChangeColorNumber(_) => {
1328        skip!("ChangeColorNumber")
1329      }
1330      OperatingSystemCommand::ChangeDynamicColors(first_color, colors) => {
1331        skip!("ChangeDynamicColors {:?} {:?}", first_color, colors)
1332      }
1333      OperatingSystemCommand::ResetDynamicColor(_) => {
1334        skip!("ResetDynamicColor")
1335      }
1336      OperatingSystemCommand::CurrentWorkingDirectory(_) => {
1337        skip!("CurrentWorkingDirectory")
1338      }
1339      OperatingSystemCommand::ResetColors(_) => skip!("ResetColors"),
1340      OperatingSystemCommand::RxvtExtension(_) => skip!("RxvtExtension"),
1341      OperatingSystemCommand::ConEmuProgress(_progress) => {
1342        skip!("ConEmuProgress")
1343      }
1344      OperatingSystemCommand::Unspecified(data) => {
1345        let strings: Vec<_> = data
1346          .into_iter()
1347          .map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
1348          .collect();
1349        skip!("OSC: Unspecified {:?}", strings);
1350      }
1351    }
1352  }
1353
1354  fn handle_csi(&mut self, csi: CSI) {
1355    match csi {
1356      CSI::Sgr(sgr) => match sgr {
1357        Sgr::Reset => self.attrs = Attrs::default(),
1358        Sgr::Intensity(level) => match level {
1359          termwiz::cell::Intensity::Normal => self.attrs.set_bold(false),
1360          termwiz::cell::Intensity::Bold => self.attrs.set_bold(true),
1361          termwiz::cell::Intensity::Half => self.attrs.set_bold(true),
1362        },
1363        Sgr::Underline(mode) => match mode {
1364          termwiz::cell::Underline::None => self.attrs.set_underline(false),
1365          termwiz::cell::Underline::Single
1366          | termwiz::cell::Underline::Double
1367          | termwiz::cell::Underline::Curly
1368          | termwiz::cell::Underline::Dotted
1369          | termwiz::cell::Underline::Dashed => self.attrs.set_underline(true),
1370        },
1371        Sgr::UnderlineColor(_) => skip!("UnderlineColor"),
1372        Sgr::Blink(_) => skip!("Blink"),
1373        Sgr::Italic(mode) => self.attrs.set_italic(mode),
1374        Sgr::Inverse(mode) => self.attrs.set_inverse(mode),
1375        Sgr::Invisible(_) => skip!("Invisible"),
1376        Sgr::StrikeThrough(_) => skip!("StrikeThrough"),
1377        Sgr::Font(_) => skip!("Font"),
1378        Sgr::Foreground(color) => self.attrs.fgcolor = color.into(),
1379        Sgr::Background(color) => self.attrs.bgcolor = color.into(),
1380        Sgr::Overline(_) => skip!("Overline"),
1381        Sgr::VerticalAlign(_) => skip!("VerticalAlign"),
1382      },
1383      CSI::Cursor(cursor) => match cursor {
1384        Cursor::BackwardTabulation(_) => skip!("BackwardTabulation"),
1385        Cursor::TabulationClear(_) => skip!("TabulationClear"),
1386        Cursor::CharacterAbsolute(pos) => {
1387          self.grid_mut().col_set(pos.as_zero_based() as u16)
1388        }
1389        Cursor::CharacterPositionAbsolute(_) => {
1390          skip!("CharacterPositionAbsolute")
1391        }
1392        Cursor::CharacterPositionBackward(_) => {
1393          skip!("CharacterPositionBackward")
1394        }
1395        Cursor::CharacterPositionForward(_) => {
1396          skip!("CharacterPositionForward")
1397        }
1398        Cursor::CharacterAndLinePosition { line, col } => {
1399          self.grid_mut().set_pos(crate::grid::Pos {
1400            row: line.as_zero_based() as u16,
1401            col: col.as_zero_based() as u16,
1402          });
1403        }
1404        Cursor::LinePositionAbsolute(row) => {
1405          self.grid_mut().row_set((row - 1) as u16)
1406        }
1407        Cursor::LinePositionBackward(_) => skip!("LinePositionBackward"),
1408        Cursor::LinePositionForward(_) => skip!("LinePositionForward"),
1409        Cursor::ForwardTabulation(_) => skip!("ForwardTabulation"),
1410        Cursor::NextLine(_) => skip!("NextLine"),
1411        Cursor::PrecedingLine(_) => skip!("PrecedingLine"),
1412        Cursor::ActivePositionReport { line: _, col: _ } => {
1413          skip!("ActivePositionReport")
1414        }
1415        Cursor::RequestActivePositionReport => {
1416          let pos = self.grid().pos();
1417          let es = CSI::Cursor(Cursor::ActivePositionReport {
1418            line: OneBased::from_zero_based(pos.row.into()),
1419            col: OneBased::from_zero_based(pos.col.into()),
1420          });
1421          self.reply_sender.reply(es.to_compact_string());
1422        }
1423        Cursor::SaveCursor => skip!("SaveCursor"),
1424        Cursor::RestoreCursor => skip!("RestoreCursor"),
1425        Cursor::TabulationControl(_) => skip!("TabulationControl"),
1426        Cursor::Left(count) => self.grid_mut().col_dec(count as u16),
1427        Cursor::Down(count) => self.grid_mut().row_inc_clamp(count as u16),
1428        Cursor::Right(count) => self.grid_mut().col_inc_clamp(count as u16),
1429        Cursor::Position { line, col } => {
1430          self.grid_mut().set_pos(crate::grid::Pos {
1431            row: line.as_zero_based() as u16,
1432            col: col.as_zero_based() as u16,
1433          })
1434        }
1435        Cursor::Up(count) => self.grid_mut().row_dec_clamp(count as u16),
1436        Cursor::LineTabulation(_) => skip!("LineTabulation"),
1437        Cursor::SetTopAndBottomMargins { top, bottom } => {
1438          self.grid_mut().set_scroll_region(
1439            top.as_zero_based() as u16,
1440            bottom.as_zero_based() as u16,
1441          )
1442        }
1443        Cursor::SetLeftAndRightMargins { left: _, right: _ } => {
1444          skip!("SetLeftAndRightMargins")
1445        }
1446        Cursor::CursorStyle(style) => {
1447          self.cursor_style = style;
1448        }
1449      },
1450      CSI::Edit(edit) => match edit {
1451        Edit::DeleteCharacter(count) => {
1452          self.grid_mut().delete_cells(count as u16)
1453        }
1454        Edit::DeleteLine(count) => self.grid_mut().delete_lines(count as u16),
1455        Edit::EraseCharacter(count) => {
1456          let attrs = self.attrs;
1457          self.grid_mut().erase_cells(count as u16, attrs);
1458        }
1459        Edit::EraseInLine(mode) => {
1460          let attrs = self.attrs;
1461          match mode {
1462            EraseInLine::EraseToEndOfLine => {
1463              self.grid_mut().erase_row_forward(attrs)
1464            }
1465            EraseInLine::EraseToStartOfLine => {
1466              self.grid_mut().erase_row_backward(attrs)
1467            }
1468            EraseInLine::EraseLine => self.grid_mut().erase_row(attrs),
1469          }
1470        }
1471        Edit::InsertCharacter(count) => {
1472          self.ich(u16::try_from(count).unwrap_or_default())
1473        }
1474        Edit::InsertLine(count) => self.grid_mut().insert_lines(count as u16),
1475        Edit::ScrollDown(count) => self.grid_mut().scroll_down(count as u16),
1476        Edit::ScrollUp(count) => self.grid_mut().scroll_up(count as u16),
1477        Edit::EraseInDisplay(mode) => {
1478          let attrs = self.attrs;
1479          match mode {
1480            EraseInDisplay::EraseToEndOfDisplay => {
1481              self.grid_mut().erase_all_forward(attrs)
1482            }
1483            EraseInDisplay::EraseToStartOfDisplay => {
1484              self.grid_mut().erase_all_backward(attrs)
1485            }
1486            EraseInDisplay::EraseDisplay => self.grid_mut().erase_all(attrs),
1487            EraseInDisplay::EraseScrollback => skip!("EraseScrollback"),
1488          }
1489        }
1490        Edit::Repeat(_) => skip!("Repeat"),
1491      },
1492      CSI::Mode(mode) => match mode {
1493        termwiz::escape::csi::Mode::SetDecPrivateMode(pmode) => match pmode {
1494          DecPrivateMode::Code(code) => match code {
1495            DecPrivateModeCode::ApplicationCursorKeys => {
1496              self.set_mode(MODE_APPLICATION_CURSOR)
1497            }
1498            DecPrivateModeCode::DecAnsiMode => skip!("DecAnsiMode"),
1499            DecPrivateModeCode::Select132Columns => {
1500              skip!("Select132Columns")
1501            }
1502            DecPrivateModeCode::SmoothScroll => skip!("SmoothScroll"),
1503            DecPrivateModeCode::ReverseVideo => skip!("ReverseVideo"),
1504            DecPrivateModeCode::OriginMode => {
1505              self.grid_mut().set_origin_mode(true)
1506            }
1507            DecPrivateModeCode::AutoWrap => skip!("AutoWrap"),
1508            DecPrivateModeCode::AutoRepeat => skip!("AutoRepeat"),
1509            DecPrivateModeCode::StartBlinkingCursor => {
1510              skip!("StartBlinkingCursor")
1511            }
1512            DecPrivateModeCode::ShowCursor => self.clear_mode(MODE_HIDE_CURSOR),
1513            DecPrivateModeCode::ReverseWraparound => {
1514              skip!("ReverseWraparound")
1515            }
1516            DecPrivateModeCode::LeftRightMarginMode => {
1517              skip!("LeftRightMarginMode")
1518            }
1519            DecPrivateModeCode::SixelDisplayMode => {
1520              skip!("SixelDisplayMode")
1521            }
1522            DecPrivateModeCode::MouseTracking => {
1523              self.set_mouse_mode(MouseProtocolMode::PressRelease)
1524            }
1525            DecPrivateModeCode::HighlightMouseTracking => {
1526              skip!("HighlightMouseTracking")
1527            }
1528            DecPrivateModeCode::ButtonEventMouse => {
1529              self.set_mouse_mode(MouseProtocolMode::ButtonMotion)
1530            }
1531            DecPrivateModeCode::AnyEventMouse => {
1532              self.set_mouse_mode(MouseProtocolMode::AnyMotion)
1533            }
1534            DecPrivateModeCode::FocusTracking => skip!("FocusTracking"),
1535            DecPrivateModeCode::Utf8Mouse => {
1536              self.set_mouse_encoding(MouseProtocolEncoding::Utf8)
1537            }
1538            DecPrivateModeCode::SGRMouse => {
1539              self.set_mouse_encoding(MouseProtocolEncoding::Sgr)
1540            }
1541            DecPrivateModeCode::SGRPixelsMouse => skip!("SGRPixelsMouse"),
1542            DecPrivateModeCode::XTermMetaSendsEscape => {
1543              skip!("XTermMetaSendsEscape")
1544            }
1545            DecPrivateModeCode::XTermAltSendsEscape => {
1546              skip!("XTermAltSendsEscape")
1547            }
1548            DecPrivateModeCode::SaveCursor => skip!("SaveCursor"),
1549            DecPrivateModeCode::ClearAndEnableAlternateScreen => {
1550              self.decsc();
1551              self.alternate_grid.clear();
1552              self.enter_alternate_grid();
1553            }
1554            DecPrivateModeCode::EnableAlternateScreen => {
1555              self.enter_alternate_grid();
1556            }
1557            DecPrivateModeCode::OptEnableAlternateScreen => {
1558              skip!("OptEnableAlternateScreen")
1559            }
1560            DecPrivateModeCode::BracketedPaste => {
1561              self.set_mode(MODE_BRACKETED_PASTE);
1562            }
1563            DecPrivateModeCode::GraphemeClustering => {
1564              skip!("GraphemeClustering");
1565            }
1566            DecPrivateModeCode::UsePrivateColorRegistersForEachGraphic => {
1567              skip!("UsePrivateColorRegistersForEachGraphic")
1568            }
1569            DecPrivateModeCode::SynchronizedOutput => {
1570              skip!("SynchronizedOutput")
1571            }
1572            DecPrivateModeCode::MinTTYApplicationEscapeKeyMode => {
1573              skip!("MinTTYApplicationEscapeKeyMode")
1574            }
1575            DecPrivateModeCode::SixelScrollsRight => {
1576              skip!("SixelScrollsRight")
1577            }
1578            DecPrivateModeCode::Win32InputMode => skip!("Win32InputMode"),
1579          },
1580          DecPrivateMode::Unspecified(9) => {
1581            self.set_mouse_mode(MouseProtocolMode::Press)
1582          }
1583          DecPrivateMode::Unspecified(m) => {
1584            skip!("SetDecPrivateMode:Unspecified:{}", m)
1585          }
1586        },
1587        termwiz::escape::csi::Mode::ResetDecPrivateMode(pmode) => match pmode {
1588          DecPrivateMode::Code(code) => match code {
1589            DecPrivateModeCode::ApplicationCursorKeys => {
1590              self.clear_mode(MODE_APPLICATION_CURSOR)
1591            }
1592            DecPrivateModeCode::DecAnsiMode => skip!("DecAnsiMode"),
1593            DecPrivateModeCode::Select132Columns => {
1594              skip!("Select132Columns")
1595            }
1596            DecPrivateModeCode::SmoothScroll => skip!("SmoothScroll"),
1597            DecPrivateModeCode::ReverseVideo => skip!("ReverseVideo"),
1598            DecPrivateModeCode::OriginMode => {
1599              self.grid_mut().set_origin_mode(false)
1600            }
1601            DecPrivateModeCode::AutoWrap => skip!("AutoWrap"),
1602            DecPrivateModeCode::AutoRepeat => skip!("AutoRepeat"),
1603            DecPrivateModeCode::StartBlinkingCursor => {
1604              skip!("StartBlinkingCursor")
1605            }
1606            DecPrivateModeCode::ShowCursor => self.set_mode(MODE_HIDE_CURSOR),
1607            DecPrivateModeCode::ReverseWraparound => {
1608              skip!("ReverseWraparound")
1609            }
1610            DecPrivateModeCode::LeftRightMarginMode => {
1611              skip!("LeftRightMarginMode")
1612            }
1613            DecPrivateModeCode::SixelDisplayMode => {
1614              skip!("SixelDisplayMode")
1615            }
1616            DecPrivateModeCode::MouseTracking => {
1617              self.clear_mouse_mode(MouseProtocolMode::PressRelease)
1618            }
1619            DecPrivateModeCode::HighlightMouseTracking => {
1620              skip!("HighlightMouseTracking")
1621            }
1622            DecPrivateModeCode::ButtonEventMouse => {
1623              self.clear_mouse_mode(MouseProtocolMode::ButtonMotion)
1624            }
1625            DecPrivateModeCode::AnyEventMouse => {
1626              self.clear_mouse_mode(MouseProtocolMode::AnyMotion)
1627            }
1628            DecPrivateModeCode::FocusTracking => skip!("FocusTracking"),
1629            DecPrivateModeCode::Utf8Mouse => {
1630              self.clear_mouse_encoding(MouseProtocolEncoding::Utf8)
1631            }
1632            DecPrivateModeCode::SGRMouse => {
1633              self.clear_mouse_encoding(MouseProtocolEncoding::Sgr)
1634            }
1635            DecPrivateModeCode::SGRPixelsMouse => {
1636              skip!("SGRPixelsMouse")
1637            }
1638            DecPrivateModeCode::XTermMetaSendsEscape => {
1639              skip!("XTermMetaSendsEscape")
1640            }
1641            DecPrivateModeCode::XTermAltSendsEscape => {
1642              skip!("XTermAltSendsEscape")
1643            }
1644            DecPrivateModeCode::SaveCursor => skip!("SaveCursor"),
1645            DecPrivateModeCode::ClearAndEnableAlternateScreen => {
1646              self.exit_alternate_grid();
1647              self.decrc();
1648            }
1649            DecPrivateModeCode::EnableAlternateScreen => {
1650              self.exit_alternate_grid()
1651            }
1652            DecPrivateModeCode::OptEnableAlternateScreen => {
1653              skip!("OptEnableAlternateScreen")
1654            }
1655            DecPrivateModeCode::BracketedPaste => {
1656              self.clear_mode(MODE_BRACKETED_PASTE)
1657            }
1658            DecPrivateModeCode::GraphemeClustering => {
1659              skip!("GraphemeClustering");
1660            }
1661            DecPrivateModeCode::UsePrivateColorRegistersForEachGraphic => {
1662              skip!("UsePrivateColorRegistersForEachGraphic")
1663            }
1664            DecPrivateModeCode::SynchronizedOutput => {
1665              skip!("SynchronizedOutput")
1666            }
1667            DecPrivateModeCode::MinTTYApplicationEscapeKeyMode => {
1668              skip!("MinTTYApplicationEscapeKeyMode")
1669            }
1670            DecPrivateModeCode::SixelScrollsRight => {
1671              skip!("SixelScrollsRight")
1672            }
1673            DecPrivateModeCode::Win32InputMode => {
1674              skip!("Win32InputMode")
1675            }
1676          },
1677          DecPrivateMode::Unspecified(9) => {
1678            self.clear_mouse_mode(MouseProtocolMode::Press)
1679          }
1680          termwiz::escape::csi::DecPrivateMode::Unspecified(_) => {
1681            skip!("DecPrivateMode::Unspecified")
1682          }
1683        },
1684        termwiz::escape::csi::Mode::SaveDecPrivateMode(pmode) => match pmode {
1685          DecPrivateMode::Code(code) => match code {
1686            DecPrivateModeCode::ApplicationCursorKeys => {
1687              skip!("ApplicationCursorKeys")
1688            }
1689            DecPrivateModeCode::DecAnsiMode => skip!("DecAnsiMode"),
1690            DecPrivateModeCode::Select132Columns => {
1691              skip!("Select132Columns")
1692            }
1693            DecPrivateModeCode::SmoothScroll => skip!("SmoothScroll"),
1694            DecPrivateModeCode::ReverseVideo => skip!("ReverseVideo"),
1695            DecPrivateModeCode::OriginMode => skip!("OriginMode"),
1696            DecPrivateModeCode::AutoWrap => skip!("AutoWrap"),
1697            DecPrivateModeCode::AutoRepeat => skip!("AutoRepeat"),
1698            DecPrivateModeCode::StartBlinkingCursor => {
1699              skip!("StartBlinkingCursor")
1700            }
1701            DecPrivateModeCode::ShowCursor => skip!("ShowCursor"),
1702            DecPrivateModeCode::ReverseWraparound => {
1703              skip!("ReverseWraparound")
1704            }
1705            DecPrivateModeCode::LeftRightMarginMode => {
1706              skip!("LeftRightMarginMode")
1707            }
1708            DecPrivateModeCode::SixelDisplayMode => {
1709              skip!("SixelDisplayMode")
1710            }
1711            DecPrivateModeCode::MouseTracking => skip!("MouseTracking"),
1712            DecPrivateModeCode::HighlightMouseTracking => {
1713              skip!("HighlightMouseTracking")
1714            }
1715            DecPrivateModeCode::ButtonEventMouse => {
1716              skip!("ButtonEventMouse")
1717            }
1718            DecPrivateModeCode::AnyEventMouse => skip!("AnyEventMouse"),
1719            DecPrivateModeCode::FocusTracking => skip!("FocusTracking"),
1720            DecPrivateModeCode::Utf8Mouse => skip!("Utf8Mouse"),
1721            DecPrivateModeCode::SGRMouse => skip!("SGRMouse"),
1722            DecPrivateModeCode::SGRPixelsMouse => {
1723              skip!("SGRPixelsMouse")
1724            }
1725            DecPrivateModeCode::XTermMetaSendsEscape => {
1726              skip!("XTermMetaSendsEscape")
1727            }
1728            DecPrivateModeCode::XTermAltSendsEscape => {
1729              skip!("XTermAltSendsEscape")
1730            }
1731            DecPrivateModeCode::SaveCursor => skip!("SaveCursor"),
1732            DecPrivateModeCode::ClearAndEnableAlternateScreen => {
1733              skip!("ClearAndEnableAlternateScreen")
1734            }
1735            DecPrivateModeCode::EnableAlternateScreen => {
1736              skip!("EnableAlternateScreen")
1737            }
1738            DecPrivateModeCode::OptEnableAlternateScreen => {
1739              skip!("OptEnableAlternateScreen")
1740            }
1741            DecPrivateModeCode::BracketedPaste => {
1742              skip!("BracketedPaste")
1743            }
1744            DecPrivateModeCode::GraphemeClustering => {
1745              skip!("GraphemeClustering");
1746            }
1747            DecPrivateModeCode::UsePrivateColorRegistersForEachGraphic => {
1748              skip!("UsePrivateColorRegistersForEachGraphic")
1749            }
1750            DecPrivateModeCode::SynchronizedOutput => {
1751              skip!("SynchronizedOutput")
1752            }
1753            DecPrivateModeCode::MinTTYApplicationEscapeKeyMode => {
1754              skip!("MinTTYApplicationEscapeKeyMode")
1755            }
1756            DecPrivateModeCode::SixelScrollsRight => {
1757              skip!("SixelScrollsRight")
1758            }
1759            DecPrivateModeCode::Win32InputMode => {
1760              skip!("Win32InputMode")
1761            }
1762          },
1763          termwiz::escape::csi::DecPrivateMode::Unspecified(_) => todo!(),
1764        },
1765        termwiz::escape::csi::Mode::RestoreDecPrivateMode(_) => {
1766          skip!("RestoreDecPrivateMode")
1767        }
1768        termwiz::escape::csi::Mode::QueryDecPrivateMode(_) => {
1769          skip!("QueryDecPrivateMode")
1770        }
1771        termwiz::escape::csi::Mode::SetMode(mode) => match mode {
1772          TerminalMode::Code(code) => match code {
1773            TerminalModeCode::KeyboardAction => {
1774              skip!("TerminalModeCode::KeyboardAction")
1775            }
1776            TerminalModeCode::Insert => skip!("TerminalModeCode::Insert"),
1777            TerminalModeCode::BiDirectionalSupportMode => {
1778              skip!("TerminalModeCode::BiDirectionalSupportMode")
1779            }
1780            TerminalModeCode::SendReceive => {
1781              skip!("TerminalModeCode::SendReceive")
1782            }
1783            TerminalModeCode::AutomaticNewline => {
1784              skip!("TerminalModeCode::AutomaticNewline")
1785            }
1786            TerminalModeCode::ShowCursor => {
1787              skip!("TerminalModeCode::ShowCursor")
1788            }
1789          },
1790          TerminalMode::Unspecified(n) => {
1791            skip!("SetMode -> TerminalMode::Unspecified({})", n)
1792          }
1793        },
1794        termwiz::escape::csi::Mode::ResetMode(mode) => match mode {
1795          TerminalMode::Code(code) => match code {
1796            TerminalModeCode::KeyboardAction => {
1797              skip!("TerminalModeCode::KeyboardAction")
1798            }
1799            TerminalModeCode::Insert => self.insert = false,
1800            TerminalModeCode::BiDirectionalSupportMode => {
1801              skip!("TerminalModeCode::BiDirectionalSupportMode")
1802            }
1803            TerminalModeCode::SendReceive => {
1804              skip!("TerminalModeCode::SendReceive")
1805            }
1806            TerminalModeCode::AutomaticNewline => {
1807              skip!("TerminalModeCode::AutomaticNewline")
1808            }
1809            TerminalModeCode::ShowCursor => {
1810              skip!("TerminalModeCode::ShowCursor")
1811            }
1812          },
1813          TerminalMode::Unspecified(n) => {
1814            skip!("ResetMode -> TerminalMode::Unspecified({})", n)
1815          }
1816        },
1817        termwiz::escape::csi::Mode::QueryMode(_) => skip!("QueryMode"),
1818        termwiz::escape::csi::Mode::XtermKeyMode {
1819          resource: _,
1820          value: _,
1821        } => {
1822          skip!("XtermKeyMode")
1823        }
1824      },
1825      CSI::Device(device) => skip!("Device: {:?}", device),
1826      CSI::Mouse(mouse) => skip!("Mouse: {:?}", mouse),
1827      CSI::Window(win) => match *win {
1828        Window::DeIconify => skip!("DeIconify"),
1829        Window::Iconify => skip!("Iconify"),
1830        Window::MoveWindow { x: _, y: _ } => skip!("MoveWindow"),
1831        Window::ResizeWindowPixels {
1832          width: _,
1833          height: _,
1834        } => {
1835          skip!("ResizeWindowPixels")
1836        }
1837        Window::RaiseWindow => skip!("RaiseWindow"),
1838        Window::LowerWindow => skip!("LowerWindow"),
1839        Window::RefreshWindow => skip!("RefreshWindow"),
1840        Window::ResizeWindowCells {
1841          width: _,
1842          height: _,
1843        } => {
1844          skip!("ResizeWindowCells")
1845        }
1846        Window::RestoreMaximizedWindow => skip!("RestoreMaximizedWindow"),
1847        Window::MaximizeWindow => skip!("MaximizeWindow"),
1848        Window::MaximizeWindowVertically => {
1849          skip!("MaximizeWindowVertically")
1850        }
1851        Window::MaximizeWindowHorizontally => {
1852          skip!("MaximizeWindowHorizontally")
1853        }
1854        Window::UndoFullScreenMode => skip!("UndoFullScreenMode"),
1855        Window::ChangeToFullScreenMode => skip!("ChangeToFullScreenMode"),
1856        Window::ToggleFullScreen => skip!("ToggleFullScreen"),
1857        Window::ReportWindowState => skip!("ReportWindowState"),
1858        Window::ReportWindowPosition => skip!("ReportWindowPosition"),
1859        Window::ReportTextAreaPosition => skip!("ReportTextAreaPosition"),
1860        Window::ReportTextAreaSizePixels => {
1861          skip!("ReportTextAreaSizePixels")
1862        }
1863        Window::ReportWindowSizePixels => skip!("ReportWindowSizePixels"),
1864        Window::ReportScreenSizePixels => skip!("ReportScreenSizePixels"),
1865        Window::ReportCellSizePixels => skip!("ReportCellSizePixels"),
1866        Window::ReportCellSizePixelsResponse {
1867          width: _,
1868          height: _,
1869        } => {
1870          skip!("ReportCellSizePixelsResponse")
1871        }
1872        Window::ReportTextAreaSizeCells => {
1873          skip!("ReportTextAreaSizeCells")
1874        }
1875        Window::ReportScreenSizeCells => skip!("ReportScreenSizeCells"),
1876        Window::ReportIconLabel => skip!("ReportIconLabel"),
1877        Window::ReportWindowTitle => skip!("ReportWindowTitle"),
1878        Window::PushIconAndWindowTitle => skip!("PushIconAndWindowTitle"),
1879        Window::PushIconTitle => skip!("PushIconTitle"),
1880        Window::PushWindowTitle => skip!("PushWindowTitle"),
1881        Window::PopIconAndWindowTitle => skip!("PopIconAndWindowTitle"),
1882        Window::PopIconTitle => skip!("PopIconTitle"),
1883        Window::PopWindowTitle => skip!("PopWindowTitle"),
1884        Window::ChecksumRectangularArea {
1885          request_id: _,
1886          page_number: _,
1887          top: _,
1888          left: _,
1889          bottom: _,
1890          right: _,
1891        } => skip!("ChecksumRectangularArea"),
1892      },
1893      CSI::Keyboard(kb) => match kb {
1894        termwiz::escape::csi::Keyboard::SetKittyState { flags: _, mode: _ } => {
1895          skip!("SetKittyState")
1896        }
1897        termwiz::escape::csi::Keyboard::PushKittyState {
1898          flags: _,
1899          mode: _,
1900        } => {
1901          skip!("PushKittyState")
1902        }
1903        termwiz::escape::csi::Keyboard::PopKittyState(_) => {
1904          skip!("PopKittyState")
1905        }
1906        termwiz::escape::csi::Keyboard::QueryKittySupport => {
1907          skip!("QueryKittySupport")
1908        }
1909        termwiz::escape::csi::Keyboard::ReportKittyState(_) => {
1910          skip!("ReportKittyState")
1911        }
1912      },
1913      CSI::SelectCharacterPath(_, _) => skip!("SelectCharacterPath"),
1914      CSI::Unspecified(n) => {
1915        let handled = match (n.control, n.params.as_slice()) {
1916          ('J', [CsiParam::P(b'?')]) => {
1917            self.decsed(0);
1918            true
1919          }
1920          ('J', [CsiParam::P(b'?'), CsiParam::Integer(mode)]) => {
1921            self.decsed(u16::try_from(*mode).unwrap_or_default());
1922            true
1923          }
1924          ('K', [CsiParam::P(b'?')]) => {
1925            self.decsel(0);
1926            true
1927          }
1928          ('K', [CsiParam::P(b'?'), CsiParam::Integer(mode)]) => {
1929            self.decsel(u16::try_from(*mode).unwrap_or_default());
1930            true
1931          }
1932          _ => false,
1933        };
1934        if !handled {
1935          skip!("unspecified {}", n);
1936        }
1937      }
1938    }
1939  }
1940
1941  fn handle_esc(&mut self, esc: Esc) {
1942    match esc {
1943      Esc::Code(code) => match code {
1944        EscCode::FullReset => self.ris(),
1945        EscCode::Index => skip!("Index"),
1946        EscCode::NextLine => skip!("NextLine"),
1947        EscCode::CursorPositionLowerLeft => {
1948          skip!("CursorPositionLowerLeft")
1949        }
1950        EscCode::HorizontalTabSet => skip!("HorizontalTabSet"),
1951        EscCode::ReverseIndex => self.ri(),
1952        EscCode::SingleShiftG2 => skip!("SingleShiftG2"),
1953        EscCode::SingleShiftG3 => skip!("SingleShiftG3"),
1954        EscCode::StartOfGuardedArea => skip!("StartOfGuardedArea"),
1955        EscCode::EndOfGuardedArea => skip!("EndOfGuardedArea"),
1956        EscCode::StartOfString => skip!("StartOfString"),
1957        EscCode::ReturnTerminalId => skip!("ReturnTerminalId"),
1958        EscCode::StringTerminator => skip!("StringTerminator"),
1959        EscCode::PrivacyMessage => skip!("PrivacyMessage"),
1960        EscCode::ApplicationProgramCommand => {
1961          skip!("ApplicationProgramCommand")
1962        }
1963        EscCode::TmuxTitle => skip!("TmuxTitle"),
1964        EscCode::DecBackIndex => skip!("DecBackIndex"),
1965        EscCode::DecSaveCursorPosition => self.save_cursor(),
1966        EscCode::DecRestoreCursorPosition => self.restore_cursor(),
1967        EscCode::DecApplicationKeyPad => self.deckpam(),
1968        EscCode::DecNormalKeyPad => self.clear_mode(MODE_APPLICATION_KEYPAD),
1969        EscCode::DecLineDrawingG0 => self.g0 = CharSet::DecLineDrawing,
1970        EscCode::UkCharacterSetG0 => self.g0 = CharSet::Uk,
1971        EscCode::AsciiCharacterSetG0 => self.g0 = CharSet::Ascii,
1972        EscCode::DecLineDrawingG1 => self.g1 = CharSet::DecLineDrawing,
1973        EscCode::UkCharacterSetG1 => self.g1 = CharSet::Uk,
1974        EscCode::AsciiCharacterSetG1 => self.g1 = CharSet::Ascii,
1975        EscCode::DecScreenAlignmentDisplay => {
1976          skip!("DecScreenAlignmentDisplay")
1977        }
1978        EscCode::DecDoubleHeightTopHalfLine => {
1979          skip!("DecDoubleHeightTopHalfLine")
1980        }
1981        EscCode::DecDoubleHeightBottomHalfLine => {
1982          skip!("DecDoubleHeightBottomHalfLine")
1983        }
1984        EscCode::DecSingleWidthLine => skip!("DecSingleWidthLine"),
1985        EscCode::DecDoubleWidthLine => skip!("DecDoubleWidthLine"),
1986        EscCode::ApplicationModeArrowUpPress => {
1987          skip!("ApplicationModeArrowUpPress")
1988        }
1989        EscCode::ApplicationModeArrowDownPress => {
1990          skip!("ApplicationModeArrowDownPress")
1991        }
1992        EscCode::ApplicationModeArrowRightPress => {
1993          skip!("ApplicationModeArrowRightPress")
1994        }
1995        EscCode::ApplicationModeArrowLeftPress => {
1996          skip!("ApplicationModeArrowLeftPress")
1997        }
1998        EscCode::ApplicationModeHomePress => {
1999          skip!("ApplicationModeHomePress")
2000        }
2001        EscCode::ApplicationModeEndPress => {
2002          skip!("ApplicationModeEndPress")
2003        }
2004        EscCode::F1Press => skip!("F1Press"),
2005        EscCode::F2Press => skip!("F2Press"),
2006        EscCode::F3Press => skip!("F3Press"),
2007        EscCode::F4Press => skip!("F4Press"),
2008      },
2009      Esc::Unspecified {
2010        intermediate,
2011        control,
2012      } => match (intermediate, control) {
2013        (None, b'g') => self.vb(),
2014        _ => {
2015          skip!("Unspecified esc: {:?} {}", intermediate, control);
2016        }
2017      },
2018    }
2019  }
2020
2021  fn handle_xt_get_tcap(&mut self, _names: Vec<String>) {
2022    skip!("XtGetTcap");
2023  }
2024}