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