zellij/zellij-server/src/panes/terminal_pane.rs

342 lines
12 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use zellij_utils::{vte, zellij_tile};
use std::fmt::Debug;
use std::os::unix::io::RawFd;
use std::time::Instant;
use zellij_tile::data::Palette;
use zellij_utils::pane_size::PositionAndSize;
use crate::panes::{
grid::Grid,
terminal_character::{
CharacterStyles, CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
},
};
use crate::pty::VteBytes;
use crate::tab::Pane;
#[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?
}
pub struct TerminalPane {
pub grid: Grid,
pub pid: RawFd,
pub selectable: bool,
pub position_and_size: PositionAndSize,
pub position_and_size_override: Option<PositionAndSize>,
pub active_at: Instant,
pub colors: Palette,
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 = *position_and_size;
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,
cols: size.cols,
..Default::default()
};
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);
}
self.set_should_render(true);
}
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
// (x, y)
self.grid.cursor_coordinates()
}
fn adjust_input_to_terminal(&self, input_bytes: Vec<u8>) -> Vec<u8> {
// 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(&self) -> PositionAndSize {
self.position_and_size
}
fn position_and_size_override(&self) -> Option<PositionAndSize> {
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 render_full_viewport(&mut self) {
self.grid.render_full_viewport();
}
fn selectable(&self) -> bool {
self.selectable
}
fn set_selectable(&mut self, selectable: bool) {
self.selectable = selectable;
}
fn set_fixed_height(&mut self, fixed_height: usize) {
self.position_and_size.rows = fixed_height;
self.position_and_size.rows_fixed = true;
}
fn set_fixed_width(&mut self, fixed_width: usize) {
self.position_and_size.cols = fixed_width;
self.position_and_size.cols_fixed = true;
}
fn set_invisible_borders(&mut self, _invisible_borders: bool) {
unimplemented!();
}
fn render(&mut self) -> Option<String> {
if self.should_render() {
let mut vte_output = String::new();
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;
}
let max_width = self.columns();
for character_chunk in self.grid.read_changes() {
let pane_x = self.get_x();
let pane_y = self.get_y();
let chunk_absolute_x = pane_x + character_chunk.x;
let chunk_absolute_y = pane_y + character_chunk.y;
let terminal_characters = character_chunk.terminal_characters;
vte_output.push_str(&format!(
"\u{1b}[{};{}H\u{1b}[m",
chunk_absolute_y + 1,
chunk_absolute_x + 1
)); // goto row/col and reset styles
let mut chunk_width = character_chunk.x;
for t_character in terminal_characters {
chunk_width += t_character.width;
if chunk_width > max_width {
break;
}
if let Some(new_styles) =
character_styles.update_and_return_diff(&t_character.styles)
{
vte_output.push_str(&new_styles.to_string());
}
vte_output.push(t_character.character);
}
character_styles.clear();
}
self.set_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.cols -= count;
self.reflow_lines();
}
fn reduce_width_left(&mut self, count: usize) {
self.position_and_size.cols -= count;
self.reflow_lines();
}
fn increase_width_left(&mut self, count: usize) {
self.position_and_size.x -= count;
self.position_and_size.cols += count;
self.reflow_lines();
}
fn increase_width_right(&mut self, count: usize) {
self.position_and_size.cols += 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.set_should_render(true);
}
fn scroll_down(&mut self, count: usize) {
self.grid.move_viewport_down(count);
self.set_should_render(true);
}
fn clear_scroll(&mut self) {
self.grid.reset_viewport();
self.set_should_render(true);
}
fn active_at(&self) -> Instant {
self.active_at
}
fn set_active_at(&mut self, time: Instant) {
self.active_at = time;
}
fn cursor_shape_csi(&self) -> String {
match self.grid.cursor_shape() {
CursorShape::Block => "\u{1b}[0 q".to_string(),
CursorShape::BlinkingBlock => "\u{1b}[1 q".to_string(),
CursorShape::Underline => "\u{1b}[4 q".to_string(),
CursorShape::BlinkingUnderline => "\u{1b}[3 q".to_string(),
CursorShape::Beam => "\u{1b}[6 q".to_string(),
CursorShape::BlinkingBeam => "\u{1b}[5 q".to_string(),
}
}
fn drain_messages_to_pty(&mut self) -> Vec<Vec<u8>> {
self.grid.pending_messages_to_pty.drain(..).collect()
}
}
impl TerminalPane {
pub fn new(pid: RawFd, position_and_size: PositionAndSize, palette: Palette) -> TerminalPane {
let grid = Grid::new(position_and_size.rows, position_and_size.cols, palette);
TerminalPane {
pid,
grid,
selectable: true,
position_and_size,
position_and_size_override: None,
vte_parser: vte::Parser::new(),
active_at: Instant::now(),
colors: palette,
}
}
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.cols,
None => self.position_and_size.cols 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);
self.set_should_render(true);
}
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
self.grid.as_character_lines()
}
#[cfg(any(feature = "test", test))]
pub fn cursor_coordinates(&self) -> Option<(usize, usize)> {
// (x, y)
self.grid.cursor_coordinates()
}
}