vt100/screen.rs
1use crate::term::BufWrite as _;
2use unicode_width::UnicodeWidthChar as _;
3
4const MODE_APPLICATION_KEYPAD: u8 = 0b0000_0001;
5const MODE_APPLICATION_CURSOR: u8 = 0b0000_0010;
6const MODE_HIDE_CURSOR: u8 = 0b0000_0100;
7const MODE_ALTERNATE_SCREEN: u8 = 0b0000_1000;
8const MODE_BRACKETED_PASTE: u8 = 0b0001_0000;
9
10/// The xterm mouse handling mode currently in use.
11#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
12pub enum MouseProtocolMode {
13 /// Mouse handling is disabled.
14 #[default]
15 None,
16
17 /// Mouse button events should be reported on button press. Also known as
18 /// X10 mouse mode.
19 Press,
20
21 /// Mouse button events should be reported on button press and release.
22 /// Also known as VT200 mouse mode.
23 PressRelease,
24
25 // Highlight,
26 /// Mouse button events should be reported on button press and release, as
27 /// well as when the mouse moves between cells while a button is held
28 /// down.
29 ButtonMotion,
30
31 /// Mouse button events should be reported on button press and release,
32 /// and mouse motion events should be reported when the mouse moves
33 /// between cells regardless of whether a button is held down or not.
34 AnyMotion,
35 // DecLocator,
36}
37
38/// The encoding to use for the enabled [`MouseProtocolMode`].
39#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
40pub enum MouseProtocolEncoding {
41 /// Default single-printable-byte encoding.
42 #[default]
43 Default,
44
45 /// UTF-8-based encoding.
46 Utf8,
47
48 /// SGR-like encoding.
49 Sgr,
50 // Urxvt,
51}
52
53/// Represents the overall terminal state.
54#[derive(Clone, Debug)]
55pub struct Screen {
56 grid: crate::grid::Grid,
57 alternate_grid: crate::grid::Grid,
58
59 attrs: crate::attrs::Attrs,
60 saved_attrs: crate::attrs::Attrs,
61
62 modes: u8,
63 mouse_protocol_mode: MouseProtocolMode,
64 mouse_protocol_encoding: MouseProtocolEncoding,
65}
66
67impl Screen {
68 pub(crate) fn new(
69 size: crate::grid::Size,
70 scrollback_len: usize,
71 ) -> Self {
72 let mut grid = crate::grid::Grid::new(size, scrollback_len);
73 grid.allocate_rows();
74 Self {
75 grid,
76 alternate_grid: crate::grid::Grid::new(size, 0),
77
78 attrs: crate::attrs::Attrs::default(),
79 saved_attrs: crate::attrs::Attrs::default(),
80
81 modes: 0,
82 mouse_protocol_mode: MouseProtocolMode::default(),
83 mouse_protocol_encoding: MouseProtocolEncoding::default(),
84 }
85 }
86
87 /// Resizes the terminal.
88 pub fn set_size(&mut self, rows: u16, cols: u16) {
89 self.grid.set_size(crate::grid::Size { rows, cols });
90 self.alternate_grid
91 .set_size(crate::grid::Size { rows, cols });
92 }
93
94 /// Returns the current size of the terminal.
95 ///
96 /// The return value will be (rows, cols).
97 #[must_use]
98 pub fn size(&self) -> (u16, u16) {
99 let size = self.grid().size();
100 (size.rows, size.cols)
101 }
102
103 /// Returns the number of lines in the scrollback.
104 #[must_use]
105 pub fn scrollback_len(&self) -> usize {
106 self.grid().scrollback_len()
107 }
108
109 /// Scrolls to the given position in the scrollback.
110 ///
111 /// This position indicates the offset from the top of the screen, and
112 /// should be `0` to put the normal screen in view.
113 ///
114 /// This affects the return values of methods called on the screen: for
115 /// instance, `screen.cell(0, 0)` will return the top left corner of the
116 /// screen after taking the scrollback offset into account.
117 ///
118 /// The value given will be clamped to the actual size of the scrollback.
119 pub fn set_scrollback(&mut self, rows: usize) {
120 self.grid_mut().set_scrollback(rows);
121 }
122
123 /// Returns the current position in the scrollback.
124 ///
125 /// This position indicates the offset from the top of the screen, and is
126 /// `0` when the normal screen is in view.
127 #[must_use]
128 pub fn scrollback(&self) -> usize {
129 self.grid().scrollback()
130 }
131
132 /// Returns the text contents of the terminal.
133 ///
134 /// This will not include any formatting information, and will be in plain
135 /// text format.
136 #[must_use]
137 pub fn contents(&self) -> String {
138 let mut contents = String::new();
139 self.write_contents(&mut contents);
140 contents
141 }
142
143 fn write_contents(&self, contents: &mut String) {
144 self.grid().write_contents(contents);
145 }
146
147 /// Returns the text contents of the terminal by row, restricted to the
148 /// given subset of columns.
149 ///
150 /// This will not include any formatting information, and will be in plain
151 /// text format.
152 ///
153 /// Newlines will not be included.
154 pub fn rows(
155 &self,
156 start: u16,
157 width: u16,
158 ) -> impl Iterator<Item = String> + '_ {
159 self.grid().visible_rows().map(move |row| {
160 let mut contents = String::new();
161 row.write_contents(&mut contents, start, width, false);
162 contents
163 })
164 }
165
166 /// Returns the text contents of the terminal logically between two cells.
167 /// This will include the remainder of the starting row after `start_col`,
168 /// followed by the entire contents of the rows between `start_row` and
169 /// `end_row`, followed by the beginning of the `end_row` up until
170 /// `end_col`. This is useful for things like determining the contents of
171 /// a clipboard selection.
172 #[must_use]
173 pub fn contents_between(
174 &self,
175 start_row: u16,
176 start_col: u16,
177 end_row: u16,
178 end_col: u16,
179 ) -> String {
180 match start_row.cmp(&end_row) {
181 std::cmp::Ordering::Less => {
182 let (_, cols) = self.size();
183 let mut contents = String::new();
184 for (i, row) in self
185 .grid()
186 .visible_rows()
187 .enumerate()
188 .skip(usize::from(start_row))
189 .take(usize::from(end_row) - usize::from(start_row) + 1)
190 {
191 if i == usize::from(start_row) {
192 row.write_contents(
193 &mut contents,
194 start_col,
195 cols - start_col,
196 false,
197 );
198 if !row.wrapped() {
199 contents.push('\n');
200 }
201 } else if i == usize::from(end_row) {
202 row.write_contents(&mut contents, 0, end_col, false);
203 } else {
204 row.write_contents(&mut contents, 0, cols, false);
205 if !row.wrapped() {
206 contents.push('\n');
207 }
208 }
209 }
210 contents
211 }
212 std::cmp::Ordering::Equal => {
213 if start_col < end_col {
214 self.rows(start_col, end_col - start_col)
215 .nth(usize::from(start_row))
216 .unwrap_or_default()
217 } else {
218 String::new()
219 }
220 }
221 std::cmp::Ordering::Greater => String::new(),
222 }
223 }
224
225 /// Return escape codes sufficient to reproduce the entire contents of the
226 /// current terminal state. This is a convenience wrapper around
227 /// [`contents_formatted`](Self::contents_formatted) and
228 /// [`input_mode_formatted`](Self::input_mode_formatted).
229 #[must_use]
230 pub fn state_formatted(&self) -> Vec<u8> {
231 let mut contents = vec![];
232 self.write_contents_formatted(&mut contents);
233 self.write_input_mode_formatted(&mut contents);
234 contents
235 }
236
237 /// Return escape codes sufficient to turn the terminal state of the
238 /// screen `prev` into the current terminal state. This is a convenience
239 /// wrapper around [`contents_diff`](Self::contents_diff) and
240 /// [`input_mode_diff`](Self::input_mode_diff).
241 #[must_use]
242 pub fn state_diff(&self, prev: &Self) -> Vec<u8> {
243 let mut contents = vec![];
244 self.write_contents_diff(&mut contents, prev);
245 self.write_input_mode_diff(&mut contents, prev);
246 contents
247 }
248
249 /// Returns the formatted visible contents of the terminal.
250 ///
251 /// Formatting information will be included inline as terminal escape
252 /// codes. The result will be suitable for feeding directly to a raw
253 /// terminal parser, and will result in the same visual output.
254 #[must_use]
255 pub fn contents_formatted(&self) -> Vec<u8> {
256 let mut contents = vec![];
257 self.write_contents_formatted(&mut contents);
258 contents
259 }
260
261 fn write_contents_formatted(&self, contents: &mut Vec<u8>) {
262 crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
263 let prev_attrs = self.grid().write_contents_formatted(contents);
264 self.attrs.write_escape_code_diff(contents, &prev_attrs);
265 }
266
267 /// Returns the formatted visible contents of the terminal by row,
268 /// restricted to the given subset of columns.
269 ///
270 /// Formatting information will be included inline as terminal escape
271 /// codes. The result will be suitable for feeding directly to a raw
272 /// terminal parser, and will result in the same visual output.
273 ///
274 /// You are responsible for positioning the cursor before printing each
275 /// row, and the final cursor position after displaying each row is
276 /// unspecified.
277 // the unwraps in this method shouldn't be reachable
278 #[allow(clippy::missing_panics_doc)]
279 pub fn rows_formatted(
280 &self,
281 start: u16,
282 width: u16,
283 ) -> impl Iterator<Item = Vec<u8>> + '_ {
284 let mut wrapping = false;
285 self.grid().visible_rows().enumerate().map(move |(i, row)| {
286 // number of rows in a grid is stored in a u16 (see Size), so
287 // visible_rows can never return enough rows to overflow here
288 let i = i.try_into().unwrap();
289 let mut contents = vec![];
290 row.write_contents_formatted(
291 &mut contents,
292 start,
293 width,
294 i,
295 wrapping,
296 None,
297 None,
298 );
299 if start == 0 && width == self.grid.size().cols {
300 wrapping = row.wrapped();
301 }
302 contents
303 })
304 }
305
306 /// Returns a terminal byte stream sufficient to turn the visible contents
307 /// of the screen described by `prev` into the visible contents of the
308 /// screen described by `self`.
309 ///
310 /// The result of rendering `prev.contents_formatted()` followed by
311 /// `self.contents_diff(prev)` should be equivalent to the result of
312 /// rendering `self.contents_formatted()`. This is primarily useful when
313 /// you already have a terminal parser whose state is described by `prev`,
314 /// since the diff will likely require less memory and cause less
315 /// flickering than redrawing the entire screen contents.
316 #[must_use]
317 pub fn contents_diff(&self, prev: &Self) -> Vec<u8> {
318 let mut contents = vec![];
319 self.write_contents_diff(&mut contents, prev);
320 contents
321 }
322
323 fn write_contents_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
324 if self.hide_cursor() != prev.hide_cursor() {
325 crate::term::HideCursor::new(self.hide_cursor())
326 .write_buf(contents);
327 }
328 let prev_attrs = self.grid().write_contents_diff(
329 contents,
330 prev.grid(),
331 prev.attrs,
332 );
333 self.attrs.write_escape_code_diff(contents, &prev_attrs);
334 }
335
336 /// Returns a sequence of terminal byte streams sufficient to turn the
337 /// visible contents of the subset of each row from `prev` (as described
338 /// by `start` and `width`) into the visible contents of the corresponding
339 /// row subset in `self`.
340 ///
341 /// You are responsible for positioning the cursor before printing each
342 /// row, and the final cursor position after displaying each row is
343 /// unspecified.
344 // the unwraps in this method shouldn't be reachable
345 #[allow(clippy::missing_panics_doc)]
346 pub fn rows_diff<'a>(
347 &'a self,
348 prev: &'a Self,
349 start: u16,
350 width: u16,
351 ) -> impl Iterator<Item = Vec<u8>> + 'a {
352 self.grid()
353 .visible_rows()
354 .zip(prev.grid().visible_rows())
355 .enumerate()
356 .map(move |(i, (row, prev_row))| {
357 // number of rows in a grid is stored in a u16 (see Size), so
358 // visible_rows can never return enough rows to overflow here
359 let i = i.try_into().unwrap();
360 let mut contents = vec![];
361 row.write_contents_diff(
362 &mut contents,
363 prev_row,
364 start,
365 width,
366 i,
367 false,
368 false,
369 crate::grid::Pos { row: i, col: start },
370 crate::attrs::Attrs::default(),
371 );
372 contents
373 })
374 }
375
376 /// Returns terminal escape sequences sufficient to set the current
377 /// terminal's input modes.
378 ///
379 /// Supported modes are:
380 /// * application keypad
381 /// * application cursor
382 /// * bracketed paste
383 /// * xterm mouse support
384 #[must_use]
385 pub fn input_mode_formatted(&self) -> Vec<u8> {
386 let mut contents = vec![];
387 self.write_input_mode_formatted(&mut contents);
388 contents
389 }
390
391 fn write_input_mode_formatted(&self, contents: &mut Vec<u8>) {
392 crate::term::ApplicationKeypad::new(
393 self.mode(MODE_APPLICATION_KEYPAD),
394 )
395 .write_buf(contents);
396 crate::term::ApplicationCursor::new(
397 self.mode(MODE_APPLICATION_CURSOR),
398 )
399 .write_buf(contents);
400 crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
401 .write_buf(contents);
402 crate::term::MouseProtocolMode::new(
403 self.mouse_protocol_mode,
404 MouseProtocolMode::None,
405 )
406 .write_buf(contents);
407 crate::term::MouseProtocolEncoding::new(
408 self.mouse_protocol_encoding,
409 MouseProtocolEncoding::Default,
410 )
411 .write_buf(contents);
412 }
413
414 /// Returns terminal escape sequences sufficient to change the previous
415 /// terminal's input modes to the input modes enabled in the current
416 /// terminal.
417 #[must_use]
418 pub fn input_mode_diff(&self, prev: &Self) -> Vec<u8> {
419 let mut contents = vec![];
420 self.write_input_mode_diff(&mut contents, prev);
421 contents
422 }
423
424 fn write_input_mode_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
425 if self.mode(MODE_APPLICATION_KEYPAD)
426 != prev.mode(MODE_APPLICATION_KEYPAD)
427 {
428 crate::term::ApplicationKeypad::new(
429 self.mode(MODE_APPLICATION_KEYPAD),
430 )
431 .write_buf(contents);
432 }
433 if self.mode(MODE_APPLICATION_CURSOR)
434 != prev.mode(MODE_APPLICATION_CURSOR)
435 {
436 crate::term::ApplicationCursor::new(
437 self.mode(MODE_APPLICATION_CURSOR),
438 )
439 .write_buf(contents);
440 }
441 if self.mode(MODE_BRACKETED_PASTE) != prev.mode(MODE_BRACKETED_PASTE)
442 {
443 crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
444 .write_buf(contents);
445 }
446 crate::term::MouseProtocolMode::new(
447 self.mouse_protocol_mode,
448 prev.mouse_protocol_mode,
449 )
450 .write_buf(contents);
451 crate::term::MouseProtocolEncoding::new(
452 self.mouse_protocol_encoding,
453 prev.mouse_protocol_encoding,
454 )
455 .write_buf(contents);
456 }
457
458 /// Returns terminal escape sequences sufficient to set the current
459 /// terminal's drawing attributes.
460 ///
461 /// Supported drawing attributes are:
462 /// * fgcolor
463 /// * bgcolor
464 /// * bold
465 /// * dim
466 /// * italic
467 /// * underline
468 /// * inverse
469 ///
470 /// This is not typically necessary, since
471 /// [`contents_formatted`](Self::contents_formatted) will leave
472 /// the current active drawing attributes in the correct state, but this
473 /// can be useful in the case of drawing additional things on top of a
474 /// terminal output, since you will need to restore the terminal state
475 /// without the terminal contents necessarily being the same.
476 #[must_use]
477 pub fn attributes_formatted(&self) -> Vec<u8> {
478 let mut contents = vec![];
479 self.write_attributes_formatted(&mut contents);
480 contents
481 }
482
483 fn write_attributes_formatted(&self, contents: &mut Vec<u8>) {
484 crate::term::ClearAttrs.write_buf(contents);
485 self.attrs.write_escape_code_diff(
486 contents,
487 &crate::attrs::Attrs::default(),
488 );
489 }
490
491 /// Returns the current cursor position of the terminal.
492 ///
493 /// The return value will be (row, col).
494 #[must_use]
495 pub fn cursor_position(&self) -> (u16, u16) {
496 let pos = self.grid().pos();
497 (pos.row, pos.col)
498 }
499
500 /// Returns terminal escape sequences sufficient to set the current
501 /// cursor state of the terminal.
502 ///
503 /// This is not typically necessary, since
504 /// [`contents_formatted`](Self::contents_formatted) will leave
505 /// the cursor in the correct state, but this can be useful in the case of
506 /// drawing additional things on top of a terminal output, since you will
507 /// need to restore the terminal state without the terminal contents
508 /// necessarily being the same.
509 ///
510 /// Note that the bytes returned by this function may alter the active
511 /// drawing attributes, because it may require redrawing existing cells in
512 /// order to position the cursor correctly (for instance, in the case
513 /// where the cursor is past the end of a row). Therefore, you should
514 /// ensure to reset the active drawing attributes if necessary after
515 /// processing this data, for instance by using
516 /// [`attributes_formatted`](Self::attributes_formatted).
517 #[must_use]
518 pub fn cursor_state_formatted(&self) -> Vec<u8> {
519 let mut contents = vec![];
520 self.write_cursor_state_formatted(&mut contents);
521 contents
522 }
523
524 fn write_cursor_state_formatted(&self, contents: &mut Vec<u8>) {
525 crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
526 self.grid()
527 .write_cursor_position_formatted(contents, None, None);
528
529 // we don't just call write_attributes_formatted here, because that
530 // would still be confusing - consider the case where the user sets
531 // their own unrelated drawing attributes (on a different parser
532 // instance) and then calls cursor_state_formatted. just documenting
533 // it and letting the user handle it on their own is more
534 // straightforward.
535 }
536
537 /// Returns the [`Cell`](crate::Cell) object at the given location in the
538 /// terminal, if it exists.
539 #[must_use]
540 pub fn cell(&self, row: u16, col: u16) -> Option<&crate::Cell> {
541 self.grid().visible_cell(crate::grid::Pos { row, col })
542 }
543
544 /// Returns whether the text in row `row` should wrap to the next line.
545 #[must_use]
546 pub fn row_wrapped(&self, row: u16) -> bool {
547 self.grid()
548 .visible_row(row)
549 .is_some_and(crate::row::Row::wrapped)
550 }
551
552 /// Returns whether the alternate screen is currently in use.
553 #[must_use]
554 pub fn alternate_screen(&self) -> bool {
555 self.mode(MODE_ALTERNATE_SCREEN)
556 }
557
558 /// Returns whether the terminal should be in application keypad mode.
559 #[must_use]
560 pub fn application_keypad(&self) -> bool {
561 self.mode(MODE_APPLICATION_KEYPAD)
562 }
563
564 /// Returns whether the terminal should be in application cursor mode.
565 #[must_use]
566 pub fn application_cursor(&self) -> bool {
567 self.mode(MODE_APPLICATION_CURSOR)
568 }
569
570 /// Returns whether the terminal should be in hide cursor mode.
571 #[must_use]
572 pub fn hide_cursor(&self) -> bool {
573 self.mode(MODE_HIDE_CURSOR)
574 }
575
576 /// Returns whether the terminal should be in bracketed paste mode.
577 #[must_use]
578 pub fn bracketed_paste(&self) -> bool {
579 self.mode(MODE_BRACKETED_PASTE)
580 }
581
582 /// Returns the currently active [`MouseProtocolMode`].
583 #[must_use]
584 pub fn mouse_protocol_mode(&self) -> MouseProtocolMode {
585 self.mouse_protocol_mode
586 }
587
588 /// Returns the currently active [`MouseProtocolEncoding`].
589 #[must_use]
590 pub fn mouse_protocol_encoding(&self) -> MouseProtocolEncoding {
591 self.mouse_protocol_encoding
592 }
593
594 /// Returns the currently active foreground color.
595 #[must_use]
596 pub fn fgcolor(&self) -> crate::Color {
597 self.attrs.fgcolor
598 }
599
600 /// Returns the currently active background color.
601 #[must_use]
602 pub fn bgcolor(&self) -> crate::Color {
603 self.attrs.bgcolor
604 }
605
606 /// Returns whether newly drawn text should be rendered with the bold text
607 /// attribute.
608 #[must_use]
609 pub fn bold(&self) -> bool {
610 self.attrs.bold()
611 }
612
613 /// Returns whether newly drawn text should be rendered with the dim text
614 /// attribute.
615 #[must_use]
616 pub fn dim(&self) -> bool {
617 self.attrs.dim()
618 }
619
620 /// Returns whether newly drawn text should be rendered with the italic
621 /// text attribute.
622 #[must_use]
623 pub fn italic(&self) -> bool {
624 self.attrs.italic()
625 }
626
627 /// Returns whether newly drawn text should be rendered with the
628 /// underlined text attribute.
629 #[must_use]
630 pub fn underline(&self) -> bool {
631 self.attrs.underline()
632 }
633
634 /// Returns whether newly drawn text should be rendered with the inverse
635 /// text attribute.
636 #[must_use]
637 pub fn inverse(&self) -> bool {
638 self.attrs.inverse()
639 }
640
641 pub(crate) fn grid(&self) -> &crate::grid::Grid {
642 if self.mode(MODE_ALTERNATE_SCREEN) {
643 &self.alternate_grid
644 } else {
645 &self.grid
646 }
647 }
648
649 fn grid_mut(&mut self) -> &mut crate::grid::Grid {
650 if self.mode(MODE_ALTERNATE_SCREEN) {
651 &mut self.alternate_grid
652 } else {
653 &mut self.grid
654 }
655 }
656
657 fn enter_alternate_grid(&mut self) {
658 self.grid_mut().set_scrollback(0);
659 self.set_mode(MODE_ALTERNATE_SCREEN);
660 self.alternate_grid.allocate_rows();
661 }
662
663 fn exit_alternate_grid(&mut self) {
664 self.clear_mode(MODE_ALTERNATE_SCREEN);
665 }
666
667 fn save_cursor(&mut self) {
668 self.grid_mut().save_cursor();
669 self.saved_attrs = self.attrs;
670 }
671
672 fn restore_cursor(&mut self) {
673 self.grid_mut().restore_cursor();
674 self.attrs = self.saved_attrs;
675 }
676
677 fn set_mode(&mut self, mode: u8) {
678 self.modes |= mode;
679 }
680
681 fn clear_mode(&mut self, mode: u8) {
682 self.modes &= !mode;
683 }
684
685 fn mode(&self, mode: u8) -> bool {
686 self.modes & mode != 0
687 }
688
689 fn set_mouse_mode(&mut self, mode: MouseProtocolMode) {
690 self.mouse_protocol_mode = mode;
691 }
692
693 fn clear_mouse_mode(&mut self, mode: MouseProtocolMode) {
694 if self.mouse_protocol_mode == mode {
695 self.mouse_protocol_mode = MouseProtocolMode::default();
696 }
697 }
698
699 fn set_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
700 self.mouse_protocol_encoding = encoding;
701 }
702
703 fn clear_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
704 if self.mouse_protocol_encoding == encoding {
705 self.mouse_protocol_encoding = MouseProtocolEncoding::default();
706 }
707 }
708}
709
710impl Screen {
711 pub(crate) fn text(&mut self, c: char) {
712 let pos = self.grid().pos();
713 let size = self.grid().size();
714 let attrs = self.attrs;
715
716 let width = c.width();
717 if width.is_none() && (u32::from(c)) < 256 {
718 // don't even try to draw control characters
719 return;
720 }
721 let width = width
722 .unwrap_or(1)
723 .try_into()
724 // width() can only return 0, 1, or 2
725 .unwrap();
726
727 // it doesn't make any sense to wrap if the last column in a row
728 // didn't already have contents. don't try to handle the case where a
729 // character wraps because there was only one column left in the
730 // previous row - literally everything handles this case differently,
731 // and this is tmux behavior (and also the simplest). i'm open to
732 // reconsidering this behavior, but only with a really good reason
733 // (xterm handles this by introducing the concept of triple width
734 // cells, which i really don't want to do).
735 let mut wrap = false;
736 if pos.col > size.cols - width {
737 let last_cell = self
738 .grid()
739 .drawing_cell(crate::grid::Pos {
740 row: pos.row,
741 col: size.cols - 1,
742 })
743 // pos.row is valid, since it comes directly from
744 // self.grid().pos() which we assume to always have a valid
745 // row value. size.cols - 1 is also always a valid column.
746 .unwrap();
747 if last_cell.has_contents() || last_cell.is_wide_continuation() {
748 wrap = true;
749 }
750 }
751 self.grid_mut().col_wrap(width, wrap);
752 let pos = self.grid().pos();
753
754 if width == 0 {
755 if pos.col > 0 {
756 let mut prev_cell = self
757 .grid_mut()
758 .drawing_cell_mut(crate::grid::Pos {
759 row: pos.row,
760 col: pos.col - 1,
761 })
762 // pos.row is valid, since it comes directly from
763 // self.grid().pos() which we assume to always have a
764 // valid row value. pos.col - 1 is valid because we just
765 // checked for pos.col > 0.
766 .unwrap();
767 if prev_cell.is_wide_continuation() {
768 prev_cell = self
769 .grid_mut()
770 .drawing_cell_mut(crate::grid::Pos {
771 row: pos.row,
772 col: pos.col - 2,
773 })
774 // pos.row is valid, since it comes directly from
775 // self.grid().pos() which we assume to always have a
776 // valid row value. we know pos.col - 2 is valid
777 // because the cell at pos.col - 1 is a wide
778 // continuation character, which means there must be
779 // the first half of the wide character before it.
780 .unwrap();
781 }
782 prev_cell.append(c);
783 } else if pos.row > 0 {
784 let prev_row = self
785 .grid()
786 .drawing_row(pos.row - 1)
787 // pos.row is valid, since it comes directly from
788 // self.grid().pos() which we assume to always have a
789 // valid row value. pos.row - 1 is valid because we just
790 // checked for pos.row > 0.
791 .unwrap();
792 if prev_row.wrapped() {
793 let mut prev_cell = self
794 .grid_mut()
795 .drawing_cell_mut(crate::grid::Pos {
796 row: pos.row - 1,
797 col: size.cols - 1,
798 })
799 // pos.row is valid, since it comes directly from
800 // self.grid().pos() which we assume to always have a
801 // valid row value. pos.row - 1 is valid because we
802 // just checked for pos.row > 0. col of size.cols - 1
803 // is always valid.
804 .unwrap();
805 if prev_cell.is_wide_continuation() {
806 prev_cell = self
807 .grid_mut()
808 .drawing_cell_mut(crate::grid::Pos {
809 row: pos.row - 1,
810 col: size.cols - 2,
811 })
812 // pos.row is valid, since it comes directly from
813 // self.grid().pos() which we assume to always
814 // have a valid row value. pos.row - 1 is valid
815 // because we just checked for pos.row > 0. col of
816 // size.cols - 2 is valid because the cell at
817 // size.cols - 1 is a wide continuation character,
818 // so it must have the first half of the wide
819 // character before it.
820 .unwrap();
821 }
822 prev_cell.append(c);
823 }
824 }
825 } else {
826 if self
827 .grid()
828 .drawing_cell(pos)
829 // pos.row is valid because we assume self.grid().pos() to
830 // always have a valid row value. pos.col is valid because we
831 // called col_wrap() immediately before this, which ensures
832 // that self.grid().pos().col has a valid value.
833 .unwrap()
834 .is_wide_continuation()
835 {
836 let prev_cell = self
837 .grid_mut()
838 .drawing_cell_mut(crate::grid::Pos {
839 row: pos.row,
840 col: pos.col - 1,
841 })
842 // pos.row is valid because we assume self.grid().pos() to
843 // always have a valid row value. pos.col is valid because
844 // we called col_wrap() immediately before this, which
845 // ensures that self.grid().pos().col has a valid value.
846 // pos.col - 1 is valid because the cell at pos.col is a
847 // wide continuation character, so it must have the first
848 // half of the wide character before it.
849 .unwrap();
850 prev_cell.clear(attrs);
851 }
852
853 if self
854 .grid()
855 .drawing_cell(pos)
856 // pos.row is valid because we assume self.grid().pos() to
857 // always have a valid row value. pos.col is valid because we
858 // called col_wrap() immediately before this, which ensures
859 // that self.grid().pos().col has a valid value.
860 .unwrap()
861 .is_wide()
862 {
863 let next_cell = self
864 .grid_mut()
865 .drawing_cell_mut(crate::grid::Pos {
866 row: pos.row,
867 col: pos.col + 1,
868 })
869 // pos.row is valid because we assume self.grid().pos() to
870 // always have a valid row value. pos.col is valid because
871 // we called col_wrap() immediately before this, which
872 // ensures that self.grid().pos().col has a valid value.
873 // pos.col + 1 is valid because the cell at pos.col is a
874 // wide character, so it must have the second half of the
875 // wide character after it.
876 .unwrap();
877 next_cell.set(' ', attrs);
878 }
879
880 let cell = self
881 .grid_mut()
882 .drawing_cell_mut(pos)
883 // pos.row is valid because we assume self.grid().pos() to
884 // always have a valid row value. pos.col is valid because we
885 // called col_wrap() immediately before this, which ensures
886 // that self.grid().pos().col has a valid value.
887 .unwrap();
888 cell.set(c, attrs);
889 self.grid_mut().col_inc(1);
890 if width > 1 {
891 let pos = self.grid().pos();
892 if self
893 .grid()
894 .drawing_cell(pos)
895 // pos.row is valid because we assume self.grid().pos() to
896 // always have a valid row value. pos.col is valid because
897 // we called col_wrap() earlier, which ensures that
898 // self.grid().pos().col has a valid value. this is true
899 // even though we just called col_inc, because this branch
900 // only happens if width > 1, and col_wrap takes width
901 // into account.
902 .unwrap()
903 .is_wide()
904 {
905 let next_next_pos = crate::grid::Pos {
906 row: pos.row,
907 col: pos.col + 1,
908 };
909 let next_next_cell = self
910 .grid_mut()
911 .drawing_cell_mut(next_next_pos)
912 // pos.row is valid because we assume
913 // self.grid().pos() to always have a valid row value.
914 // pos.col is valid because we called col_wrap()
915 // earlier, which ensures that self.grid().pos().col
916 // has a valid value. this is true even though we just
917 // called col_inc, because this branch only happens if
918 // width > 1, and col_wrap takes width into account.
919 // pos.col + 1 is valid because the cell at pos.col is
920 // wide, and so it must have the second half of the
921 // wide character after it.
922 .unwrap();
923 next_next_cell.clear(attrs);
924 if next_next_pos.col == size.cols - 1 {
925 self.grid_mut()
926 .drawing_row_mut(pos.row)
927 // we assume self.grid().pos().row is always valid
928 .unwrap()
929 .wrap(false);
930 }
931 }
932 let next_cell = self
933 .grid_mut()
934 .drawing_cell_mut(pos)
935 // pos.row is valid because we assume self.grid().pos() to
936 // always have a valid row value. pos.col is valid because
937 // we called col_wrap() earlier, which ensures that
938 // self.grid().pos().col has a valid value. this is true
939 // even though we just called col_inc, because this branch
940 // only happens if width > 1, and col_wrap takes width
941 // into account.
942 .unwrap();
943 next_cell.clear(crate::attrs::Attrs::default());
944 next_cell.set_wide_continuation(true);
945 self.grid_mut().col_inc(1);
946 }
947 }
948 }
949
950 // control codes
951
952 pub(crate) fn bs(&mut self) {
953 self.grid_mut().col_dec(1);
954 }
955
956 pub(crate) fn tab(&mut self) {
957 self.grid_mut().col_tab();
958 }
959
960 pub(crate) fn lf(&mut self) {
961 self.grid_mut().row_inc_scroll(1);
962 }
963
964 pub(crate) fn vt(&mut self) {
965 self.lf();
966 }
967
968 pub(crate) fn ff(&mut self) {
969 self.lf();
970 }
971
972 pub(crate) fn cr(&mut self) {
973 self.grid_mut().col_set(0);
974 }
975
976 // escape codes
977
978 // ESC 7
979 pub(crate) fn decsc(&mut self) {
980 self.save_cursor();
981 }
982
983 // ESC 8
984 pub(crate) fn decrc(&mut self) {
985 self.restore_cursor();
986 }
987
988 // ESC =
989 pub(crate) fn deckpam(&mut self) {
990 self.set_mode(MODE_APPLICATION_KEYPAD);
991 }
992
993 // ESC >
994 pub(crate) fn deckpnm(&mut self) {
995 self.clear_mode(MODE_APPLICATION_KEYPAD);
996 }
997
998 // ESC M
999 pub(crate) fn ri(&mut self) {
1000 self.grid_mut().row_dec_scroll(1);
1001 }
1002
1003 // ESC c
1004 pub(crate) fn ris(&mut self) {
1005 *self = Self::new(self.grid.size(), self.grid.scrollback_len());
1006 }
1007
1008 // csi codes
1009
1010 // CSI @
1011 pub(crate) fn ich(&mut self, count: u16) {
1012 self.grid_mut().insert_cells(count);
1013 }
1014
1015 // CSI A
1016 pub(crate) fn cuu(&mut self, offset: u16) {
1017 self.grid_mut().row_dec_clamp(offset);
1018 }
1019
1020 // CSI B
1021 pub(crate) fn cud(&mut self, offset: u16) {
1022 self.grid_mut().row_inc_clamp(offset);
1023 }
1024
1025 // CSI C
1026 pub(crate) fn cuf(&mut self, offset: u16) {
1027 self.grid_mut().col_inc_clamp(offset);
1028 }
1029
1030 // CSI D
1031 pub(crate) fn cub(&mut self, offset: u16) {
1032 self.grid_mut().col_dec(offset);
1033 }
1034
1035 // CSI E
1036 pub(crate) fn cnl(&mut self, offset: u16) {
1037 self.grid_mut().col_set(0);
1038 self.grid_mut().row_inc_clamp(offset);
1039 }
1040
1041 // CSI F
1042 pub(crate) fn cpl(&mut self, offset: u16) {
1043 self.grid_mut().col_set(0);
1044 self.grid_mut().row_dec_clamp(offset);
1045 }
1046
1047 // CSI G
1048 pub(crate) fn cha(&mut self, col: u16) {
1049 self.grid_mut().col_set(col - 1);
1050 }
1051
1052 // CSI H
1053 pub(crate) fn cup(&mut self, (row, col): (u16, u16)) {
1054 self.grid_mut().set_pos(crate::grid::Pos {
1055 row: row - 1,
1056 col: col - 1,
1057 });
1058 }
1059
1060 // CSI J
1061 pub(crate) fn ed(
1062 &mut self,
1063 mode: u16,
1064 mut unhandled: impl FnMut(&mut Self),
1065 ) {
1066 let attrs = self.attrs;
1067 match mode {
1068 0 => self.grid_mut().erase_all_forward(attrs),
1069 1 => self.grid_mut().erase_all_backward(attrs),
1070 2 => self.grid_mut().erase_all(attrs),
1071 _ => unhandled(self),
1072 }
1073 }
1074
1075 // CSI ? J
1076 pub(crate) fn decsed(
1077 &mut self,
1078 mode: u16,
1079 unhandled: impl FnMut(&mut Self),
1080 ) {
1081 self.ed(mode, unhandled);
1082 }
1083
1084 // CSI K
1085 pub(crate) fn el(
1086 &mut self,
1087 mode: u16,
1088 mut unhandled: impl FnMut(&mut Self),
1089 ) {
1090 let attrs = self.attrs;
1091 match mode {
1092 0 => self.grid_mut().erase_row_forward(attrs),
1093 1 => self.grid_mut().erase_row_backward(attrs),
1094 2 => self.grid_mut().erase_row(attrs),
1095 _ => unhandled(self),
1096 }
1097 }
1098
1099 // CSI ? K
1100 pub(crate) fn decsel(
1101 &mut self,
1102 mode: u16,
1103 unhandled: impl FnMut(&mut Self),
1104 ) {
1105 self.el(mode, unhandled);
1106 }
1107
1108 // CSI L
1109 pub(crate) fn il(&mut self, count: u16) {
1110 self.grid_mut().insert_lines(count);
1111 }
1112
1113 // CSI M
1114 pub(crate) fn dl(&mut self, count: u16) {
1115 self.grid_mut().delete_lines(count);
1116 }
1117
1118 // CSI P
1119 pub(crate) fn dch(&mut self, count: u16) {
1120 self.grid_mut().delete_cells(count);
1121 }
1122
1123 // CSI S
1124 pub(crate) fn su(&mut self, count: u16) {
1125 self.grid_mut().scroll_up(count);
1126 }
1127
1128 // CSI T
1129 pub(crate) fn sd(&mut self, count: u16) {
1130 self.grid_mut().scroll_down(count);
1131 }
1132
1133 // CSI X
1134 pub(crate) fn ech(&mut self, count: u16) {
1135 let attrs = self.attrs;
1136 self.grid_mut().erase_cells(count, attrs);
1137 }
1138
1139 // CSI d
1140 pub(crate) fn vpa(&mut self, row: u16) {
1141 self.grid_mut().row_set(row - 1);
1142 }
1143
1144 // CSI ? h
1145 pub(crate) fn decset(
1146 &mut self,
1147 params: &vte::Params,
1148 mut unhandled: impl FnMut(&mut Self),
1149 ) {
1150 for param in params {
1151 match param {
1152 [1] => self.set_mode(MODE_APPLICATION_CURSOR),
1153 [6] => self.grid_mut().set_origin_mode(true),
1154 [9] => self.set_mouse_mode(MouseProtocolMode::Press),
1155 [25] => self.clear_mode(MODE_HIDE_CURSOR),
1156 [47] => self.enter_alternate_grid(),
1157 [1000] => {
1158 self.set_mouse_mode(MouseProtocolMode::PressRelease);
1159 }
1160 [1002] => {
1161 self.set_mouse_mode(MouseProtocolMode::ButtonMotion);
1162 }
1163 [1003] => self.set_mouse_mode(MouseProtocolMode::AnyMotion),
1164 [1005] => {
1165 self.set_mouse_encoding(MouseProtocolEncoding::Utf8);
1166 }
1167 [1006] => {
1168 self.set_mouse_encoding(MouseProtocolEncoding::Sgr);
1169 }
1170 [1049] => {
1171 self.decsc();
1172 self.alternate_grid.clear();
1173 self.enter_alternate_grid();
1174 }
1175 [2004] => self.set_mode(MODE_BRACKETED_PASTE),
1176 _ => unhandled(self),
1177 }
1178 }
1179 }
1180
1181 // CSI ? l
1182 pub(crate) fn decrst(
1183 &mut self,
1184 params: &vte::Params,
1185 mut unhandled: impl FnMut(&mut Self),
1186 ) {
1187 for param in params {
1188 match param {
1189 [1] => self.clear_mode(MODE_APPLICATION_CURSOR),
1190 [6] => self.grid_mut().set_origin_mode(false),
1191 [9] => self.clear_mouse_mode(MouseProtocolMode::Press),
1192 [25] => self.set_mode(MODE_HIDE_CURSOR),
1193 [47] => {
1194 self.exit_alternate_grid();
1195 }
1196 [1000] => {
1197 self.clear_mouse_mode(MouseProtocolMode::PressRelease);
1198 }
1199 [1002] => {
1200 self.clear_mouse_mode(MouseProtocolMode::ButtonMotion);
1201 }
1202 [1003] => {
1203 self.clear_mouse_mode(MouseProtocolMode::AnyMotion);
1204 }
1205 [1005] => {
1206 self.clear_mouse_encoding(MouseProtocolEncoding::Utf8);
1207 }
1208 [1006] => {
1209 self.clear_mouse_encoding(MouseProtocolEncoding::Sgr);
1210 }
1211 [1049] => {
1212 self.exit_alternate_grid();
1213 self.decrc();
1214 }
1215 [2004] => self.clear_mode(MODE_BRACKETED_PASTE),
1216 _ => unhandled(self),
1217 }
1218 }
1219 }
1220
1221 // CSI m
1222 pub(crate) fn sgr(
1223 &mut self,
1224 params: &vte::Params,
1225 mut unhandled: impl FnMut(&mut Self),
1226 ) {
1227 // XXX really i want to just be able to pass in a default Params
1228 // instance with a 0 in it, but vte doesn't allow creating new Params
1229 // instances
1230 if params.is_empty() {
1231 self.attrs = crate::attrs::Attrs::default();
1232 return;
1233 }
1234
1235 let mut iter = params.iter();
1236
1237 macro_rules! next_param {
1238 () => {
1239 match iter.next() {
1240 Some(n) => n,
1241 _ => return,
1242 }
1243 };
1244 }
1245
1246 macro_rules! to_u8 {
1247 ($n:expr) => {
1248 if let Some(n) = u16_to_u8($n) {
1249 n
1250 } else {
1251 return;
1252 }
1253 };
1254 }
1255
1256 macro_rules! next_param_u8 {
1257 () => {
1258 if let &[n] = next_param!() {
1259 to_u8!(n)
1260 } else {
1261 return;
1262 }
1263 };
1264 }
1265
1266 loop {
1267 match next_param!() {
1268 [0] => self.attrs = crate::attrs::Attrs::default(),
1269 [1] => self.attrs.set_bold(),
1270 [2] => self.attrs.set_dim(),
1271 [3] => self.attrs.set_italic(true),
1272 [4] => self.attrs.set_underline(true),
1273 [7] => self.attrs.set_inverse(true),
1274 [22] => self.attrs.set_normal_intensity(),
1275 [23] => self.attrs.set_italic(false),
1276 [24] => self.attrs.set_underline(false),
1277 [27] => self.attrs.set_inverse(false),
1278 [n] if (30..=37).contains(n) => {
1279 self.attrs.fgcolor = crate::Color::Idx(to_u8!(*n) - 30);
1280 }
1281 [38, 2, r, g, b] => {
1282 self.attrs.fgcolor =
1283 crate::Color::Rgb(to_u8!(*r), to_u8!(*g), to_u8!(*b));
1284 }
1285 [38, 5, i] => {
1286 self.attrs.fgcolor = crate::Color::Idx(to_u8!(*i));
1287 }
1288 [38] => match next_param!() {
1289 [2] => {
1290 let r = next_param_u8!();
1291 let g = next_param_u8!();
1292 let b = next_param_u8!();
1293 self.attrs.fgcolor = crate::Color::Rgb(r, g, b);
1294 }
1295 [5] => {
1296 self.attrs.fgcolor =
1297 crate::Color::Idx(next_param_u8!());
1298 }
1299 _ => {
1300 unhandled(self);
1301 return;
1302 }
1303 },
1304 [39] => {
1305 self.attrs.fgcolor = crate::Color::Default;
1306 }
1307 [n] if (40..=47).contains(n) => {
1308 self.attrs.bgcolor = crate::Color::Idx(to_u8!(*n) - 40);
1309 }
1310 [48, 2, r, g, b] => {
1311 self.attrs.bgcolor =
1312 crate::Color::Rgb(to_u8!(*r), to_u8!(*g), to_u8!(*b));
1313 }
1314 [48, 5, i] => {
1315 self.attrs.bgcolor = crate::Color::Idx(to_u8!(*i));
1316 }
1317 [48] => match next_param!() {
1318 [2] => {
1319 let r = next_param_u8!();
1320 let g = next_param_u8!();
1321 let b = next_param_u8!();
1322 self.attrs.bgcolor = crate::Color::Rgb(r, g, b);
1323 }
1324 [5] => {
1325 self.attrs.bgcolor =
1326 crate::Color::Idx(next_param_u8!());
1327 }
1328 _ => {
1329 unhandled(self);
1330 return;
1331 }
1332 },
1333 [49] => {
1334 self.attrs.bgcolor = crate::Color::Default;
1335 }
1336 [n] if (90..=97).contains(n) => {
1337 self.attrs.fgcolor = crate::Color::Idx(to_u8!(*n) - 82);
1338 }
1339 [n] if (100..=107).contains(n) => {
1340 self.attrs.bgcolor = crate::Color::Idx(to_u8!(*n) - 92);
1341 }
1342 _ => unhandled(self),
1343 }
1344 }
1345 }
1346
1347 // CSI r
1348 pub(crate) fn decstbm(&mut self, (top, bottom): (u16, u16)) {
1349 self.grid_mut().set_scroll_region(top - 1, bottom - 1);
1350 }
1351}
1352
1353fn u16_to_u8(i: u16) -> Option<u8> {
1354 if i > u16::from(u8::MAX) {
1355 None
1356 } else {
1357 // safe because we just ensured that the value fits in a u8
1358 Some(i.try_into().unwrap())
1359 }
1360}