aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2021-04-14 13:52:45 +0200
committerGitHub <noreply@github.com>2021-04-14 13:52:45 +0200
commitceb2ebbd2bdeecd0e50fcb2fbc618264a4ad9a3d (patch)
treee7e088423d75b447f9a2c7d8fcb48c394eb75c41
parentfix(bug): remove buggy expansion boundaries (diff)
downloadsl0ppij-ceb2ebbd2bdeecd0e50fcb2fbc618264a4ad9a3d.tar.gz
sl0ppij-ceb2ebbd2bdeecd0e50fcb2fbc618264a4ad9a3d.tar.xz
sl0ppij-ceb2ebbd2bdeecd0e50fcb2fbc618264a4ad9a3d.zip
fix(terminal): persiste vte state (#269)
* fix(terminal): persist vte state * style(fmt): rustfmt
-rw-r--r--src/client/panes/grid.rs320
-rw-r--r--src/client/panes/terminal_pane.rs368
-rw-r--r--src/tests/utils.rs2
3 files changed, 333 insertions, 357 deletions
diff --git a/src/client/panes/grid.rs b/src/client/panes/grid.rs
index d7068855..86dc4753 100644
--- a/src/client/panes/grid.rs
+++ b/src/client/panes/grid.rs
@@ -7,6 +7,8 @@ use std::{
const TABSTOP_WIDTH: usize = 8; // TODO: is this always right?
const SCROLL_BACK: usize = 10_000;
+use crate::utils::logging::debug_log_to_file;
+
use crate::panes::terminal_character::{
CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
};
@@ -153,8 +155,13 @@ pub struct Grid {
lines_above: VecDeque<Row>,
viewport: Vec<Row>,
lines_below: Vec<Row>,
+ alternative_lines_above_viewport_and_cursor: Option<(VecDeque<Row>, Vec<Row>, Cursor)>,
cursor: Cursor,
scroll_region: Option<(usize, usize)>,
+ pending_styles: CharacterStyles,
+ pub should_render: bool,
+ pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "")
+ pub clear_viewport_before_rendering: bool,
pub width: usize,
pub height: usize,
}
@@ -182,6 +189,11 @@ impl Grid {
scroll_region: None,
width: columns,
height: rows,
+ pending_styles: CharacterStyles::new(),
+ should_render: true,
+ cursor_key_mode: false,
+ alternative_lines_above_viewport_and_cursor: None,
+ clear_viewport_before_rendering: false,
}
}
pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) {
@@ -441,7 +453,7 @@ impl Grid {
pub fn rotate_scroll_region_down(&mut self, _count: usize) {
// TBD
}
- pub fn add_canonical_line(&mut self, pad_character: TerminalCharacter) {
+ pub fn add_canonical_line(&mut self) {
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
if self.cursor.y == scroll_region_bottom {
// end of scroll region
@@ -451,7 +463,7 @@ impl Grid {
// controlling the scroll region (presumably filled by whatever comes next in the
// scroll buffer, but that's not something we control)
self.viewport.remove(scroll_region_top);
- let columns = vec![pad_character; self.width];
+ let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
self.viewport
.insert(scroll_region_bottom, Row::from_columns(columns).canonical());
return;
@@ -625,7 +637,7 @@ impl Grid {
self.cursor.y + count
};
for _ in 0..lines_to_add {
- self.add_canonical_line(pad_character);
+ self.add_canonical_line();
}
self.pad_lines_until(self.cursor.y, pad_character);
}
@@ -727,6 +739,308 @@ impl Grid {
.unwrap()
.append(&mut empty_space_to_append);
}
+ fn add_newline(&mut self) {
+ self.add_canonical_line();
+ self.mark_for_rerender();
+ }
+ pub fn mark_for_rerender(&mut self) {
+ self.should_render = true;
+ }
+ fn reset_terminal_state(&mut self) {
+ self.lines_above = VecDeque::with_capacity(SCROLL_BACK);
+ self.lines_below = vec![];
+ self.viewport = vec![Row::new().canonical()];
+ self.alternative_lines_above_viewport_and_cursor = None;
+ self.cursor_key_mode = false;
+ self.clear_viewport_before_rendering = true;
+ self.cursor = Cursor::new(0, 0);
+ }
+}
+
+impl vte::Perform for Grid {
+ fn print(&mut self, c: char) {
+ // apparently, building TerminalCharacter like this without a "new" method
+ // is a little faster
+ let terminal_character = TerminalCharacter {
+ character: c,
+ styles: self.pending_styles,
+ };
+ self.add_character(terminal_character);
+ }
+
+ fn execute(&mut self, byte: u8) {
+ match byte {
+ 8 => {
+ // backspace
+ self.move_cursor_backwards(1);
+ }
+ 9 => {
+ // tab
+ self.advance_to_next_tabstop(self.pending_styles);
+ }
+ 10 => {
+ // 0a, newline
+ self.add_newline();
+ }
+ 13 => {
+ // 0d, carriage return
+ self.move_cursor_to_beginning_of_line();
+ }
+ _ => {}
+ }
+ }
+
+ fn hook(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _c: char) {
+ // TBD
+ }
+
+ fn put(&mut self, _byte: u8) {
+ // TBD
+ }
+
+ fn unhook(&mut self) {
+ // TBD
+ }
+
+ fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {
+ // TBD
+ }
+
+ fn csi_dispatch(&mut self, params: &[i64], _intermediates: &[u8], _ignore: bool, c: char) {
+ if c == 'm' {
+ self.pending_styles.add_style_from_ansi_params(params);
+ } else if c == 'C' {
+ // move cursor forward
+ let move_by = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ self.move_cursor_forward_until_edge(move_by);
+ } else if c == 'K' {
+ // clear line (0 => right, 1 => left, 2 => all)
+ if params[0] == 0 {
+ let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
+ char_to_replace.styles = self.pending_styles;
+ self.replace_characters_in_line_after_cursor(char_to_replace);
+ } else if params[0] == 1 {
+ let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
+ char_to_replace.styles = self.pending_styles;
+ self.replace_characters_in_line_before_cursor(char_to_replace);
+ } else if params[0] == 2 {
+ self.clear_cursor_line();
+ }
+ } else if c == 'J' {
+ // clear all (0 => below, 1 => above, 2 => all, 3 => saved)
+ let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
+ char_to_replace.styles = self.pending_styles;
+ if params[0] == 0 {
+ self.clear_all_after_cursor(char_to_replace);
+ } else if params[0] == 2 {
+ self.clear_all(char_to_replace);
+ }
+ // TODO: implement 1
+ } else if c == 'H' {
+ // goto row/col
+ // we subtract 1 from the row/column because these are 1 indexed
+ // (except when they are 0, in which case they should be 1
+ // don't look at me, I don't make the rules)
+ let (row, col) = if params.len() == 1 {
+ if params[0] == 0 {
+ (0, params[0] as usize)
+ } else {
+ (params[0] as usize - 1, params[0] as usize)
+ }
+ } else if params[0] == 0 {
+ (0, params[1] as usize - 1)
+ } else {
+ (params[0] as usize - 1, params[1] as usize - 1)
+ };
+ let pad_character = EMPTY_TERMINAL_CHARACTER;
+ self.move_cursor_to(col, row, pad_character);
+ } else if c == 'A' {
+ // move cursor up until edge of screen
+ let move_up_count = if params[0] == 0 { 1 } else { params[0] };
+ self.move_cursor_up(move_up_count as usize);
+ } else if c == 'B' {
+ // move cursor down until edge of screen
+ let move_down_count = if params[0] == 0 { 1 } else { params[0] };
+ let pad_character = EMPTY_TERMINAL_CHARACTER;
+ self.move_cursor_down(move_down_count as usize, pad_character);
+ } else if c == 'D' {
+ let move_back_count = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ self.move_cursor_back(move_back_count);
+ } else if c == 'l' {
+ let first_intermediate_is_questionmark = match _intermediates.get(0) {
+ Some(b'?') => true,
+ None => false,
+ _ => false,
+ };
+ if first_intermediate_is_questionmark {
+ match params.get(0) {
+ Some(&1049) => {
+ if let Some((
+ alternative_lines_above,
+ alternative_viewport,
+ alternative_cursor,
+ )) = self.alternative_lines_above_viewport_and_cursor.as_mut()
+ {
+ std::mem::swap(&mut self.lines_above, alternative_lines_above);
+ std::mem::swap(&mut self.viewport, alternative_viewport);
+ std::mem::swap(&mut self.cursor, alternative_cursor);
+ }
+ self.alternative_lines_above_viewport_and_cursor = None;
+ self.clear_viewport_before_rendering = true;
+ self.change_size(self.height, self.width); // the alternative_viewport might have been of a different size...
+ self.mark_for_rerender();
+ }
+ Some(&25) => {
+ self.hide_cursor();
+ self.mark_for_rerender();
+ }
+ Some(&1) => {
+ self.cursor_key_mode = false;
+ }
+ _ => {}
+ };
+ }
+ } else if c == 'h' {
+ let first_intermediate_is_questionmark = match _intermediates.get(0) {
+ Some(b'?') => true,
+ None => false,
+ _ => false,
+ };
+ if first_intermediate_is_questionmark {
+ match params.get(0) {
+ Some(&25) => {
+ self.show_cursor();
+ self.mark_for_rerender();
+ }
+ Some(&1049) => {
+ let current_lines_above = std::mem::replace(
+ &mut self.lines_above,
+ VecDeque::with_capacity(SCROLL_BACK),
+ );
+ let current_viewport =
+ std::mem::replace(&mut self.viewport, vec![Row::new().canonical()]);
+ let current_cursor = std::mem::replace(&mut self.cursor, Cursor::new(0, 0));
+ self.alternative_lines_above_viewport_and_cursor =
+ Some((current_lines_above, current_viewport, current_cursor));
+ self.clear_viewport_before_rendering = true;
+ }
+ Some(&1) => {
+ self.cursor_key_mode = true;
+ }
+ _ => {}
+ };
+ }
+ } else if c == 'r' {
+ if params.len() > 1 {
+ // minus 1 because these are 1 indexed
+ let top_line_index = params[0] as usize - 1;
+ let bottom_line_index = params[1] as usize - 1;
+ self.set_scroll_region(top_line_index, bottom_line_index);
+ self.show_cursor();
+ } else {
+ self.clear_scroll_region();
+ }
+ } else if c == 'M' {
+ // delete lines if currently inside scroll region
+ let line_count_to_delete = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ let pad_character = EMPTY_TERMINAL_CHARACTER;
+ self.delete_lines_in_scroll_region(line_count_to_delete, pad_character);
+ } else if c == 'L' {
+ // insert blank lines if inside scroll region
+ let line_count_to_add = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ let pad_character = EMPTY_TERMINAL_CHARACTER;
+ self.add_empty_lines_in_scroll_region(line_count_to_add, pad_character);
+ } else if c == 'q' {
+ // ignore for now to run on mac
+ } else if c == 'G' {
+ let column = if params[0] == 0 {
+ 0
+ } else {
+ params[0] as usize - 1
+ };
+ self.move_cursor_to_column(column);
+ } else if c == 'd' {
+ // goto line
+ let line = if params[0] == 0 {
+ 1
+ } else {
+ // minus 1 because this is 1 indexed
+ params[0] as usize - 1
+ };
+ let pad_character = EMPTY_TERMINAL_CHARACTER;
+ self.move_cursor_to_line(line, pad_character);
+ } else if c == 'P' {
+ // erase characters
+ let count = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ self.erase_characters(count, self.pending_styles);
+ } else if c == 'X' {
+ // erase characters and replace with empty characters of current style
+ let count = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ self.replace_with_empty_chars(count, self.pending_styles);
+ } else if c == 'T' {
+ /*
+ * 124 54 T SD
+ * Scroll down, new lines inserted at top of screen
+ * [4T = Scroll down 4, bring previous lines back into view
+ */
+ let line_count: i64 = *params.get(0).expect("A number of lines was expected.");
+
+ if line_count >= 0 {
+ self.rotate_scroll_region_up(line_count as usize);
+ } else {
+ self.rotate_scroll_region_down(line_count.abs() as usize);
+ }
+ } else if c == 'S' {
+ // move scroll up
+ let count = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ let pad_character = EMPTY_TERMINAL_CHARACTER;
+ self.delete_lines_in_scroll_region(count, pad_character);
+ // TODO: since delete_lines_in_scroll_region also adds lines, is the below redundant?
+ self.add_empty_lines_in_scroll_region(count, pad_character);
+ } else {
+ let _ = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
+ }
+ }
+
+ fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
+ match (byte, intermediates.get(0)) {
+ (b'M', None) => {
+ self.move_cursor_up_with_scrolling(1);
+ }
+ (b'c', None) => {
+ self.reset_terminal_state();
+ }
+ _ => {}
+ }
+ }
}
#[derive(Clone)]
diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs
index fd9bbb07..5d63a100 100644
--- a/src/client/panes/terminal_pane.rs
+++ b/src/client/panes/terminal_pane.rs
@@ -8,7 +8,6 @@ use crate::panes::terminal_character::{
CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
};
use crate::pty_bus::VteBytes;
-use crate::utils::logging::debug_log_to_file;
#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)]
pub enum PaneId {
@@ -36,19 +35,14 @@ impl From<Winsize> for PositionAndSize {
}
}
-#[derive(Debug)]
pub struct TerminalPane {
pub grid: Grid,
- pub alternative_grid: Option<Grid>, // for 1049h/l instructions which tell us to switch between these two
pub pid: RawFd,
- pub should_render: bool,
pub selectable: bool,
pub position_and_size: PositionAndSize,
pub position_and_size_override: Option<PositionAndSize>,
- pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "")
pub max_height: Option<usize>,
- pending_styles: CharacterStyles,
- clear_viewport_before_rendering: bool,
+ vte_parser: vte::Parser,
}
impl Pane for TerminalPane {
@@ -84,9 +78,8 @@ impl Pane for TerminalPane {
self.reflow_lines();
}
fn handle_pty_bytes(&mut self, bytes: VteBytes) {
- let mut vte_parser = vte::Parser::new();
for byte in bytes.iter() {
- vte_parser.advance(self, *byte);
+ self.vte_parser.advance(&mut self.grid, *byte);
}
}
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
@@ -101,7 +94,7 @@ impl Pane for TerminalPane {
match input_bytes.as_slice() {
[27, 91, 68] => {
// left arrow
- if self.cursor_key_mode {
+ if self.grid.cursor_key_mode {
// please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
// some editors will not show this
return "OD".as_bytes().to_vec();
@@ -109,7 +102,7 @@ impl Pane for TerminalPane {
}
[27, 91, 67] => {
// right arrow
- if self.cursor_key_mode {
+ if self.grid.cursor_key_mode {
// please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
// some editors will not show this
return "OC".as_bytes().to_vec();
@@ -117,7 +110,7 @@ impl Pane for TerminalPane {
}
[27, 91, 65] => {
// up arrow
- if self.cursor_key_mode {
+ if self.grid.cursor_key_mode {
// please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
// some editors will not show this
return "OA".as_bytes().to_vec();
@@ -125,7 +118,7 @@ impl Pane for TerminalPane {
}
[27, 91, 66] => {
// down arrow
- if self.cursor_key_mode {
+ if self.grid.cursor_key_mode {
// please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
// some editors will not show this
return "OB".as_bytes().to_vec();
@@ -140,10 +133,10 @@ impl Pane for TerminalPane {
self.position_and_size_override
}
fn should_render(&self) -> bool {
- self.should_render
+ self.grid.should_render
}
fn set_should_render(&mut self, should_render: bool) {
- self.should_render = should_render;
+ self.grid.should_render = should_render;
}
fn selectable(&self) -> bool {
self.selectable
@@ -173,7 +166,7 @@ impl Pane for TerminalPane {
let buffer_lines = &self.read_buffer_as_lines();
let display_cols = self.get_columns();
let mut character_styles = CharacterStyles::new();
- if self.clear_viewport_before_rendering {
+ if self.grid.clear_viewport_before_rendering {
for line_index in 0..self.grid.height {
let x = self.get_x();
let y = self.get_y();
@@ -186,7 +179,7 @@ impl Pane for TerminalPane {
vte_output.push(EMPTY_TERMINAL_CHARACTER.character);
}
}
- self.clear_viewport_before_rendering = false;
+ self.grid.clear_viewport_before_rendering = false;
}
for (row, line) in buffer_lines.iter().enumerate() {
let x = self.get_x();
@@ -210,7 +203,7 @@ impl Pane for TerminalPane {
}
character_styles.clear();
}
- self.should_render = false;
+ self.grid.should_render = false;
Some(vte_output)
} else {
None
@@ -269,40 +262,31 @@ impl Pane for TerminalPane {
}
fn scroll_up(&mut self, count: usize) {
self.grid.move_viewport_up(count);
- self.mark_for_rerender();
+ self.grid.should_render = true;
}
fn scroll_down(&mut self, count: usize) {
self.grid.move_viewport_down(count);
- self.mark_for_rerender();
+ self.grid.should_render = true;
}
fn clear_scroll(&mut self) {
self.grid.reset_viewport();
- self.mark_for_rerender();
+ self.grid.should_render = true;
}
}
impl TerminalPane {
pub fn new(pid: RawFd, position_and_size: PositionAndSize) -> TerminalPane {
let grid = Grid::new(position_and_size.rows, position_and_size.columns);
- let pending_styles = CharacterStyles::new();
-
TerminalPane {
pid,
grid,
- alternative_grid: None,
- should_render: true,
selectable: true,
- pending_styles,
position_and_size,
position_and_size_override: None,
- cursor_key_mode: false,
- clear_viewport_before_rendering: false,
max_height: None,
+ vte_parser: vte::Parser::new(),
}
}
- pub fn mark_for_rerender(&mut self) {
- self.should_render = true;
- }
pub fn get_x(&self) -> usize {
match self.position_and_size_override {
Some(position_and_size_override) => position_and_size_override.x,
@@ -331,11 +315,7 @@ impl TerminalPane {
let rows = self.get_rows();
let columns = self.get_columns();
self.grid.change_size(rows, columns);
- if let Some(alternative_grid) = self.alternative_grid.as_mut() {
- alternative_grid.change_size(rows, columns);
- }
}
-
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
self.grid.as_character_lines()
}
@@ -344,322 +324,4 @@ impl TerminalPane {
// (x, y)
self.grid.cursor_coordinates()
}
- pub fn rotate_scroll_region_up(&mut self, count: usize) {
- self.grid.rotate_scroll_region_up(count);
- self.mark_for_rerender();
- }
- pub fn rotate_scroll_region_down(&mut self, count: usize) {
- self.grid.rotate_scroll_region_down(count);
- self.mark_for_rerender();
- }
- fn reset_terminal_state(&mut self) {
- let rows = self.get_rows();
- let columns = self.get_columns();
- self.grid = Grid::new(rows, columns);
- self.alternative_grid = None;
- self.cursor_key_mode = false;
- }
- fn add_newline(&mut self) {
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid.add_canonical_line(pad_character);
- self.mark_for_rerender();
- }
- fn move_to_beginning_of_line(&mut self) {
- self.grid.move_cursor_to_beginning_of_line();
- }
- fn move_cursor_backwards(&mut self, count: usize) {
- self.grid.move_cursor_backwards(count);
- }
- fn _reset_all_ansi_codes(&mut self) {
- self.pending_styles.clear();
- }
-}
-
-impl vte::Perform for TerminalPane {
- fn print(&mut self, c: char) {
- // apparently, building TerminalCharacter like this without a "new" method
- // is a little faster
- let terminal_character = TerminalCharacter {
- character: c,
- styles: self.pending_styles,
- };
- self.grid.add_character(terminal_character);
- }
-
- fn execute(&mut self, byte: u8) {
- match byte {
- 8 => {
- // backspace
- self.move_cursor_backwards(1);
- }
- 9 => {
- // tab
- self.grid.advance_to_next_tabstop(self.pending_styles);
- }
- 10 => {
- // 0a, newline
- self.add_newline();
- }
- 13 => {
- // 0d, carriage return
- self.move_to_beginning_of_line();
- }
- _ => {}
- }
- }
-
- fn hook(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _c: char) {
- // TBD
- }
-
- fn put(&mut self, _byte: u8) {
- // TBD
- }
-
- fn unhook(&mut self) {
- // TBD
- }
-
- fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {
- // TBD
- }
-
- fn csi_dispatch(&mut self, params: &[i64], _intermediates: &[u8], _ignore: bool, c: char) {
- if c == 'm' {
- self.pending_styles.add_style_from_ansi_params(params);
- } else if c == 'C' {
- // move cursor forward
- let move_by = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- self.grid.move_cursor_forward_until_edge(move_by);
- } else if c == 'K' {
- // clear line (0 => right, 1 => left, 2 => all)
- if params[0] == 0 {
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.pending_styles;
- self.grid
- .replace_characters_in_line_after_cursor(char_to_replace);
- } else if params[0] == 1 {
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.pending_styles;
- self.grid
- .replace_characters_in_line_before_cursor(char_to_replace);
- } else if params[0] == 2 {
- self.grid.clear_cursor_line();
- }
- } else if c == 'J' {
- // clear all (0 => below, 1 => above, 2 => all, 3 => saved)
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.pending_styles;
- if params[0] == 0 {
- self.grid.clear_all_after_cursor(char_to_replace);
- } else if params[0] == 2 {
- self.grid.clear_all(char_to_replace);
- }
- // TODO: implement 1
- } else if c == 'H' {
- // goto row/col
- // we subtract 1 from the row/column because these are 1 indexed
- // (except when they are 0, in which case they should be 1
- // don't look at me, I don't make the rules)
- let (row, col) = if params.len() == 1 {
- if params[0] == 0 {
- (0, params[0] as usize)
- } else {
- (params[0] as usize - 1, params[0] as usize)
- }
- } else if params[0] == 0 {
- (0, params[1] as usize - 1)
- } else {
- (params[0] as usize - 1, params[1] as usize - 1)
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid.move_cursor_to(col, row, pad_character);
- } else if c == 'A' {
- // move cursor up until edge of screen
- let move_up_count = if params[0] == 0 { 1 } else { params[0] };
- self.grid.move_cursor_up(move_up_count as usize);
- } else if c == 'B' {
- // move cursor down until edge of screen
- let move_down_count = if params[0] == 0 { 1 } else { params[0] };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid
- .move_cursor_down(move_down_count as usize, pad_character);
- } else if c == 'D' {
- let move_back_count = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- self.grid.move_cursor_back(move_back_count);
- } else if c == 'l' {
- let first_intermediate_is_questionmark = match _intermediates.get(0) {
- Some(b'?') => true,
- None => false,
- _ => false,
- };
- if first_intermediate_is_questionmark {
- match params.get(0) {
- Some(&1049) => {
- if let Some(alternative_grid) = self.alternative_grid.as_mut() {
- std::mem::swap(&mut self.grid, alternative_grid);
- }
- self.alternative_grid = None;
- self.clear_viewport_before_rendering = true;
- self.mark_for_rerender();
- }
- Some(&25) => {
- self.grid.hide_cursor();
- self.mark_for_rerender();
- }
- Some(&1) => {
- self.cursor_key_mode = false;
- }
- _ => {}
- };
- }
- } else if c == 'h' {
- let first_intermediate_is_questionmark = match _intermediates.get(0) {
- Some(b'?') => true,
- None => false,
- _ => false,
- };
- if first_intermediate_is_questionmark {
- match params.get(0) {
- Some(&25) => {
- self.grid.show_cursor();
- self.mark_for_rerender();
- }
- Some(&1049) => {
- let columns = self
- .position_and_size_override
- .map(|x| x.columns)
- .unwrap_or(self.position_and_size.columns);
- let rows = self
- .position_and_size_override
- .map(|x| x.rows)
- .unwrap_or(self.position_and_size.rows);
- let current_grid =
- std::mem::replace(&mut self.grid, Grid::new(rows, columns));
- self.alternative_grid = Some(current_grid);
- self.clear_viewport_before_rendering = true;
- }
- Some(&1) => {
- self.cursor_key_mode = true;
- }
- _ => {}
- };
- }
- } else if c == 'r' {
- if params.len() > 1 {
- // minus 1 because these are 1 indexed
- let top_line_index = params[0] as usize - 1;
- let bottom_line_index = params[1] as usize - 1;
- self.grid
- .set_scroll_region(top_line_index, bottom_line_index);
- self.grid.show_cursor();
- } else {
- self.grid.clear_scroll_region();
- }
- } else if c == 'M' {
- // delete lines if currently inside scroll region
- let line_count_to_delete = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid
- .delete_lines_in_scroll_region(line_count_to_delete, pad_character);
- } else if c == 'L' {
- // insert blank lines if inside scroll region
- let line_count_to_add = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid
- .add_empty_lines_in_scroll_region(line_count_to_add, pad_character);
- } else if c == 'q' {
- // ignore for now to run on mac
- } else if c == 'G' {
- let column = if params[0] == 0 {
- 0
- } else {
- params[0] as usize - 1
- };
- self.grid.move_cursor_to_column(column);
- } else if c == 'd' {
- // goto line
- let line = if params[0] == 0 {
- 1
- } else {
- // minus 1 because this is 1 indexed
- params[0] as usize - 1
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid.move_cursor_to_line(line, pad_character);
- } else if c == 'P' {
- // erase characters
- let count = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- self.grid.erase_characters(count, self.pending_styles);
- } else if c == 'X' {
- // erase characters and replace with empty characters of current style
- let count = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- self.grid
- .replace_with_empty_chars(count, self.pending_styles);
- } else if c == 'T' {
- /*
- * 124 54 T SD
- * Scroll down, new lines inserted at top of screen
- * [4T = Scroll down 4, bring previous lines back into view
- */
- let line_count: i64 = *params.get(0).expect("A number of lines was expected.");
-
- if line_count >= 0 {
- self.rotate_scroll_region_up(line_count as usize);
- } else {
- self.rotate_scroll_region_down(line_count.abs() as usize);
- }
- } else if c == 'S' {
- // move scroll up
- let count = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid
- .delete_lines_in_scroll_region(count, pad_character);
- // TODO: since delete_lines_in_scroll_region also adds lines, is the below redundant?
- self.grid
- .add_empty_lines_in_scroll_region(count, pad_character);
- } else {
- let _ = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
- }
- }
-
- fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
- match (byte, intermediates.get(0)) {
- (b'M', None) => {
- self.grid.move_cursor_up_with_scrolling(1);
- }
- (b'c', None) => {
- self.reset_terminal_state();
- }
- _ => {}
- }
- }
}
diff --git a/src/tests/utils.rs b/src/tests/utils.rs
index 915adfb0..a58e8610 100644
--- a/src/tests/utils.rs
+++ b/src/tests/utils.rs
@@ -12,7 +12,7 @@ pub fn get_output_frame_snapshots(
let mut snapshots = vec![];
for frame in output_frames.iter() {
for byte in frame.iter() {
- vte_parser.advance(&mut terminal_output, *byte);
+ vte_parser.advance(&mut terminal_output.grid, *byte);
}
let output_lines = terminal_output.read_buffer_as_lines();
let cursor_coordinates = terminal_output.cursor_coordinates();