summaryrefslogtreecommitdiffstats
path: root/zellij-server/src/panes/terminal_pane.rs
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2022-02-18 21:10:06 +0100
committerGitHub <noreply@github.com>2022-02-18 21:10:06 +0100
commit821e7cbc5a9711173061a272dc0378fff1fe5596 (patch)
treed7806a3d88dbf70d27996d9f85f66e93135670b2 /zellij-server/src/panes/terminal_pane.rs
parent10a22c479ffb4d76627eadbee2dc4b53ae4309c3 (diff)
feat(ui): add floating panes (#1066)
* basic functionality * close and reopen scratch terminal working * embed/float and resize whole tab for floating and static floating panes * move focus working * fix focus change in floating panes * move pane with mouse * floating z indices * tests and better resize algorithm * starting to work on performance * some performance experimentations * new render engine * reverse painters algorithm for floating panes * fix frame buffering * improve ux situation * handle multiple new panes on screen without overlap * adjust keybindings * adjust key hints * fix multiuser frame ui * fix various floating/multiuser bugs * remove stuff * wide characters under floating panes * fix wide character frame override * fix non-frame boundaries interactions with floating panes * fix selection character width * fix title frame wide char overflow * fix existing tests * add tests * refactor output out of tab * refactor floating panes out of tab * refactor tab * moar refactoring * refactorings and bring back terminal window title setting * add frame vte output * remove more unused stuff * remove even more unused stuff * you know the drill * refactor floating panes and remove more stuffs * refactor pane grids * remove unused output caching * refactor output * remove unused stuff * rustfmt * some formatting * rustfmt * reduce clippy to normal * remove comment * remove unused * fix closign pane * fix tests
Diffstat (limited to 'zellij-server/src/panes/terminal_pane.rs')
-rw-r--r--zellij-server/src/panes/terminal_pane.rs146
1 files changed, 47 insertions, 99 deletions
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index 728716b29..ee6fb559e 100644
--- a/zellij-server/src/panes/terminal_pane.rs
+++ b/zellij-server/src/panes/terminal_pane.rs
@@ -1,16 +1,17 @@
-use crate::panes::AnsiCode;
+use crate::output::CharacterChunk;
use crate::panes::{
grid::Grid,
- terminal_character::{
- CharacterStyles, CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
- },
+ terminal_character::{CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER},
};
+use crate::panes::{AnsiCode, LinkHandler};
use crate::pty::VteBytes;
use crate::tab::Pane;
use crate::ClientId;
+use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
-use std::fmt::{Debug, Write};
+use std::fmt::Debug;
use std::os::unix::io::RawFd;
+use std::rc::Rc;
use std::time::{self, Instant};
use zellij_utils::pane_size::Offset;
use zellij_utils::{
@@ -184,102 +185,42 @@ impl Pane for TerminalPane {
fn set_selectable(&mut self, selectable: bool) {
self.selectable = selectable;
}
- fn render(&mut self, _client_id: Option<ClientId>) -> Option<String> {
- // we don't use client_id because terminal panes render the same for all users
+ fn render(
+ &mut self,
+ _client_id: Option<ClientId>,
+ ) -> Option<(Vec<CharacterChunk>, Option<String>)> {
if self.should_render() {
- let mut vte_output = String::new();
- let mut character_styles = CharacterStyles::new();
+ let mut raw_vte_output = String::new();
let content_x = self.get_content_x();
let content_y = self.get_content_y();
- if self.grid.clear_viewport_before_rendering {
- for line_index in 0..self.grid.height {
- write!(
- &mut vte_output,
- "\u{1b}[{};{}H\u{1b}[m",
- content_y + line_index + 1,
- content_x + 1
- )
- .unwrap(); // 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;
- }
- // here we clear the previous cursor locations by adding an empty style-less character
- // in their location, this is done before the main rendering logic so that if there
- // actually is another character there, it will be overwritten
- for (y, x) in self.fake_cursor_locations.drain() {
- // we need to make sure to update the line in the line buffer so that if there's
- // another character there it'll override it and we won't create holes with our
- // empty character
- self.grid.update_line_for_rendering(y);
- let x = content_x + x;
- let y = content_y + y;
- write!(
- &mut vte_output,
- "\u{1b}[{};{}H\u{1b}[m{}",
- y + 1,
- x + 1,
- EMPTY_TERMINAL_CHARACTER.character
- )
- .unwrap();
- }
- let max_width = self.get_content_columns();
- for character_chunk in self.grid.read_changes() {
- let pane_x = self.get_content_x();
- let pane_y = self.get_content_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;
- write!(
- &mut vte_output,
- "\u{1b}[{};{}H\u{1b}[m",
- chunk_absolute_y + 1,
- chunk_absolute_x + 1
- )
- .unwrap(); // goto row/col and reset styles
-
- let mut chunk_width = character_chunk.x;
- for mut t_character in terminal_characters {
- // adjust the background of currently selected characters
- // doing it here is much easier than in grid
- if self.grid.selection.contains(character_chunk.y, chunk_width) {
- let color = match self.colors.bg {
- PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb),
- PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col),
- };
- t_character.styles = t_character.styles.background(Some(color));
- }
- 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, self.grid.changed_colors)
- {
- write!(
- &mut vte_output,
- "{}{}",
- new_styles,
- self.grid.link_handler.output_osc8(new_styles.link_anchor)
- )
- .unwrap();
- }
-
- vte_output.push(t_character.character);
+ let mut character_chunks = self.grid.read_changes(content_x, content_y);
+ for character_chunk in character_chunks.iter_mut() {
+ character_chunk.add_changed_colors(self.grid.changed_colors);
+ if self
+ .grid
+ .selection
+ .contains_row(character_chunk.y.saturating_sub(content_y))
+ {
+ let background_color = match self.colors.bg {
+ PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb),
+ PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col),
+ };
+ character_chunk.add_selection_and_background(
+ self.grid.selection,
+ background_color,
+ content_x,
+ content_y,
+ );
}
- character_styles.clear();
}
if self.grid.ring_bell {
let ring_bell = '\u{7}';
- vte_output.push(ring_bell);
+ raw_vte_output.push(ring_bell);
self.grid.ring_bell = false;
}
self.set_should_render(false);
- Some(vte_output)
+ Some((character_chunks, Some(raw_vte_output)))
} else {
None
}
@@ -289,9 +230,8 @@ impl Pane for TerminalPane {
client_id: ClientId,
frame_params: FrameParams,
input_mode: InputMode,
- ) -> Option<String> {
+ ) -> Option<(Vec<CharacterChunk>, Option<String>)> {
// TODO: remove the cursor stuff from here
- let mut vte_output = None;
let pane_title = if self.pane_name.is_empty()
&& input_mode == InputMode::RenamePane
&& frame_params.is_main_client
@@ -316,18 +256,24 @@ impl Pane for TerminalPane {
Some(last_frame) => {
if &frame != last_frame {
if !self.borderless {
- vte_output = Some(frame.render());
+ let frame_output = frame.render();
+ self.frame.insert(client_id, frame);
+ Some(frame_output)
+ } else {
+ None
}
- self.frame.insert(client_id, frame);
+ } else {
+ None
}
- vte_output
}
None => {
if !self.borderless {
- vte_output = Some(frame.render());
+ let frame_output = frame.render();
+ self.frame.insert(client_id, frame);
+ Some(frame_output)
+ } else {
+ None
}
- self.frame.insert(client_id, frame);
- vte_output
}
}
}
@@ -452,7 +398,7 @@ impl Pane for TerminalPane {
self.grid.pending_messages_to_pty.drain(..).collect()
}
- fn start_selection(&mut self, start: &Position, client_id: ClientId) {
+ fn start_selection(&mut self, start: &Position, _client_id: ClientId) {
self.grid.start_selection(start);
self.set_should_render(true);
}
@@ -511,12 +457,14 @@ impl TerminalPane {
palette: Palette,
pane_index: usize,
pane_name: String,
+ link_handler: Rc<RefCell<LinkHandler>>,
) -> TerminalPane {
let initial_pane_title = format!("Pane #{}", pane_index);
let grid = Grid::new(
position_and_size.rows.as_usize(),
position_and_size.cols.as_usize(),
palette,
+ link_handler.clone(),
);
TerminalPane {
frame: HashMap::new(),