use crate::tab::Pane; use ::nix::pty::Winsize; use ::std::os::unix::io::RawFd; use std::fmt::Debug; use crate::panes::grid::Grid; use crate::panes::terminal_character::{ CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER, }; use crate::pty_bus::VteBytes; #[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)] pub enum PaneId { Terminal(RawFd), Plugin(u32), // FIXME: Drop the trait object, make this a wrapper for the struct? } /// Contains the position and size of a [`Pane`], or more generally of any terminal, measured /// in character rows and columns. #[derive(Clone, Copy, Debug, Default)] pub struct PositionAndSize { pub x: usize, pub y: usize, pub rows: usize, pub columns: usize, } impl From for PositionAndSize { fn from(winsize: Winsize) -> PositionAndSize { PositionAndSize { columns: winsize.ws_col as usize, rows: winsize.ws_row as usize, ..Default::default() } } } pub struct TerminalPane { pub grid: Grid, pub pid: RawFd, pub selectable: bool, pub position_and_size: PositionAndSize, pub position_and_size_override: Option, pub max_height: Option, vte_parser: vte::Parser, } impl Pane for TerminalPane { fn x(&self) -> usize { self.get_x() } fn y(&self) -> usize { self.get_y() } fn rows(&self) -> usize { self.get_rows() } fn columns(&self) -> usize { self.get_columns() } fn reset_size_and_position_override(&mut self) { self.position_and_size_override = None; self.reflow_lines(); } fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) { self.position_and_size.columns = position_and_size.columns; self.position_and_size.rows = position_and_size.rows; self.reflow_lines(); } fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) { let position_and_size_override = PositionAndSize { x, y, rows: size.rows, columns: size.columns, }; self.position_and_size_override = Some(position_and_size_override); self.reflow_lines(); } fn handle_pty_bytes(&mut self, bytes: VteBytes) { for byte in bytes.iter() { self.vte_parser.advance(&mut self.grid, *byte); } } fn cursor_coordinates(&self) -> Option<(usize, usize)> { // (x, y) self.grid.cursor_coordinates() } fn adjust_input_to_terminal(&self, input_bytes: Vec) -> Vec { // there are some cases in which the terminal state means that input sent to it // needs to be adjusted. // here we match against those cases - if need be, we adjust the input and if not // we send back the original input match input_bytes.as_slice() { [27, 91, 68] => { // left arrow 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(); } } [27, 91, 67] => { // right arrow 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(); } } [27, 91, 65] => { // up arrow 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(); } } [27, 91, 66] => { // down arrow 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(); } } _ => {} }; input_bytes } fn position_and_size_override(&self) -> Option { self.position_and_size_override } fn should_render(&self) -> bool { self.grid.should_render } fn set_should_render(&mut self, should_render: bool) { self.grid.should_render = should_render; } fn selectable(&self) -> bool { self.selectable } fn set_selectable(&mut self, selectable: bool) { self.selectable = selectable; } fn set_max_height(&mut self, max_height: usize) { self.max_height = Some(max_height); } fn set_invisible_borders(&mut self, _invisible_borders: bool) { unimplemented!(); } fn max_height(&self) -> Option { self.max_height } fn render(&mut self) -> Option { // FIXME: // the below conditional is commented out because it causes several bugs: // 1. When panes are resized or tabs are switched the previous contents of the screen stick // around // 2. When there are wide characters in a pane, since we don't yet handle them properly, // the spill over to the pane to the right // if self.should_render || cfg!(test) { if true { let mut vte_output = String::new(); let buffer_lines = &self.read_buffer_as_lines(); let display_cols = self.get_columns(); let mut character_styles = CharacterStyles::new(); 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(); vte_output.push_str(&format!( "\u{1b}[{};{}H\u{1b}[m", y + line_index + 1, x + 1 )); // goto row/col and reset styles for _col_index in 0..self.grid.width { vte_output.push(EMPTY_TERMINAL_CHARACTER.character); } } self.grid.clear_viewport_before_rendering = false; } for (row, line) in buffer_lines.iter().enumerate() { let x = self.get_x(); let y = self.get_y(); vte_output.push_str(&format!("\u{1b}[{};{}H\u{1b}[m", y + row + 1, x + 1)); // goto row/col and reset styles for (col, t_character) in line.iter().enumerate() { if col < display_cols { // in some cases (eg. while resizing) some characters will spill over // before they are corrected by the shell (for the prompt) or by reflowing // lines if let Some(new_styles) = character_styles.update_and_return_diff(&t_character.styles) { // the terminal keeps the previous styles as long as we're in the same // line, so we only want to update the new styles here (this also // includes resetting previous styles as needed) vte_output.push_str(&new_styles.to_string()); } vte_output.push(t_character.character); } } character_styles.clear(); } self.grid.should_render = false; Some(vte_output) } else { None } } fn pid(&self) -> PaneId { PaneId::Terminal(self.pid) } fn reduce_height_down(&mut self, count: usize) { self.position_and_size.y += count; self.position_and_size.rows -= count; self.reflow_lines(); } fn increase_height_down(&mut self, count: usize) { self.position_and_size.rows += count; self.reflow_lines(); } fn increase_height_up(&mut self, count: usize) { self.position_and_size.y -= count; self.position_and_size.rows += count; self.reflow_lines(); } fn reduce_height_up(&mut self, count: usize) { self.position_and_size.rows -= count; self.reflow_lines(); } fn reduce_width_right(&mut self, count: usize) { self.position_and_size.x += count; self.position_and_size.columns -= count; self.reflow_lines(); } fn reduce_width_left(&mut self, count: usize) { self.position_and_size.columns -= count; self.reflow_lines(); } fn increase_width_left(&mut self, count: usize) { self.position_and_size.x -= count; self.position_and_size.columns += count; self.reflow_lines(); } fn increase_width_right(&mut self, count: usize) { self.position_and_size.columns += count; self.reflow_lines(); } fn push_down(&mut self, count: usize) { self.position_and_size.y += count; } fn push_right(&mut self, count: usize) { self.position_and_size.x += count; } fn pull_left(&mut self, count: usize) { self.position_and_size.x -= count; } fn pull_up(&mut self, count: usize) { self.position_and_size.y -= count; } fn scroll_up(&mut self, count: usize) { self.grid.move_viewport_up(count); self.grid.should_render = true; } fn scroll_down(&mut self, count: usize) { self.grid.move_viewport_down(count); self.grid.should_render = true; } fn clear_scroll(&mut self) { self.grid.reset_viewport(); 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); TerminalPane { pid, grid, selectable: true, position_and_size, position_and_size_override: None, max_height: None, vte_parser: vte::Parser::new(), } } pub fn get_x(&self) -> usize { match self.position_and_size_override { Some(position_and_size_override) => position_and_size_override.x, None => self.position_and_size.x as usize, } } pub fn get_y(&self) -> usize { match self.position_and_size_override { Some(position_and_size_override) => position_and_size_override.y, None => self.position_and_size.y as usize, } } pub fn get_columns(&self) -> usize { match &self.position_and_size_override.as_ref() { Some(position_and_size_override) => position_and_size_override.columns, None => self.position_and_size.columns as usize, } } pub fn get_rows(&self) -> usize { match &self.position_and_size_override.as_ref() { Some(position_and_size_override) => position_and_size_override.rows, None => self.position_and_size.rows as usize, } } fn reflow_lines(&mut self) { let rows = self.get_rows(); let columns = self.get_columns(); self.grid.change_size(rows, columns); } pub fn read_buffer_as_lines(&self) -> Vec> { self.grid.as_character_lines() } #[cfg(test)] pub fn cursor_coordinates(&self) -> Option<(usize, usize)> { // (x, y) self.grid.cursor_coordinates() } }