vt100/
grid.rs

1use crate::term::BufWrite as _;
2
3#[derive(Clone, Debug)]
4pub struct Grid {
5    size: Size,
6    pos: Pos,
7    saved_pos: Pos,
8    rows: Vec<crate::row::Row>,
9    scroll_top: u16,
10    scroll_bottom: u16,
11    origin_mode: bool,
12    saved_origin_mode: bool,
13    scrollback: std::collections::VecDeque<crate::row::Row>,
14    scrollback_len: usize,
15    scrollback_offset: usize,
16}
17
18impl Grid {
19    pub fn new(size: Size, scrollback_len: usize) -> Self {
20        Self {
21            size,
22            pos: Pos::default(),
23            saved_pos: Pos::default(),
24            rows: vec![],
25            scroll_top: 0,
26            scroll_bottom: size.rows - 1,
27            origin_mode: false,
28            saved_origin_mode: false,
29            scrollback: std::collections::VecDeque::new(),
30            scrollback_len,
31            scrollback_offset: 0,
32        }
33    }
34
35    pub fn allocate_rows(&mut self) {
36        if self.rows.is_empty() {
37            self.rows.extend(
38                std::iter::repeat_with(|| {
39                    crate::row::Row::new(self.size.cols)
40                })
41                .take(usize::from(self.size.rows)),
42            );
43        }
44    }
45
46    fn new_row(&self) -> crate::row::Row {
47        crate::row::Row::new(self.size.cols)
48    }
49
50    pub fn clear(&mut self) {
51        self.pos = Pos::default();
52        self.saved_pos = Pos::default();
53        for row in self.drawing_rows_mut() {
54            row.clear(crate::attrs::Attrs::default());
55        }
56        self.scroll_top = 0;
57        self.scroll_bottom = self.size.rows - 1;
58        self.origin_mode = false;
59        self.saved_origin_mode = false;
60    }
61
62    pub fn size(&self) -> Size {
63        self.size
64    }
65
66    pub fn set_size(&mut self, size: Size) {
67        if size.cols != self.size.cols {
68            for row in &mut self.rows {
69                row.wrap(false);
70            }
71        }
72
73        if self.scroll_bottom == self.size.rows - 1 {
74            self.scroll_bottom = size.rows - 1;
75        }
76
77        self.size = size;
78        for row in &mut self.rows {
79            row.resize(size.cols, crate::cell::Cell::default());
80        }
81        self.rows.resize(usize::from(size.rows), self.new_row());
82
83        if self.scroll_bottom >= size.rows {
84            self.scroll_bottom = size.rows - 1;
85        }
86        if self.scroll_bottom < self.scroll_top {
87            self.scroll_top = 0;
88        }
89
90        self.row_clamp_top(false);
91        self.row_clamp_bottom(false);
92        self.col_clamp();
93    }
94
95    pub fn pos(&self) -> Pos {
96        self.pos
97    }
98
99    pub fn set_pos(&mut self, mut pos: Pos) {
100        if self.origin_mode {
101            pos.row = pos.row.saturating_add(self.scroll_top);
102        }
103        self.pos = pos;
104        self.row_clamp_top(self.origin_mode);
105        self.row_clamp_bottom(self.origin_mode);
106        self.col_clamp();
107    }
108
109    pub fn save_cursor(&mut self) {
110        self.saved_pos = self.pos;
111        self.saved_origin_mode = self.origin_mode;
112    }
113
114    pub fn restore_cursor(&mut self) {
115        self.pos = self.saved_pos;
116        self.origin_mode = self.saved_origin_mode;
117    }
118
119    pub fn visible_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
120        let scrollback_len = self.scrollback.len();
121        let rows_len = self.rows.len();
122        self.scrollback
123            .iter()
124            .skip(scrollback_len - self.scrollback_offset)
125            .chain(self.rows.iter().take(rows_len - self.scrollback_offset))
126    }
127
128    pub fn drawing_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
129        self.rows.iter()
130    }
131
132    pub fn drawing_rows_mut(
133        &mut self,
134    ) -> impl Iterator<Item = &mut crate::row::Row> {
135        self.rows.iter_mut()
136    }
137
138    pub fn visible_row(&self, row: u16) -> Option<&crate::row::Row> {
139        self.visible_rows().nth(usize::from(row))
140    }
141
142    pub fn drawing_row(&self, row: u16) -> Option<&crate::row::Row> {
143        self.drawing_rows().nth(usize::from(row))
144    }
145
146    pub fn drawing_row_mut(
147        &mut self,
148        row: u16,
149    ) -> Option<&mut crate::row::Row> {
150        self.drawing_rows_mut().nth(usize::from(row))
151    }
152
153    pub fn current_row_mut(&mut self) -> &mut crate::row::Row {
154        self.drawing_row_mut(self.pos.row)
155            // we assume self.pos.row is always valid
156            .unwrap()
157    }
158
159    pub fn visible_cell(&self, pos: Pos) -> Option<&crate::cell::Cell> {
160        self.visible_row(pos.row).and_then(|r| r.get(pos.col))
161    }
162
163    pub fn drawing_cell(&self, pos: Pos) -> Option<&crate::cell::Cell> {
164        self.drawing_row(pos.row).and_then(|r| r.get(pos.col))
165    }
166
167    pub fn drawing_cell_mut(
168        &mut self,
169        pos: Pos,
170    ) -> Option<&mut crate::cell::Cell> {
171        self.drawing_row_mut(pos.row)
172            .and_then(|r| r.get_mut(pos.col))
173    }
174
175    pub fn scrollback_len(&self) -> usize {
176        self.scrollback_len
177    }
178
179    pub fn scrollback(&self) -> usize {
180        self.scrollback_offset
181    }
182
183    pub fn set_scrollback(&mut self, rows: usize) {
184        self.scrollback_offset = rows.min(self.scrollback.len());
185    }
186
187    pub fn write_contents(&self, contents: &mut String) {
188        let mut wrapping = false;
189        for row in self.visible_rows() {
190            row.write_contents(contents, 0, self.size.cols, wrapping);
191            if !row.wrapped() {
192                contents.push('\n');
193            }
194            wrapping = row.wrapped();
195        }
196
197        while contents.ends_with('\n') {
198            contents.truncate(contents.len() - 1);
199        }
200    }
201
202    pub fn write_contents_formatted(
203        &self,
204        contents: &mut Vec<u8>,
205    ) -> crate::attrs::Attrs {
206        crate::term::ClearAttrs::default().write_buf(contents);
207        crate::term::ClearScreen::default().write_buf(contents);
208
209        let mut prev_attrs = crate::attrs::Attrs::default();
210        let mut prev_pos = Pos::default();
211        let mut wrapping = false;
212        for (i, row) in self.visible_rows().enumerate() {
213            // we limit the number of cols to a u16 (see Size), so
214            // visible_rows() can never return more rows than will fit
215            let i = i.try_into().unwrap();
216            let (new_pos, new_attrs) = row.write_contents_formatted(
217                contents,
218                0,
219                self.size.cols,
220                i,
221                wrapping,
222                Some(prev_pos),
223                Some(prev_attrs),
224            );
225            prev_pos = new_pos;
226            prev_attrs = new_attrs;
227            wrapping = row.wrapped();
228        }
229
230        self.write_cursor_position_formatted(
231            contents,
232            Some(prev_pos),
233            Some(prev_attrs),
234        );
235
236        prev_attrs
237    }
238
239    pub fn write_contents_diff(
240        &self,
241        contents: &mut Vec<u8>,
242        prev: &Self,
243        mut prev_attrs: crate::attrs::Attrs,
244    ) -> crate::attrs::Attrs {
245        let mut prev_pos = prev.pos;
246        let mut wrapping = false;
247        let mut prev_wrapping = false;
248        for (i, (row, prev_row)) in
249            self.visible_rows().zip(prev.visible_rows()).enumerate()
250        {
251            // we limit the number of cols to a u16 (see Size), so
252            // visible_rows() can never return more rows than will fit
253            let i = i.try_into().unwrap();
254            let (new_pos, new_attrs) = row.write_contents_diff(
255                contents,
256                prev_row,
257                0,
258                self.size.cols,
259                i,
260                wrapping,
261                prev_wrapping,
262                prev_pos,
263                prev_attrs,
264            );
265            prev_pos = new_pos;
266            prev_attrs = new_attrs;
267            wrapping = row.wrapped();
268            prev_wrapping = prev_row.wrapped();
269        }
270
271        self.write_cursor_position_formatted(
272            contents,
273            Some(prev_pos),
274            Some(prev_attrs),
275        );
276
277        prev_attrs
278    }
279
280    pub fn write_cursor_position_formatted(
281        &self,
282        contents: &mut Vec<u8>,
283        prev_pos: Option<Pos>,
284        prev_attrs: Option<crate::attrs::Attrs>,
285    ) {
286        let prev_attrs = prev_attrs.unwrap_or_default();
287        // writing a character to the last column of a row doesn't wrap the
288        // cursor immediately - it waits until the next character is actually
289        // drawn. it is only possible for the cursor to have this kind of
290        // position after drawing a character though, so if we end in this
291        // position, we need to redraw the character at the end of the row.
292        if prev_pos != Some(self.pos) && self.pos.col >= self.size.cols {
293            let mut pos = Pos {
294                row: self.pos.row,
295                col: self.size.cols - 1,
296            };
297            if self
298                .drawing_cell(pos)
299                // we assume self.pos.row is always valid, and self.size.cols
300                // - 1 is always a valid column
301                .unwrap()
302                .is_wide_continuation()
303            {
304                pos.col = self.size.cols - 2;
305            }
306            let cell =
307                // we assume self.pos.row is always valid, and self.size.cols
308                // - 2 must be a valid column because self.size.cols - 1 is
309                // always valid and we just checked that the cell at
310                // self.size.cols - 1 is a wide continuation character, which
311                // means that the first half of the wide character must be
312                // before it
313                self.drawing_cell(pos).unwrap();
314            if cell.has_contents() {
315                if let Some(prev_pos) = prev_pos {
316                    crate::term::MoveFromTo::new(prev_pos, pos)
317                        .write_buf(contents);
318                } else {
319                    crate::term::MoveTo::new(pos).write_buf(contents);
320                }
321                cell.attrs().write_escape_code_diff(contents, &prev_attrs);
322                contents.extend(cell.contents().as_bytes());
323                prev_attrs.write_escape_code_diff(contents, cell.attrs());
324            } else {
325                // if the cell doesn't have contents, we can't have gotten
326                // here by drawing a character in the last column. this means
327                // that as far as i'm aware, we have to have reached here from
328                // a newline when we were already after the end of an earlier
329                // row. in the case where we are already after the end of an
330                // earlier row, we can just write a few newlines, otherwise we
331                // also need to do the same as above to get ourselves to after
332                // the end of a row.
333                let mut found = false;
334                for i in (0..self.pos.row).rev() {
335                    pos.row = i;
336                    pos.col = self.size.cols - 1;
337                    if self
338                        .drawing_cell(pos)
339                        // i is always less than self.pos.row, which we assume
340                        // to be always valid, so it must also be valid.
341                        // self.size.cols - 1 is always a valid col.
342                        .unwrap()
343                        .is_wide_continuation()
344                    {
345                        pos.col = self.size.cols - 2;
346                    }
347                    let cell = self
348                        .drawing_cell(pos)
349                        // i is always less than self.pos.row, which we assume
350                        // to be always valid, so it must also be valid.
351                        // self.size.cols - 2 is valid because self.size.cols
352                        // - 1 is always valid, and col gets set to
353                        // self.size.cols - 2 when the cell at self.size.cols
354                        // - 1 is a wide continuation character, meaning that
355                        // the first half of the wide character must be before
356                        // it
357                        .unwrap();
358                    if cell.has_contents() {
359                        if let Some(prev_pos) = prev_pos {
360                            if prev_pos.row != i
361                                || prev_pos.col < self.size.cols
362                            {
363                                crate::term::MoveFromTo::new(prev_pos, pos)
364                                    .write_buf(contents);
365                                cell.attrs().write_escape_code_diff(
366                                    contents,
367                                    &prev_attrs,
368                                );
369                                contents.extend(cell.contents().as_bytes());
370                                prev_attrs.write_escape_code_diff(
371                                    contents,
372                                    cell.attrs(),
373                                );
374                            }
375                        } else {
376                            crate::term::MoveTo::new(pos).write_buf(contents);
377                            cell.attrs().write_escape_code_diff(
378                                contents,
379                                &prev_attrs,
380                            );
381                            contents.extend(cell.contents().as_bytes());
382                            prev_attrs.write_escape_code_diff(
383                                contents,
384                                cell.attrs(),
385                            );
386                        }
387                        contents.extend(
388                            "\n".repeat(usize::from(self.pos.row - i))
389                                .as_bytes(),
390                        );
391                        found = true;
392                        break;
393                    }
394                }
395
396                // this can happen if you get the cursor off the end of a row,
397                // and then do something to clear the end of the current row
398                // without moving the cursor (IL, DL, ED, EL, etc). we know
399                // there can't be something in the last column because we
400                // would have caught that above, so it should be safe to
401                // overwrite it.
402                if !found {
403                    pos = Pos {
404                        row: self.pos.row,
405                        col: self.size.cols - 1,
406                    };
407                    if let Some(prev_pos) = prev_pos {
408                        crate::term::MoveFromTo::new(prev_pos, pos)
409                            .write_buf(contents);
410                    } else {
411                        crate::term::MoveTo::new(pos).write_buf(contents);
412                    }
413                    contents.push(b' ');
414                    // we know that the cell has no contents, but it still may
415                    // have drawing attributes (background color, etc)
416                    let end_cell = self
417                        .drawing_cell(pos)
418                        // we assume self.pos.row is always valid, and
419                        // self.size.cols - 1 is always a valid column
420                        .unwrap();
421                    end_cell
422                        .attrs()
423                        .write_escape_code_diff(contents, &prev_attrs);
424                    crate::term::SaveCursor::default().write_buf(contents);
425                    crate::term::Backspace::default().write_buf(contents);
426                    crate::term::EraseChar::new(1).write_buf(contents);
427                    crate::term::RestoreCursor::default().write_buf(contents);
428                    prev_attrs
429                        .write_escape_code_diff(contents, end_cell.attrs());
430                }
431            }
432        } else if let Some(prev_pos) = prev_pos {
433            crate::term::MoveFromTo::new(prev_pos, self.pos)
434                .write_buf(contents);
435        } else {
436            crate::term::MoveTo::new(self.pos).write_buf(contents);
437        }
438    }
439
440    pub fn erase_all(&mut self, attrs: crate::attrs::Attrs) {
441        for row in self.drawing_rows_mut() {
442            row.clear(attrs);
443        }
444    }
445
446    pub fn erase_all_forward(&mut self, attrs: crate::attrs::Attrs) {
447        let pos = self.pos;
448        for row in self.drawing_rows_mut().skip(usize::from(pos.row) + 1) {
449            row.clear(attrs);
450        }
451
452        self.erase_row_forward(attrs);
453    }
454
455    pub fn erase_all_backward(&mut self, attrs: crate::attrs::Attrs) {
456        let pos = self.pos;
457        for row in self.drawing_rows_mut().take(usize::from(pos.row)) {
458            row.clear(attrs);
459        }
460
461        self.erase_row_backward(attrs);
462    }
463
464    pub fn erase_row(&mut self, attrs: crate::attrs::Attrs) {
465        self.current_row_mut().clear(attrs);
466    }
467
468    pub fn erase_row_forward(&mut self, attrs: crate::attrs::Attrs) {
469        let size = self.size;
470        let pos = self.pos;
471        let row = self.current_row_mut();
472        for col in pos.col..size.cols {
473            row.erase(col, attrs);
474        }
475    }
476
477    pub fn erase_row_backward(&mut self, attrs: crate::attrs::Attrs) {
478        let size = self.size;
479        let pos = self.pos;
480        let row = self.current_row_mut();
481        for col in 0..=pos.col.min(size.cols - 1) {
482            row.erase(col, attrs);
483        }
484    }
485
486    pub fn insert_cells(&mut self, count: u16) {
487        let size = self.size;
488        let pos = self.pos;
489        let wide = pos.col < size.cols
490            && self
491                .drawing_cell(pos)
492                // we assume self.pos.row is always valid, and we know we are
493                // not off the end of a row because we just checked pos.col <
494                // size.cols
495                .unwrap()
496                .is_wide_continuation();
497        let row = self.current_row_mut();
498        for _ in 0..count {
499            if wide {
500                row.get_mut(pos.col).unwrap().set_wide_continuation(false);
501            }
502            row.insert(pos.col, crate::cell::Cell::default());
503            if wide {
504                row.get_mut(pos.col).unwrap().set_wide_continuation(true);
505            }
506        }
507        row.truncate(size.cols);
508    }
509
510    pub fn delete_cells(&mut self, count: u16) {
511        let size = self.size;
512        let pos = self.pos;
513        let row = self.current_row_mut();
514        for _ in 0..(count.min(size.cols - pos.col)) {
515            row.remove(pos.col);
516        }
517        row.resize(size.cols, crate::cell::Cell::default());
518    }
519
520    pub fn erase_cells(&mut self, count: u16, attrs: crate::attrs::Attrs) {
521        let size = self.size;
522        let pos = self.pos;
523        let row = self.current_row_mut();
524        for col in pos.col..((pos.col.saturating_add(count)).min(size.cols)) {
525            row.erase(col, attrs);
526        }
527    }
528
529    pub fn insert_lines(&mut self, count: u16) {
530        for _ in 0..count {
531            self.rows.remove(usize::from(self.scroll_bottom));
532            self.rows.insert(usize::from(self.pos.row), self.new_row());
533            // self.scroll_bottom is maintained to always be a valid row
534            self.rows[usize::from(self.scroll_bottom)].wrap(false);
535        }
536    }
537
538    pub fn delete_lines(&mut self, count: u16) {
539        for _ in 0..(count.min(self.size.rows - self.pos.row)) {
540            self.rows
541                .insert(usize::from(self.scroll_bottom) + 1, self.new_row());
542            self.rows.remove(usize::from(self.pos.row));
543        }
544    }
545
546    pub fn scroll_up(&mut self, count: u16) {
547        for _ in 0..(count.min(self.size.rows - self.scroll_top)) {
548            self.rows
549                .insert(usize::from(self.scroll_bottom) + 1, self.new_row());
550            let removed = self.rows.remove(usize::from(self.scroll_top));
551            if self.scrollback_len > 0 && !self.scroll_region_active() {
552                self.scrollback.push_back(removed);
553                while self.scrollback.len() > self.scrollback_len {
554                    self.scrollback.pop_front();
555                }
556                if self.scrollback_offset > 0 {
557                    self.scrollback_offset =
558                        self.scrollback.len().min(self.scrollback_offset + 1);
559                }
560            }
561        }
562    }
563
564    pub fn scroll_down(&mut self, count: u16) {
565        for _ in 0..count {
566            self.rows.remove(usize::from(self.scroll_bottom));
567            self.rows
568                .insert(usize::from(self.scroll_top), self.new_row());
569            // self.scroll_bottom is maintained to always be a valid row
570            self.rows[usize::from(self.scroll_bottom)].wrap(false);
571        }
572    }
573
574    pub fn set_scroll_region(&mut self, top: u16, bottom: u16) {
575        let bottom = bottom.min(self.size().rows - 1);
576        if top < bottom {
577            self.scroll_top = top;
578            self.scroll_bottom = bottom;
579        } else {
580            self.scroll_top = 0;
581            self.scroll_bottom = self.size().rows - 1;
582        }
583        self.pos.row = self.scroll_top;
584        self.pos.col = 0;
585    }
586
587    fn in_scroll_region(&self) -> bool {
588        self.pos.row >= self.scroll_top && self.pos.row <= self.scroll_bottom
589    }
590
591    fn scroll_region_active(&self) -> bool {
592        self.scroll_top != 0 || self.scroll_bottom != self.size.rows - 1
593    }
594
595    pub fn set_origin_mode(&mut self, mode: bool) {
596        self.origin_mode = mode;
597        self.set_pos(Pos { row: 0, col: 0 });
598    }
599
600    pub fn row_inc_clamp(&mut self, count: u16) {
601        let in_scroll_region = self.in_scroll_region();
602        self.pos.row = self.pos.row.saturating_add(count);
603        self.row_clamp_bottom(in_scroll_region);
604    }
605
606    pub fn row_inc_scroll(&mut self, count: u16) -> u16 {
607        let in_scroll_region = self.in_scroll_region();
608        self.pos.row = self.pos.row.saturating_add(count);
609        let lines = self.row_clamp_bottom(in_scroll_region);
610        if in_scroll_region {
611            self.scroll_up(lines);
612            lines
613        } else {
614            0
615        }
616    }
617
618    pub fn row_dec_clamp(&mut self, count: u16) {
619        let in_scroll_region = self.in_scroll_region();
620        self.pos.row = self.pos.row.saturating_sub(count);
621        self.row_clamp_top(in_scroll_region);
622    }
623
624    pub fn row_dec_scroll(&mut self, count: u16) {
625        let in_scroll_region = self.in_scroll_region();
626        // need to account for clamping by both row_clamp_top and by
627        // saturating_sub
628        let extra_lines = if count > self.pos.row {
629            count - self.pos.row
630        } else {
631            0
632        };
633        self.pos.row = self.pos.row.saturating_sub(count);
634        let lines = self.row_clamp_top(in_scroll_region);
635        self.scroll_down(lines + extra_lines);
636    }
637
638    pub fn row_set(&mut self, i: u16) {
639        self.pos.row = i;
640        self.row_clamp();
641    }
642
643    pub fn col_inc(&mut self, count: u16) {
644        self.pos.col = self.pos.col.saturating_add(count);
645    }
646
647    pub fn col_inc_clamp(&mut self, count: u16) {
648        self.pos.col = self.pos.col.saturating_add(count);
649        self.col_clamp();
650    }
651
652    pub fn col_dec(&mut self, count: u16) {
653        self.pos.col = self.pos.col.saturating_sub(count);
654    }
655
656    pub fn col_tab(&mut self) {
657        self.pos.col -= self.pos.col % 8;
658        self.pos.col += 8;
659        self.col_clamp();
660    }
661
662    pub fn col_set(&mut self, i: u16) {
663        self.pos.col = i;
664        self.col_clamp();
665    }
666
667    pub fn col_wrap(&mut self, width: u16, wrap: bool) {
668        if self.pos.col > self.size.cols - width {
669            let mut prev_pos = self.pos;
670            self.pos.col = 0;
671            let scrolled = self.row_inc_scroll(1);
672            prev_pos.row -= scrolled;
673            let new_pos = self.pos;
674            self.drawing_row_mut(prev_pos.row)
675                // we assume self.pos.row is always valid, and so prev_pos.row
676                // must be valid because it is always less than or equal to
677                // self.pos.row
678                .unwrap()
679                .wrap(wrap && prev_pos.row + 1 == new_pos.row);
680        }
681    }
682
683    fn row_clamp_top(&mut self, limit_to_scroll_region: bool) -> u16 {
684        if limit_to_scroll_region && self.pos.row < self.scroll_top {
685            let rows = self.scroll_top - self.pos.row;
686            self.pos.row = self.scroll_top;
687            rows
688        } else {
689            0
690        }
691    }
692
693    fn row_clamp_bottom(&mut self, limit_to_scroll_region: bool) -> u16 {
694        let bottom = if limit_to_scroll_region {
695            self.scroll_bottom
696        } else {
697            self.size.rows - 1
698        };
699        if self.pos.row > bottom {
700            let rows = self.pos.row - bottom;
701            self.pos.row = bottom;
702            rows
703        } else {
704            0
705        }
706    }
707
708    fn row_clamp(&mut self) {
709        if self.pos.row > self.size.rows - 1 {
710            self.pos.row = self.size.rows - 1;
711        }
712    }
713
714    fn col_clamp(&mut self) {
715        if self.pos.col > self.size.cols - 1 {
716            self.pos.col = self.size.cols - 1;
717        }
718    }
719}
720
721#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
722pub struct Size {
723    pub rows: u16,
724    pub cols: u16,
725}
726
727#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
728pub struct Pos {
729    pub row: u16,
730    pub col: u16,
731}