@akiojin/gwt 9.7.0 → 9.8.0

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