refactor(plugins): change the data flow (#1934)
* refactor(plugins): do not block render loop * refactor(plugins): cleanup * style(fmt): rustfmt * fix(plugins): various rendering pipeline fixes
This commit is contained in:
parent
b2b5bdc564
commit
3d2a6d6a5a
22 changed files with 1055 additions and 458 deletions
681
Cargo.lock
generated
681
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -129,10 +129,10 @@ impl ZellijPlugin for State {
|
|||
};
|
||||
match background {
|
||||
PaletteColor::Rgb((r, g, b)) => {
|
||||
println!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b);
|
||||
print!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b);
|
||||
},
|
||||
PaletteColor::EightBit(color) => {
|
||||
println!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color);
|
||||
print!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color);
|
||||
},
|
||||
}
|
||||
self.should_render = false;
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ impl ZellijPlugin for State {
|
|||
}
|
||||
|
||||
if rows > 1 {
|
||||
println!("\u{1b}[m{}\u{1b}[0K", second_line);
|
||||
print!("\u{1b}[m{}\u{1b}[0K", second_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ impl ZellijPlugin for State {
|
|||
*self.scroll_mut() = self.selected() + 2 - rows;
|
||||
}
|
||||
|
||||
let is_last_row = i == rows.saturating_sub(1);
|
||||
let i = self.scroll() + i;
|
||||
if let Some(entry) = self.files.get(i) {
|
||||
let mut path = entry.as_line(cols).normal();
|
||||
|
|
@ -102,11 +103,19 @@ impl ZellijPlugin for State {
|
|||
}
|
||||
|
||||
if i == self.selected() {
|
||||
println!("{}", path.reversed());
|
||||
if is_last_row {
|
||||
print!("{}", path.reversed());
|
||||
} else {
|
||||
println!("{}", path.reversed());
|
||||
}
|
||||
} else {
|
||||
println!("{}", path);
|
||||
if is_last_row {
|
||||
print!("{}", path);
|
||||
} else {
|
||||
println!("{}", path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if !is_last_row {
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,10 +127,10 @@ impl ZellijPlugin for State {
|
|||
};
|
||||
match background {
|
||||
PaletteColor::Rgb((r, g, b)) => {
|
||||
println!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b);
|
||||
print!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b);
|
||||
},
|
||||
PaletteColor::EightBit(color) => {
|
||||
println!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color);
|
||||
print!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color);
|
||||
},
|
||||
}
|
||||
self.should_render = false;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ macro_rules! asset_map {
|
|||
|
||||
#[cfg(not(feature = "disable_automatic_asset_installation"))]
|
||||
pub(crate) fn populate_data_dir(data_dir: &Path) {
|
||||
// First run installation of default plugins & layouts
|
||||
let mut assets = asset_map! {
|
||||
"assets/plugins/compact-bar.wasm" => "plugins/compact-bar.wasm",
|
||||
"assets/plugins/status-bar.wasm" => "plugins/status-bar.wasm",
|
||||
|
|
|
|||
|
|
@ -847,7 +847,10 @@ pub fn resize_terminal_window() {
|
|||
name: "wait for terminal to be resized and app to be re-rendered",
|
||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||
let mut step_is_complete = false;
|
||||
if remote_terminal.cursor_position_is(53, 2) && remote_terminal.tip_appears() {
|
||||
if remote_terminal.cursor_position_is(53, 2)
|
||||
&& remote_terminal.tip_appears()
|
||||
&& remote_terminal.snapshot_contains("Ctrl +")
|
||||
{
|
||||
// size has been changed
|
||||
step_is_complete = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ daemonize = "0.4.1"
|
|||
serde_json = "1.0"
|
||||
unicode-width = "0.1.8"
|
||||
url = "2.2.2"
|
||||
wasmer = "1.0.0"
|
||||
wasmer-wasi = "1.0.0"
|
||||
wasmer = "2.3.0"
|
||||
wasmer-wasi = "2.3.0"
|
||||
cassowary = "0.3.0"
|
||||
zellij-utils = { path = "../zellij-utils/", version = "0.34.0" }
|
||||
log = "0.4.17"
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ impl Seek for LoggingPipe {
|
|||
}
|
||||
}
|
||||
|
||||
#[typetag::serde]
|
||||
impl WasiFile for LoggingPipe {
|
||||
fn last_accessed(&self) -> u64 {
|
||||
0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
mod floating_pane_grid;
|
||||
use zellij_utils::position::Position;
|
||||
|
||||
use crate::resize_pty;
|
||||
use crate::tab::Pane;
|
||||
use floating_pane_grid::FloatingPaneGrid;
|
||||
|
||||
|
|
@ -8,7 +9,9 @@ use crate::{
|
|||
os_input_output::ServerOsApi,
|
||||
output::{FloatingPanesStack, Output},
|
||||
panes::{ActivePanes, PaneId},
|
||||
thread_bus::ThreadSenders,
|
||||
ui::pane_contents_and_ui::PaneContentsAndUi,
|
||||
wasm_vm::PluginInstruction,
|
||||
ClientId,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
|
|
@ -22,22 +25,6 @@ use zellij_utils::{
|
|||
pane_size::{Offset, PaneGeom, Size, Viewport},
|
||||
};
|
||||
|
||||
macro_rules! resize_pty {
|
||||
($pane:expr, $os_input:expr) => {
|
||||
if let PaneId::Terminal(ref pid) = $pane.pid() {
|
||||
// FIXME: This `set_terminal_size_using_terminal_id` call would be best in
|
||||
// `TerminalPane::reflow_lines`
|
||||
$os_input.set_terminal_size_using_terminal_id(
|
||||
*pid,
|
||||
$pane.get_content_columns() as u16,
|
||||
$pane.get_content_rows() as u16,
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct FloatingPanes {
|
||||
panes: BTreeMap<PaneId, Box<dyn Pane>>,
|
||||
display_area: Rc<RefCell<Size>>,
|
||||
|
|
@ -53,6 +40,7 @@ pub struct FloatingPanes {
|
|||
active_panes: ActivePanes,
|
||||
show_panes: bool,
|
||||
pane_being_moved_with_mouse: Option<(PaneId, Position)>,
|
||||
senders: ThreadSenders,
|
||||
}
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
|
|
@ -68,6 +56,7 @@ impl FloatingPanes {
|
|||
default_mode_info: ModeInfo,
|
||||
style: Style,
|
||||
os_input: Box<dyn ServerOsApi>,
|
||||
senders: ThreadSenders,
|
||||
) -> Self {
|
||||
FloatingPanes {
|
||||
panes: BTreeMap::new(),
|
||||
|
|
@ -84,6 +73,7 @@ impl FloatingPanes {
|
|||
show_panes: false,
|
||||
active_panes: ActivePanes::new(&os_input),
|
||||
pane_being_moved_with_mouse: None,
|
||||
senders,
|
||||
}
|
||||
}
|
||||
pub fn stack(&self) -> Option<FloatingPanesStack> {
|
||||
|
|
@ -241,7 +231,7 @@ impl FloatingPanes {
|
|||
} else {
|
||||
pane.set_content_offset(Offset::default());
|
||||
}
|
||||
resize_pty!(pane, os_api).unwrap();
|
||||
resize_pty!(pane, os_api, self.senders).unwrap();
|
||||
}
|
||||
}
|
||||
pub fn render(&mut self, output: &mut Output) -> Result<()> {
|
||||
|
|
@ -321,7 +311,7 @@ impl FloatingPanes {
|
|||
}
|
||||
pub fn resize_pty_all_panes(&mut self, os_api: &mut Box<dyn ServerOsApi>) {
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, os_api).unwrap();
|
||||
resize_pty!(pane, os_api, self.senders).unwrap();
|
||||
}
|
||||
}
|
||||
pub fn resize_active_pane_left(
|
||||
|
|
@ -341,7 +331,7 @@ impl FloatingPanes {
|
|||
);
|
||||
floating_pane_grid.resize_pane_left(active_floating_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, os_api).unwrap();
|
||||
resize_pty!(pane, os_api, self.senders).unwrap();
|
||||
}
|
||||
self.set_force_render();
|
||||
return true;
|
||||
|
|
@ -365,7 +355,7 @@ impl FloatingPanes {
|
|||
);
|
||||
floating_pane_grid.resize_pane_right(active_floating_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, os_api).unwrap();
|
||||
resize_pty!(pane, os_api, self.senders).unwrap();
|
||||
}
|
||||
self.set_force_render();
|
||||
return true;
|
||||
|
|
@ -389,7 +379,7 @@ impl FloatingPanes {
|
|||
);
|
||||
floating_pane_grid.resize_pane_down(active_floating_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, os_api).unwrap();
|
||||
resize_pty!(pane, os_api, self.senders).unwrap();
|
||||
}
|
||||
self.set_force_render();
|
||||
return true;
|
||||
|
|
@ -413,7 +403,7 @@ impl FloatingPanes {
|
|||
);
|
||||
floating_pane_grid.resize_pane_up(active_floating_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, os_api).unwrap();
|
||||
resize_pty!(pane, os_api, self.senders).unwrap();
|
||||
}
|
||||
self.set_force_render();
|
||||
return true;
|
||||
|
|
@ -437,7 +427,7 @@ impl FloatingPanes {
|
|||
);
|
||||
floating_pane_grid.resize_increase(active_floating_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, os_api).unwrap();
|
||||
resize_pty!(pane, os_api, self.senders).unwrap();
|
||||
}
|
||||
self.set_force_render();
|
||||
return true;
|
||||
|
|
@ -461,7 +451,7 @@ impl FloatingPanes {
|
|||
);
|
||||
floating_pane_grid.resize_decrease(active_floating_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, os_api).unwrap();
|
||||
resize_pty!(pane, os_api, self.senders).unwrap();
|
||||
}
|
||||
self.set_force_render();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ use std::cell::RefCell;
|
|||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
use zellij_utils::data::Style;
|
||||
use zellij_utils::errors::prelude::*;
|
||||
use zellij_utils::regex::Regex;
|
||||
|
||||
use std::{
|
||||
|
|
@ -549,7 +551,7 @@ impl Grid {
|
|||
pub fn cursor_shape(&self) -> CursorShape {
|
||||
self.cursor.get_shape()
|
||||
}
|
||||
pub fn scrollback_position_and_length(&mut self) -> (usize, usize) {
|
||||
pub fn scrollback_position_and_length(&self) -> (usize, usize) {
|
||||
// (position, length)
|
||||
(
|
||||
self.lines_below.len(),
|
||||
|
|
@ -979,6 +981,71 @@ impl Grid {
|
|||
|
||||
(changed_character_chunks, changed_sixel_image_chunks)
|
||||
}
|
||||
pub fn render(
|
||||
&mut self,
|
||||
content_x: usize,
|
||||
content_y: usize,
|
||||
style: &Style,
|
||||
) -> Result<Option<(Vec<CharacterChunk>, Option<String>, Vec<SixelImageChunk>)>> {
|
||||
let mut raw_vte_output = String::new();
|
||||
|
||||
let (mut character_chunks, sixel_image_chunks) = self.read_changes(content_x, content_y);
|
||||
for character_chunk in character_chunks.iter_mut() {
|
||||
character_chunk.add_changed_colors(self.changed_colors);
|
||||
if self
|
||||
.selection
|
||||
.contains_row(character_chunk.y.saturating_sub(content_y))
|
||||
{
|
||||
let background_color = match style.colors.bg {
|
||||
PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb),
|
||||
PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col),
|
||||
};
|
||||
character_chunk.add_selection_and_colors(
|
||||
self.selection,
|
||||
background_color,
|
||||
None,
|
||||
content_x,
|
||||
content_y,
|
||||
);
|
||||
} else if !self.search_results.selections.is_empty() {
|
||||
for res in self.search_results.selections.iter() {
|
||||
if res.contains_row(character_chunk.y.saturating_sub(content_y)) {
|
||||
let (select_background_palette, select_foreground_palette) =
|
||||
if Some(res) == self.search_results.active.as_ref() {
|
||||
(style.colors.orange, style.colors.black)
|
||||
} else {
|
||||
(style.colors.green, style.colors.black)
|
||||
};
|
||||
let background_color = match select_background_palette {
|
||||
PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb),
|
||||
PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col),
|
||||
};
|
||||
let foreground_color = match select_foreground_palette {
|
||||
PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb),
|
||||
PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col),
|
||||
};
|
||||
character_chunk.add_selection_and_colors(
|
||||
*res,
|
||||
background_color,
|
||||
Some(foreground_color),
|
||||
content_x,
|
||||
content_y,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.ring_bell {
|
||||
let ring_bell = '\u{7}';
|
||||
raw_vte_output.push(ring_bell);
|
||||
self.ring_bell = false;
|
||||
}
|
||||
return Ok(Some((
|
||||
character_chunks,
|
||||
Some(raw_vte_output),
|
||||
sixel_image_chunks,
|
||||
)));
|
||||
}
|
||||
pub fn cursor_coordinates(&self) -> Option<(usize, usize)> {
|
||||
if self.cursor_is_hidden {
|
||||
None
|
||||
|
|
@ -1959,6 +2026,14 @@ impl Grid {
|
|||
None
|
||||
}
|
||||
}
|
||||
pub fn delete_viewport_and_scroll(&mut self) {
|
||||
self.lines_above.clear();
|
||||
self.viewport.clear();
|
||||
self.lines_below.clear();
|
||||
}
|
||||
pub fn reset_cursor_position(&mut self) {
|
||||
self.cursor = Cursor::new(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Perform for Grid {
|
||||
|
|
@ -2563,7 +2638,7 @@ impl Perform for Grid {
|
|||
};
|
||||
if first_intermediate_is_questionmark {
|
||||
let query_type = params_iter.next();
|
||||
let is_query = params_iter.next() == Some(&[1]);
|
||||
let is_query = matches!(params_iter.next(), Some(&[1]));
|
||||
if is_query {
|
||||
// XTSMGRAPHICS
|
||||
match query_type {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,50 @@
|
|||
use std::fmt::Write;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::output::{CharacterChunk, SixelImageChunk};
|
||||
use crate::panes::PaneId;
|
||||
use crate::panes::{grid::Grid, sixel::SixelImageStore, LinkHandler, PaneId};
|
||||
use crate::pty::VteBytes;
|
||||
use crate::tab::Pane;
|
||||
use crate::ui::pane_boundaries_frame::{FrameParams, PaneFrame};
|
||||
use crate::wasm_vm::PluginInstruction;
|
||||
use crate::ClientId;
|
||||
use zellij_utils::pane_size::Offset;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use zellij_utils::pane_size::{Offset, SizeInPixels};
|
||||
use zellij_utils::position::Position;
|
||||
use zellij_utils::shared::ansi_len;
|
||||
use zellij_utils::{
|
||||
channels::SenderWithContext,
|
||||
data::{Event, InputMode, Mouse, PaletteColor},
|
||||
data::{Event, InputMode, Mouse, Palette, PaletteColor, Style},
|
||||
errors::prelude::*,
|
||||
pane_size::{Dimension, PaneGeom},
|
||||
shared::make_terminal_title,
|
||||
vte,
|
||||
};
|
||||
|
||||
macro_rules! get_or_create_grid {
|
||||
($self:ident, $client_id:ident) => {{
|
||||
let rows = $self.get_content_rows();
|
||||
let cols = $self.get_content_columns();
|
||||
|
||||
$self.grids.entry($client_id).or_insert_with(|| {
|
||||
let mut grid = Grid::new(
|
||||
rows,
|
||||
cols,
|
||||
$self.terminal_emulator_colors.clone(),
|
||||
$self.terminal_emulator_color_codes.clone(),
|
||||
$self.link_handler.clone(),
|
||||
$self.character_cell_size.clone(),
|
||||
$self.sixel_image_store.clone(),
|
||||
);
|
||||
grid.hide_cursor();
|
||||
grid
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) struct PluginPane {
|
||||
pub pid: u32,
|
||||
pub should_render: bool,
|
||||
pub should_render: HashMap<ClientId, bool>,
|
||||
pub selectable: bool,
|
||||
pub geom: PaneGeom,
|
||||
pub geom_override: Option<PaneGeom>,
|
||||
|
|
@ -31,8 +53,16 @@ pub(crate) struct PluginPane {
|
|||
pub active_at: Instant,
|
||||
pub pane_title: String,
|
||||
pub pane_name: String,
|
||||
pub style: Style,
|
||||
sixel_image_store: Rc<RefCell<SixelImageStore>>,
|
||||
terminal_emulator_colors: Rc<RefCell<Palette>>,
|
||||
terminal_emulator_color_codes: Rc<RefCell<HashMap<usize, String>>>,
|
||||
link_handler: Rc<RefCell<LinkHandler>>,
|
||||
character_cell_size: Rc<RefCell<Option<SizeInPixels>>>,
|
||||
vte_parsers: HashMap<ClientId, vte::Parser>,
|
||||
grids: HashMap<ClientId, Grid>,
|
||||
prev_pane_name: String,
|
||||
frame: bool,
|
||||
frame: HashMap<ClientId, PaneFrame>,
|
||||
borderless: bool,
|
||||
}
|
||||
|
||||
|
|
@ -43,21 +73,35 @@ impl PluginPane {
|
|||
send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
||||
title: String,
|
||||
pane_name: String,
|
||||
sixel_image_store: Rc<RefCell<SixelImageStore>>,
|
||||
terminal_emulator_colors: Rc<RefCell<Palette>>,
|
||||
terminal_emulator_color_codes: Rc<RefCell<HashMap<usize, String>>>,
|
||||
link_handler: Rc<RefCell<LinkHandler>>,
|
||||
character_cell_size: Rc<RefCell<Option<SizeInPixels>>>,
|
||||
style: Style,
|
||||
) -> Self {
|
||||
Self {
|
||||
pid,
|
||||
should_render: true,
|
||||
should_render: HashMap::new(),
|
||||
selectable: true,
|
||||
geom: position_and_size,
|
||||
geom_override: None,
|
||||
send_plugin_instructions,
|
||||
active_at: Instant::now(),
|
||||
frame: false,
|
||||
frame: HashMap::new(),
|
||||
content_offset: Offset::default(),
|
||||
pane_title: title,
|
||||
borderless: false,
|
||||
pane_name: pane_name.clone(),
|
||||
prev_pane_name: pane_name,
|
||||
terminal_emulator_colors,
|
||||
terminal_emulator_color_codes,
|
||||
link_handler,
|
||||
character_cell_size,
|
||||
sixel_image_store,
|
||||
vte_parsers: HashMap::new(),
|
||||
grids: HashMap::new(),
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -98,18 +142,37 @@ impl Pane for PluginPane {
|
|||
}
|
||||
fn reset_size_and_position_override(&mut self) {
|
||||
self.geom_override = None;
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
fn set_geom(&mut self, position_and_size: PaneGeom) {
|
||||
self.geom = position_and_size;
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
fn set_geom_override(&mut self, pane_geom: PaneGeom) {
|
||||
self.geom_override = Some(pane_geom);
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
fn handle_pty_bytes(&mut self, _event: VteBytes) {
|
||||
// noop
|
||||
fn handle_plugin_bytes(&mut self, client_id: ClientId, bytes: VteBytes) {
|
||||
self.set_client_should_render(client_id, true);
|
||||
let grid = get_or_create_grid!(self, client_id);
|
||||
|
||||
// this is part of the plugin contract, whenever we update the plugin and call its render function, we delete the existing viewport
|
||||
// and scroll, reset the cursor position and make sure all the viewport is rendered
|
||||
grid.delete_viewport_and_scroll();
|
||||
grid.reset_cursor_position();
|
||||
grid.render_full_viewport();
|
||||
|
||||
let vte_parser = self
|
||||
.vte_parsers
|
||||
.entry(client_id)
|
||||
.or_insert_with(|| vte::Parser::new());
|
||||
for &byte in &bytes {
|
||||
vte_parser.advance(grid, byte);
|
||||
}
|
||||
self.should_render.insert(client_id, true);
|
||||
}
|
||||
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
|
||||
None
|
||||
|
|
@ -124,10 +187,21 @@ impl Pane for PluginPane {
|
|||
self.geom_override
|
||||
}
|
||||
fn should_render(&self) -> bool {
|
||||
self.should_render
|
||||
// set should_render for all clients
|
||||
self.should_render.values().any(|v| *v)
|
||||
}
|
||||
fn set_should_render(&mut self, should_render: bool) {
|
||||
self.should_render = should_render;
|
||||
self.should_render
|
||||
.values_mut()
|
||||
.for_each(|v| *v = should_render);
|
||||
}
|
||||
fn render_full_viewport(&mut self) {
|
||||
// this marks the pane for a full re-render, rather than just rendering the
|
||||
// diff as it usually does with the OutputBuffer
|
||||
self.frame.clear();
|
||||
for grid in self.grids.values_mut() {
|
||||
grid.render_full_viewport();
|
||||
}
|
||||
}
|
||||
fn selectable(&self) -> bool {
|
||||
self.selectable
|
||||
|
|
@ -139,127 +213,86 @@ impl Pane for PluginPane {
|
|||
&mut self,
|
||||
client_id: Option<ClientId>,
|
||||
) -> Result<Option<(Vec<CharacterChunk>, Option<String>, Vec<SixelImageChunk>)>> {
|
||||
// this is a bit of a hack but works in a pinch
|
||||
let client_id = match client_id {
|
||||
Some(id) => id,
|
||||
None => return Ok(None),
|
||||
};
|
||||
// if self.should_render {
|
||||
if true {
|
||||
let err_context = || format!("failed to render plugin panes for client {client_id}");
|
||||
|
||||
// while checking should_render rather than rendering each pane every time
|
||||
// is more performant, it causes some problems when the pane to the left should be
|
||||
// rendered and has wide characters (eg. Chinese characters or emoji)
|
||||
// as a (hopefully) temporary hack, we render all panes until we find a better solution
|
||||
let mut vte_output = String::new();
|
||||
let (buf_tx, buf_rx) = channel();
|
||||
|
||||
self.send_plugin_instructions
|
||||
.send(PluginInstruction::Render(
|
||||
buf_tx,
|
||||
self.pid,
|
||||
client_id,
|
||||
self.get_content_rows(),
|
||||
self.get_content_columns(),
|
||||
))
|
||||
.to_anyhow()
|
||||
.with_context(err_context)?;
|
||||
|
||||
self.should_render = false;
|
||||
// This is where we receive the text to render from the plugins.
|
||||
let contents = buf_rx
|
||||
.recv()
|
||||
.with_context(err_context)
|
||||
.to_log()
|
||||
.unwrap_or("No output from plugin received. See logs".to_string());
|
||||
for (index, line) in contents.lines().enumerate() {
|
||||
let actual_len = ansi_len(line);
|
||||
let line_to_print = if actual_len > self.get_content_columns() {
|
||||
let mut line = String::from(line);
|
||||
line.truncate(self.get_content_columns());
|
||||
line
|
||||
} else {
|
||||
[
|
||||
line,
|
||||
&str::repeat(" ", self.get_content_columns() - ansi_len(line)),
|
||||
]
|
||||
.concat()
|
||||
};
|
||||
|
||||
write!(
|
||||
&mut vte_output,
|
||||
"\u{1b}[{};{}H\u{1b}[m{}",
|
||||
self.get_content_y() + 1 + index,
|
||||
self.get_content_x() + 1,
|
||||
line_to_print,
|
||||
)
|
||||
.with_context(err_context)?; // goto row/col and reset styles
|
||||
let line_len = line_to_print.len();
|
||||
if line_len < self.get_content_columns() {
|
||||
// pad line
|
||||
for _ in line_len..self.get_content_columns() {
|
||||
vte_output.push(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
let total_line_count = contents.lines().count();
|
||||
if total_line_count < self.get_content_rows() {
|
||||
// pad lines
|
||||
for line_index in total_line_count..self.get_content_rows() {
|
||||
let x = self.get_content_x();
|
||||
let y = self.get_content_y();
|
||||
write!(
|
||||
&mut vte_output,
|
||||
"\u{1b}[{};{}H\u{1b}[m",
|
||||
y + line_index + 1,
|
||||
x + 1
|
||||
)
|
||||
.with_context(err_context)?; // goto row/col and reset styles
|
||||
for _col_index in 0..self.get_content_columns() {
|
||||
vte_output.push(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some((vec![], Some(vte_output), vec![]))) // TODO: PluginPanes should have their own grid so that we can return the non-serialized TerminalCharacters and have them participate in the render buffer
|
||||
} else {
|
||||
Ok(None)
|
||||
if client_id.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
if let Some(client_id) = client_id {
|
||||
if self.should_render.get(&client_id).copied().unwrap_or(false) {
|
||||
let content_x = self.get_content_x();
|
||||
let content_y = self.get_content_y();
|
||||
if let Some(grid) = self.grids.get_mut(&client_id) {
|
||||
match grid.render(content_x, content_y, &self.style) {
|
||||
Ok(rendered_assets) => {
|
||||
self.should_render.insert(client_id, false);
|
||||
return Ok(rendered_assets);
|
||||
},
|
||||
e => return e,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
fn render_frame(
|
||||
&mut self,
|
||||
_client_id: ClientId,
|
||||
client_id: ClientId,
|
||||
frame_params: FrameParams,
|
||||
input_mode: InputMode,
|
||||
) -> Result<Option<(Vec<CharacterChunk>, Option<String>)>> {
|
||||
// FIXME: This is a hack that assumes all fixed-size panes are borderless. This
|
||||
// will eventually need fixing!
|
||||
let res = if self.frame && !(self.geom.rows.is_fixed() || self.geom.cols.is_fixed()) {
|
||||
if self.borderless {
|
||||
return Ok(None);
|
||||
}
|
||||
if let Some(grid) = self.grids.get(&client_id) {
|
||||
let err_context = || format!("failed to render frame for client {client_id}");
|
||||
let pane_title = if self.pane_name.is_empty()
|
||||
&& input_mode == InputMode::RenamePane
|
||||
&& frame_params.is_main_client
|
||||
{
|
||||
String::from("Enter name...")
|
||||
} else if self.pane_name.is_empty() {
|
||||
self.pane_title.clone()
|
||||
grid.title
|
||||
.clone()
|
||||
.unwrap_or_else(|| self.pane_title.clone())
|
||||
} else {
|
||||
self.pane_name.clone()
|
||||
};
|
||||
|
||||
let frame = PaneFrame::new(
|
||||
self.current_geom().into(),
|
||||
(0, 0), // scroll position
|
||||
grid.scrollback_position_and_length(),
|
||||
pane_title,
|
||||
frame_params,
|
||||
);
|
||||
Some(
|
||||
frame
|
||||
.render()
|
||||
.with_context(|| format!("failed to render frame for client {_client_id}"))?,
|
||||
)
|
||||
|
||||
let res = match self.frame.get(&client_id) {
|
||||
// TODO: use and_then or something?
|
||||
Some(last_frame) => {
|
||||
if &frame != last_frame {
|
||||
if !self.borderless {
|
||||
let frame_output = frame.render().with_context(err_context)?;
|
||||
self.frame.insert(client_id, frame);
|
||||
Some(frame_output)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
None => {
|
||||
if !self.borderless {
|
||||
let frame_output = frame.render().with_context(err_context)?;
|
||||
self.frame.insert(client_id, frame);
|
||||
Some(frame_output)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(res)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(res)
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
fn render_fake_cursor(
|
||||
&mut self,
|
||||
|
|
@ -298,42 +331,50 @@ impl Pane for PluginPane {
|
|||
fn reduce_height(&mut self, percent: f64) {
|
||||
if let Some(p) = self.geom.rows.as_percent() {
|
||||
self.geom.rows = Dimension::percent(p - percent);
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
}
|
||||
fn increase_height(&mut self, percent: f64) {
|
||||
if let Some(p) = self.geom.rows.as_percent() {
|
||||
self.geom.rows = Dimension::percent(p + percent);
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
}
|
||||
fn reduce_width(&mut self, percent: f64) {
|
||||
if let Some(p) = self.geom.cols.as_percent() {
|
||||
self.geom.cols = Dimension::percent(p - percent);
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
}
|
||||
fn increase_width(&mut self, percent: f64) {
|
||||
if let Some(p) = self.geom.cols.as_percent() {
|
||||
self.geom.cols = Dimension::percent(p + percent);
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
}
|
||||
fn push_down(&mut self, count: usize) {
|
||||
self.geom.y += count;
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
fn push_right(&mut self, count: usize) {
|
||||
self.geom.x += count;
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
fn pull_left(&mut self, count: usize) {
|
||||
self.geom.x -= count;
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
fn pull_up(&mut self, count: usize) {
|
||||
self.geom.y -= count;
|
||||
self.should_render = true;
|
||||
self.resize_grids();
|
||||
self.set_should_render(true);
|
||||
}
|
||||
fn scroll_up(&mut self, count: usize, client_id: ClientId) {
|
||||
self.send_plugin_instructions
|
||||
|
|
@ -394,11 +435,12 @@ impl Pane for PluginPane {
|
|||
fn set_active_at(&mut self, time: Instant) {
|
||||
self.active_at = time;
|
||||
}
|
||||
fn set_frame(&mut self, frame: bool) {
|
||||
self.frame = frame;
|
||||
fn set_frame(&mut self, _frame: bool) {
|
||||
self.frame.clear();
|
||||
}
|
||||
fn set_content_offset(&mut self, offset: Offset) {
|
||||
self.content_offset = offset;
|
||||
self.resize_grids();
|
||||
}
|
||||
|
||||
fn store_pane_name(&mut self) {
|
||||
|
|
@ -428,3 +470,17 @@ impl Pane for PluginPane {
|
|||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginPane {
|
||||
fn resize_grids(&mut self) {
|
||||
let content_rows = self.get_content_rows();
|
||||
let content_columns = self.get_content_columns();
|
||||
for grid in self.grids.values_mut() {
|
||||
grid.change_size(content_rows, content_columns);
|
||||
}
|
||||
self.set_should_render(true);
|
||||
}
|
||||
fn set_client_should_render(&mut self, client_id: ClientId, should_render: bool) {
|
||||
self.should_render.insert(client_id, should_render);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::output::{CharacterChunk, SixelImageChunk};
|
||||
use crate::panes::sixel::SixelImageStore;
|
||||
use crate::panes::LinkHandler;
|
||||
use crate::panes::{
|
||||
grid::Grid,
|
||||
terminal_character::{render_first_run_banner, TerminalCharacter, EMPTY_TERMINAL_CHARACTER},
|
||||
};
|
||||
use crate::panes::{AnsiCode, LinkHandler};
|
||||
use crate::pty::VteBytes;
|
||||
use crate::tab::{AdjustedInput, Pane};
|
||||
use crate::ClientId;
|
||||
|
|
@ -280,69 +280,15 @@ impl Pane for TerminalPane {
|
|||
_client_id: Option<ClientId>,
|
||||
) -> Result<Option<(Vec<CharacterChunk>, Option<String>, Vec<SixelImageChunk>)>> {
|
||||
if self.should_render() {
|
||||
let mut raw_vte_output = String::new();
|
||||
let content_x = self.get_content_x();
|
||||
let content_y = self.get_content_y();
|
||||
|
||||
let (mut character_chunks, sixel_image_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.style.colors.bg {
|
||||
PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb),
|
||||
PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col),
|
||||
};
|
||||
character_chunk.add_selection_and_colors(
|
||||
self.grid.selection,
|
||||
background_color,
|
||||
None,
|
||||
content_x,
|
||||
content_y,
|
||||
);
|
||||
} else if !self.grid.search_results.selections.is_empty() {
|
||||
for res in self.grid.search_results.selections.iter() {
|
||||
if res.contains_row(character_chunk.y.saturating_sub(content_y)) {
|
||||
let (select_background_palette, select_foreground_palette) =
|
||||
if Some(res) == self.grid.search_results.active.as_ref() {
|
||||
(self.style.colors.orange, self.style.colors.black)
|
||||
} else {
|
||||
(self.style.colors.green, self.style.colors.black)
|
||||
};
|
||||
let background_color = match select_background_palette {
|
||||
PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb),
|
||||
PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col),
|
||||
};
|
||||
let foreground_color = match select_foreground_palette {
|
||||
PaletteColor::Rgb(rgb) => AnsiCode::RgbCode(rgb),
|
||||
PaletteColor::EightBit(col) => AnsiCode::ColorIndex(col),
|
||||
};
|
||||
character_chunk.add_selection_and_colors(
|
||||
*res,
|
||||
background_color,
|
||||
Some(foreground_color),
|
||||
content_x,
|
||||
content_y,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
match self.grid.render(content_x, content_y, &self.style) {
|
||||
Ok(rendered_assets) => {
|
||||
self.set_should_render(false);
|
||||
return Ok(rendered_assets);
|
||||
},
|
||||
e => return e,
|
||||
}
|
||||
if self.grid.ring_bell {
|
||||
let ring_bell = '\u{7}';
|
||||
raw_vte_output.push(ring_bell);
|
||||
self.grid.ring_bell = false;
|
||||
}
|
||||
self.set_should_render(false);
|
||||
Ok(Some((
|
||||
character_chunks,
|
||||
Some(raw_vte_output),
|
||||
sixel_image_chunks,
|
||||
)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
mod pane_resizer;
|
||||
mod tiled_pane_grid;
|
||||
|
||||
use crate::resize_pty;
|
||||
use tiled_pane_grid::{split, TiledPaneGrid};
|
||||
|
||||
use crate::{
|
||||
os_input_output::ServerOsApi,
|
||||
output::Output,
|
||||
panes::{ActivePanes, PaneId},
|
||||
tab::{Pane, MIN_TERMINAL_HEIGHT, MIN_TERMINAL_WIDTH},
|
||||
ui::{boundaries::Boundaries, pane_contents_and_ui::PaneContentsAndUi},
|
||||
thread_bus::ThreadSenders,
|
||||
ui::boundaries::Boundaries,
|
||||
ui::pane_contents_and_ui::PaneContentsAndUi,
|
||||
wasm_vm::PluginInstruction,
|
||||
ClientId,
|
||||
};
|
||||
use tiled_pane_grid::{split, TiledPaneGrid};
|
||||
use zellij_utils::{
|
||||
data::{ModeInfo, Style},
|
||||
errors::prelude::*,
|
||||
|
|
@ -24,22 +29,6 @@ use std::{
|
|||
time::Instant,
|
||||
};
|
||||
|
||||
macro_rules! resize_pty {
|
||||
($pane:expr, $os_input:expr) => {
|
||||
if let PaneId::Terminal(ref pid) = $pane.pid() {
|
||||
// FIXME: This `set_terminal_size_using_terminal_id` call would be best in
|
||||
// `TerminalPane::reflow_lines`
|
||||
$os_input.set_terminal_size_using_terminal_id(
|
||||
*pid,
|
||||
$pane.get_content_columns() as u16,
|
||||
$pane.get_content_rows() as u16,
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn pane_content_offset(position_and_size: &PaneGeom, viewport: &Viewport) -> (usize, usize) {
|
||||
// (columns_offset, rows_offset)
|
||||
// if the pane is not on the bottom or right edge on the screen, we need to reserve one space
|
||||
|
|
@ -75,6 +64,7 @@ pub struct TiledPanes {
|
|||
panes_to_hide: HashSet<PaneId>,
|
||||
fullscreen_is_active: bool,
|
||||
os_api: Box<dyn ServerOsApi>,
|
||||
senders: ThreadSenders,
|
||||
}
|
||||
|
||||
impl TiledPanes {
|
||||
|
|
@ -91,6 +81,7 @@ impl TiledPanes {
|
|||
default_mode_info: ModeInfo,
|
||||
style: Style,
|
||||
os_api: Box<dyn ServerOsApi>,
|
||||
senders: ThreadSenders,
|
||||
) -> Self {
|
||||
TiledPanes {
|
||||
panes: BTreeMap::new(),
|
||||
|
|
@ -108,6 +99,7 @@ impl TiledPanes {
|
|||
panes_to_hide: HashSet::new(),
|
||||
fullscreen_is_active: false,
|
||||
os_api,
|
||||
senders,
|
||||
}
|
||||
}
|
||||
pub fn add_pane_with_existing_geom(&mut self, pane_id: PaneId, mut pane: Box<dyn Pane>) {
|
||||
|
|
@ -270,7 +262,7 @@ impl TiledPanes {
|
|||
pane.set_content_offset(Offset::shift(pane_rows_offset, pane_columns_offset));
|
||||
}
|
||||
|
||||
resize_pty!(pane, self.os_api).unwrap();
|
||||
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
||||
}
|
||||
}
|
||||
pub fn can_split_pane_horizontally(&mut self, client_id: ClientId) -> bool {
|
||||
|
|
@ -523,7 +515,7 @@ impl TiledPanes {
|
|||
);
|
||||
pane_grid.resize_pane_left(&active_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, self.os_api).unwrap();
|
||||
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -537,7 +529,7 @@ impl TiledPanes {
|
|||
);
|
||||
pane_grid.resize_pane_right(&active_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, self.os_api).unwrap();
|
||||
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -551,7 +543,7 @@ impl TiledPanes {
|
|||
);
|
||||
pane_grid.resize_pane_up(&active_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, self.os_api).unwrap();
|
||||
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -565,7 +557,7 @@ impl TiledPanes {
|
|||
);
|
||||
pane_grid.resize_pane_down(&active_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, self.os_api).unwrap();
|
||||
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -579,7 +571,7 @@ impl TiledPanes {
|
|||
);
|
||||
pane_grid.resize_increase(&active_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, self.os_api).unwrap();
|
||||
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -593,7 +585,7 @@ impl TiledPanes {
|
|||
);
|
||||
pane_grid.resize_decrease(&active_pane_id);
|
||||
for pane in self.panes.values_mut() {
|
||||
resize_pty!(pane, self.os_api).unwrap();
|
||||
resize_pty!(pane, self.os_api, self.senders).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -826,7 +818,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = prev_geom_override {
|
||||
new_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(new_position, self.os_api).unwrap();
|
||||
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
||||
new_position.set_should_render(true);
|
||||
|
||||
let current_position = self.panes.get_mut(&active_pane_id).unwrap();
|
||||
|
|
@ -834,7 +826,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = next_geom_override {
|
||||
current_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(current_position, self.os_api).unwrap();
|
||||
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
||||
current_position.set_should_render(true);
|
||||
self.set_pane_frames(self.draw_pane_frames);
|
||||
}
|
||||
|
|
@ -860,7 +852,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = prev_geom_override {
|
||||
new_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(new_position, self.os_api).unwrap();
|
||||
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
||||
new_position.set_should_render(true);
|
||||
|
||||
let current_position = self.panes.get_mut(active_pane_id).unwrap();
|
||||
|
|
@ -868,7 +860,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = next_geom_override {
|
||||
current_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(current_position, self.os_api).unwrap();
|
||||
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
||||
current_position.set_should_render(true);
|
||||
self.set_pane_frames(self.draw_pane_frames);
|
||||
}
|
||||
|
|
@ -896,7 +888,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = prev_geom_override {
|
||||
new_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(new_position, self.os_api).unwrap();
|
||||
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
||||
new_position.set_should_render(true);
|
||||
|
||||
let current_position = self.panes.get_mut(active_pane_id).unwrap();
|
||||
|
|
@ -904,7 +896,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = next_geom_override {
|
||||
current_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(current_position, self.os_api).unwrap();
|
||||
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
||||
current_position.set_should_render(true);
|
||||
self.set_pane_frames(self.draw_pane_frames);
|
||||
}
|
||||
|
|
@ -932,7 +924,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = prev_geom_override {
|
||||
new_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(new_position, self.os_api).unwrap();
|
||||
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
||||
new_position.set_should_render(true);
|
||||
|
||||
let current_position = self.panes.get_mut(active_pane_id).unwrap();
|
||||
|
|
@ -940,7 +932,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = next_geom_override {
|
||||
current_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(current_position, self.os_api).unwrap();
|
||||
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
||||
current_position.set_should_render(true);
|
||||
self.set_pane_frames(self.draw_pane_frames);
|
||||
}
|
||||
|
|
@ -968,7 +960,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = prev_geom_override {
|
||||
new_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(new_position, self.os_api).unwrap();
|
||||
resize_pty!(new_position, self.os_api, self.senders).unwrap();
|
||||
new_position.set_should_render(true);
|
||||
|
||||
let current_position = self.panes.get_mut(active_pane_id).unwrap();
|
||||
|
|
@ -976,7 +968,7 @@ impl TiledPanes {
|
|||
if let Some(geom) = next_geom_override {
|
||||
current_position.set_geom_override(geom);
|
||||
}
|
||||
resize_pty!(current_position, self.os_api).unwrap();
|
||||
resize_pty!(current_position, self.os_api, self.senders).unwrap();
|
||||
current_position.set_should_render(true);
|
||||
self.set_pane_frames(self.draw_pane_frames);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub enum ClientOrTabIndex {
|
|||
|
||||
/// Instructions related to PTYs (pseudoterminals).
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum PtyInstruction {
|
||||
pub enum PtyInstruction {
|
||||
SpawnTerminal(
|
||||
Option<TerminalAction>,
|
||||
Option<bool>,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use zellij_utils::errors::{prelude::*, ContextType, PtyWriteContext};
|
|||
use crate::thread_bus::Bus;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum PtyWriteInstruction {
|
||||
pub enum PtyWriteInstruction {
|
||||
Write(Vec<u8>, u32),
|
||||
Exit,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ type HoldForCommand = Option<RunCommand>;
|
|||
#[derive(Debug, Clone)]
|
||||
pub enum ScreenInstruction {
|
||||
PtyBytes(u32, VteBytes),
|
||||
PluginBytes(u32, ClientId, VteBytes), // u32 is plugin_id
|
||||
Render,
|
||||
NewPane(
|
||||
PaneId,
|
||||
|
|
@ -217,6 +218,7 @@ impl From<&ScreenInstruction> for ScreenContext {
|
|||
fn from(screen_instruction: &ScreenInstruction) -> Self {
|
||||
match *screen_instruction {
|
||||
ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes,
|
||||
ScreenInstruction::PluginBytes(..) => ScreenContext::PluginBytes,
|
||||
ScreenInstruction::Render => ScreenContext::Render,
|
||||
ScreenInstruction::NewPane(..) => ScreenContext::NewPane,
|
||||
ScreenInstruction::OpenInPlaceEditor(..) => ScreenContext::OpenInPlaceEditor,
|
||||
|
|
@ -531,7 +533,6 @@ impl Screen {
|
|||
};
|
||||
|
||||
if let Some(new_tab) = self.tabs.values().find(|t| t.position == new_tab_pos) {
|
||||
//if let Some(current_tab) = self.get_active_tab(client_id) {
|
||||
match self.get_active_tab(client_id) {
|
||||
Ok(current_tab) => {
|
||||
// If new active tab is same as the current one, do nothing.
|
||||
|
|
@ -1276,6 +1277,17 @@ pub(crate) fn screen_thread_main(
|
|||
}
|
||||
}
|
||||
},
|
||||
ScreenInstruction::PluginBytes(pid, client_id, vte_bytes) => {
|
||||
let all_tabs = screen.get_tabs_mut();
|
||||
for tab in all_tabs.values_mut() {
|
||||
if tab.has_plugin(pid) {
|
||||
tab.handle_plugin_bytes(pid, client_id, vte_bytes)
|
||||
.context("failed to process plugin bytes")?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
screen.render()?;
|
||||
},
|
||||
ScreenInstruction::Render => {
|
||||
screen.render()?;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -47,20 +47,27 @@ use zellij_utils::{
|
|||
pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport},
|
||||
};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! resize_pty {
|
||||
($pane:expr, $os_input:expr) => {
|
||||
if let PaneId::Terminal(ref pid) = $pane.pid() {
|
||||
// FIXME: This `set_terminal_size_using_terminal_id` call would be best in
|
||||
// `TerminalPane::reflow_lines`
|
||||
$os_input.set_terminal_size_using_terminal_id(
|
||||
($pane:expr, $os_input:expr, $senders:expr) => {{
|
||||
match $pane.pid() {
|
||||
PaneId::Terminal(ref pid) => $os_input.set_terminal_size_using_terminal_id(
|
||||
*pid,
|
||||
$pane.get_content_columns() as u16,
|
||||
$pane.get_content_rows() as u16,
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
),
|
||||
PaneId::Plugin(ref pid) => {
|
||||
let err_context = || format!("failed to resize plugin {pid}");
|
||||
$senders
|
||||
.send_to_plugin(PluginInstruction::Resize(
|
||||
*pid,
|
||||
$pane.get_content_columns(),
|
||||
$pane.get_content_rows(),
|
||||
))
|
||||
.with_context(err_context)
|
||||
},
|
||||
}
|
||||
};
|
||||
}};
|
||||
}
|
||||
|
||||
type HoldForCommand = Option<RunCommand>;
|
||||
|
|
@ -130,7 +137,8 @@ pub trait Pane {
|
|||
fn reset_size_and_position_override(&mut self);
|
||||
fn set_geom(&mut self, position_and_size: PaneGeom);
|
||||
fn set_geom_override(&mut self, pane_geom: PaneGeom);
|
||||
fn handle_pty_bytes(&mut self, bytes: VteBytes);
|
||||
fn handle_pty_bytes(&mut self, _bytes: VteBytes) {}
|
||||
fn handle_plugin_bytes(&mut self, _client_id: ClientId, _bytes: VteBytes) {}
|
||||
fn cursor_coordinates(&self) -> Option<(usize, usize)>;
|
||||
fn adjust_input_to_terminal(&mut self, _input_bytes: Vec<u8>) -> Option<AdjustedInput> {
|
||||
None
|
||||
|
|
@ -425,6 +433,7 @@ impl Tab {
|
|||
default_mode_info.clone(),
|
||||
style,
|
||||
os_api.clone(),
|
||||
senders.clone(),
|
||||
);
|
||||
let floating_panes = FloatingPanes::new(
|
||||
display_area.clone(),
|
||||
|
|
@ -436,6 +445,7 @@ impl Tab {
|
|||
default_mode_info.clone(),
|
||||
style,
|
||||
os_api.clone(),
|
||||
senders.clone(),
|
||||
);
|
||||
|
||||
let clipboard_provider = match copy_options.command {
|
||||
|
|
@ -525,7 +535,11 @@ impl Tab {
|
|||
let pane_title = run.location.to_string();
|
||||
self.senders
|
||||
.send_to_plugin(PluginInstruction::Load(
|
||||
pid_tx, run, tab_index, client_id,
|
||||
pid_tx,
|
||||
run,
|
||||
tab_index,
|
||||
client_id,
|
||||
position_and_size.into(),
|
||||
))
|
||||
.with_context(err_context)?;
|
||||
let pid = pid_rx.recv().with_context(err_context)?;
|
||||
|
|
@ -539,6 +553,12 @@ impl Tab {
|
|||
.clone(),
|
||||
pane_title,
|
||||
layout.name.clone().unwrap_or_default(),
|
||||
self.sixel_image_store.clone(),
|
||||
self.terminal_emulator_colors.clone(),
|
||||
self.terminal_emulator_color_codes.clone(),
|
||||
self.link_handler.clone(),
|
||||
self.character_cell_size.clone(),
|
||||
self.style,
|
||||
);
|
||||
new_plugin.set_borderless(layout.borderless);
|
||||
self.tiled_panes
|
||||
|
|
@ -779,7 +799,8 @@ impl Tab {
|
|||
embedded_pane_to_float.set_content_offset(Offset::default());
|
||||
}
|
||||
embedded_pane_to_float.set_geom(new_pane_geom);
|
||||
resize_pty!(embedded_pane_to_float, self.os_api).with_context(err_context)?;
|
||||
resize_pty!(embedded_pane_to_float, self.os_api, self.senders)
|
||||
.with_context(err_context)?;
|
||||
embedded_pane_to_float.set_active_at(Instant::now());
|
||||
self.floating_panes
|
||||
.add_pane(focused_pane_id, embedded_pane_to_float);
|
||||
|
|
@ -860,7 +881,7 @@ impl Tab {
|
|||
initial_pane_title,
|
||||
);
|
||||
new_pane.set_content_offset(Offset::frame(1)); // floating panes always have a frame
|
||||
resize_pty!(new_pane, self.os_api).with_context(err_context)?;
|
||||
resize_pty!(new_pane, self.os_api, self.senders).with_context(err_context)?;
|
||||
self.floating_panes.add_pane(pid, Box::new(new_pane));
|
||||
self.floating_panes.focus_pane_for_all_clients(pid);
|
||||
}
|
||||
|
|
@ -934,7 +955,7 @@ impl Tab {
|
|||
self.get_active_pane(client_id)
|
||||
.with_context(|| format!("no active pane found for client {client_id}"))
|
||||
.and_then(|current_active_pane| {
|
||||
resize_pty!(current_active_pane, self.os_api)
|
||||
resize_pty!(current_active_pane, self.os_api, self.senders)
|
||||
})
|
||||
.with_context(err_context)?;
|
||||
},
|
||||
|
|
@ -1084,6 +1105,16 @@ impl Tab {
|
|||
.values()
|
||||
.any(|s_p| s_p.pid() == PaneId::Terminal(pid))
|
||||
}
|
||||
pub fn has_plugin(&self, plugin_id: u32) -> bool {
|
||||
self.tiled_panes.panes_contain(&PaneId::Plugin(plugin_id))
|
||||
|| self
|
||||
.floating_panes
|
||||
.panes_contain(&PaneId::Plugin(plugin_id))
|
||||
|| self
|
||||
.suppressed_panes
|
||||
.values()
|
||||
.any(|s_p| s_p.pid() == PaneId::Plugin(plugin_id))
|
||||
}
|
||||
pub fn handle_pty_bytes(&mut self, pid: u32, bytes: VteBytes) -> Result<()> {
|
||||
let err_context = || format!("failed to handle pty bytes from fd {pid}");
|
||||
if let Some(terminal_output) = self
|
||||
|
|
@ -1112,6 +1143,26 @@ impl Tab {
|
|||
}
|
||||
self.process_pty_bytes(pid, bytes).with_context(err_context)
|
||||
}
|
||||
pub fn handle_plugin_bytes(
|
||||
&mut self,
|
||||
pid: u32,
|
||||
client_id: ClientId,
|
||||
bytes: VteBytes,
|
||||
) -> Result<()> {
|
||||
if let Some(plugin_pane) = self
|
||||
.tiled_panes
|
||||
.get_pane_mut(PaneId::Plugin(pid))
|
||||
.or_else(|| self.floating_panes.get_pane_mut(PaneId::Plugin(pid)))
|
||||
.or_else(|| {
|
||||
self.suppressed_panes
|
||||
.values_mut()
|
||||
.find(|s_p| s_p.pid() == PaneId::Plugin(pid))
|
||||
})
|
||||
{
|
||||
plugin_pane.handle_plugin_bytes(client_id, bytes);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn process_pending_vte_events(&mut self, pid: u32) -> Result<()> {
|
||||
if let Some(pending_vte_events) = self.pending_vte_events.get_mut(&pid) {
|
||||
let vte_events: Vec<VteBytes> = pending_vte_events.drain(..).collect();
|
||||
|
|
@ -1136,7 +1187,8 @@ impl Tab {
|
|||
})
|
||||
{
|
||||
if self.pids_waiting_resize.remove(&pid) {
|
||||
resize_pty!(terminal_output, self.os_api).with_context(err_context)?;
|
||||
resize_pty!(terminal_output, self.os_api, self.senders)
|
||||
.with_context(err_context)?;
|
||||
}
|
||||
terminal_output.handle_pty_bytes(bytes);
|
||||
let messages_to_pty = terminal_output.drain_messages_to_pty();
|
||||
|
|
@ -1833,7 +1885,7 @@ impl Tab {
|
|||
// the pane there we replaced. Now, we need to update its pty about its new size.
|
||||
// We couldn't do that before, and we can't use the original moved item now - so we
|
||||
// need to refetch it
|
||||
resize_pty!(suppressed_pane, self.os_api).unwrap();
|
||||
resize_pty!(suppressed_pane, self.os_api, self.senders).unwrap();
|
||||
}
|
||||
replaced_pane
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use zellij_utils::{channels, channels::SenderWithContext, errors::ErrorContext};
|
|||
|
||||
/// A container for senders to the different threads in zellij on the server side
|
||||
#[derive(Default, Clone)]
|
||||
pub(crate) struct ThreadSenders {
|
||||
pub struct ThreadSenders {
|
||||
pub to_screen: Option<SenderWithContext<ScreenInstruction>>,
|
||||
pub to_pty: Option<SenderWithContext<PtyInstruction>>,
|
||||
pub to_plugin: Option<SenderWithContext<PluginInstruction>>,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ use zellij_utils::{
|
|||
layout::RunPlugin,
|
||||
plugins::{PluginConfig, PluginType, PluginsConfig},
|
||||
},
|
||||
pane_size::Size,
|
||||
serde,
|
||||
};
|
||||
|
||||
|
|
@ -95,11 +96,11 @@ folder from the output of the `zellij setup --check` command.
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum PluginInstruction {
|
||||
Load(Sender<u32>, RunPlugin, usize, ClientId), // tx_pid, plugin metadata, tab_index, client_ids
|
||||
pub enum PluginInstruction {
|
||||
Load(Sender<u32>, RunPlugin, usize, ClientId, Size), // tx_pid, plugin metadata, tab_index, client_ids
|
||||
Update(Option<u32>, Option<ClientId>, Event), // Focused plugin / broadcast, client_id, event data
|
||||
Render(Sender<String>, u32, ClientId, usize, usize), // String buffer, plugin id, client_id, rows, cols
|
||||
Unload(u32), // plugin_id
|
||||
Unload(u32), // plugin_id
|
||||
Resize(u32, usize, usize), // plugin_id, columns, rows
|
||||
AddClient(ClientId),
|
||||
RemoveClient(ClientId),
|
||||
Exit,
|
||||
|
|
@ -110,8 +111,8 @@ impl From<&PluginInstruction> for PluginContext {
|
|||
match *plugin_instruction {
|
||||
PluginInstruction::Load(..) => PluginContext::Load,
|
||||
PluginInstruction::Update(..) => PluginContext::Update,
|
||||
PluginInstruction::Render(..) => PluginContext::Render,
|
||||
PluginInstruction::Unload(..) => PluginContext::Unload,
|
||||
PluginInstruction::Resize(..) => PluginContext::Resize,
|
||||
PluginInstruction::Exit => PluginContext::Exit,
|
||||
PluginInstruction::AddClient(_) => PluginContext::AddClient,
|
||||
PluginInstruction::RemoveClient(_) => PluginContext::RemoveClient,
|
||||
|
|
@ -143,7 +144,8 @@ pub(crate) fn wasm_thread_main(
|
|||
|
||||
let mut plugin_id = 0;
|
||||
let mut headless_plugins = HashMap::new();
|
||||
let mut plugin_map: HashMap<(u32, ClientId), (Instance, PluginEnv)> = HashMap::new(); // u32 => pid
|
||||
let mut plugin_map: HashMap<(u32, ClientId), (Instance, PluginEnv, (usize, usize))> =
|
||||
HashMap::new(); // u32 => pid, (usize, usize) => rows/columns TODO: clean this up into a struct or something
|
||||
let mut connected_clients: Vec<ClientId> = vec![];
|
||||
let plugin_dir = data_dir.join("plugins/");
|
||||
let plugin_global_data_dir = plugin_dir.join("data");
|
||||
|
|
@ -157,7 +159,7 @@ pub(crate) fn wasm_thread_main(
|
|||
let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel");
|
||||
err_ctx.add_call(ContextType::Plugin((&event).into()));
|
||||
match event {
|
||||
PluginInstruction::Load(pid_tx, run, tab_index, client_id) => {
|
||||
PluginInstruction::Load(pid_tx, run, tab_index, client_id, size) => {
|
||||
let err_context = || format!("failed to load plugin for client {client_id}");
|
||||
|
||||
let plugin = plugins
|
||||
|
|
@ -175,7 +177,10 @@ pub(crate) fn wasm_thread_main(
|
|||
let main_user_env = plugin_env.clone();
|
||||
load_plugin(&mut main_user_instance).with_context(err_context)?;
|
||||
|
||||
plugin_map.insert((plugin_id, client_id), (main_user_instance, main_user_env));
|
||||
plugin_map.insert(
|
||||
(plugin_id, client_id),
|
||||
(main_user_instance, main_user_env, (size.rows, size.cols)),
|
||||
);
|
||||
|
||||
// clone plugins for the rest of the client ids if they exist
|
||||
for client_id in connected_clients.iter() {
|
||||
|
|
@ -190,7 +195,10 @@ pub(crate) fn wasm_thread_main(
|
|||
let mut instance = Instance::new(&module, &zellij.chain_back(wasi))
|
||||
.with_context(err_context)?;
|
||||
load_plugin(&mut instance).with_context(err_context)?;
|
||||
plugin_map.insert((plugin_id, *client_id), (instance, new_plugin_env));
|
||||
plugin_map.insert(
|
||||
(plugin_id, *client_id),
|
||||
(instance, new_plugin_env, (size.rows, size.cols)),
|
||||
);
|
||||
}
|
||||
pid_tx.send(plugin_id).with_context(err_context)?;
|
||||
plugin_id += 1;
|
||||
|
|
@ -204,7 +212,9 @@ pub(crate) fn wasm_thread_main(
|
|||
}
|
||||
};
|
||||
|
||||
for (&(plugin_id, client_id), (instance, plugin_env)) in &plugin_map {
|
||||
for (&(plugin_id, client_id), (instance, plugin_env, (rows, columns))) in
|
||||
&plugin_map
|
||||
{
|
||||
let subs = plugin_env
|
||||
.subscriptions
|
||||
.lock()
|
||||
|
|
@ -237,37 +247,24 @@ pub(crate) fn wasm_thread_main(
|
|||
Err(e) => Err(e).with_context(err_context),
|
||||
}
|
||||
})?;
|
||||
|
||||
if *rows > 0 && *columns > 0 {
|
||||
let render = instance
|
||||
.exports
|
||||
.get_function("render")
|
||||
.with_context(err_context)?;
|
||||
render
|
||||
.call(&[Value::I32(*rows as i32), Value::I32(*columns as i32)])
|
||||
.with_context(err_context)?;
|
||||
let rendered_bytes = wasi_read_string(&plugin_env.wasi_env);
|
||||
drop(bus.senders.send_to_screen(ScreenInstruction::PluginBytes(
|
||||
plugin_id,
|
||||
client_id,
|
||||
rendered_bytes.as_bytes().to_vec(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(bus.senders.send_to_screen(ScreenInstruction::Render));
|
||||
},
|
||||
PluginInstruction::Render(buf_tx, pid, cid, rows, cols) => {
|
||||
let err_context = || {
|
||||
format!(
|
||||
"failed to render plugin with pid {pid} and cid {cid} at ({rows}, {cols})"
|
||||
)
|
||||
};
|
||||
|
||||
if rows == 0 || cols == 0 {
|
||||
buf_tx.send(String::new()).with_context(err_context)?;
|
||||
} else {
|
||||
let (instance, plugin_env) = plugin_map
|
||||
.get(&(pid, cid))
|
||||
.context("failed to find plugin for rendering")
|
||||
.with_context(err_context)?;
|
||||
let render = instance
|
||||
.exports
|
||||
.get_function("render")
|
||||
.with_context(err_context)?;
|
||||
|
||||
render
|
||||
.call(&[Value::I32(rows as i32), Value::I32(cols as i32)])
|
||||
.with_context(err_context)?;
|
||||
|
||||
buf_tx
|
||||
.send(wasi_read_string(&plugin_env.wasi_env))
|
||||
.with_context(err_context)?;
|
||||
}
|
||||
},
|
||||
PluginInstruction::Unload(pid) => {
|
||||
info!("Bye from plugin {}", &pid);
|
||||
|
|
@ -279,6 +276,38 @@ pub(crate) fn wasm_thread_main(
|
|||
}
|
||||
}
|
||||
},
|
||||
PluginInstruction::Resize(pid, new_columns, new_rows) => {
|
||||
let err_context = || format!("failed to resize plugin {pid}");
|
||||
for (
|
||||
(plugin_id, client_id),
|
||||
(instance, plugin_env, (current_rows, current_columns)),
|
||||
) in plugin_map.iter_mut()
|
||||
{
|
||||
if *plugin_id == pid {
|
||||
*current_rows = new_rows;
|
||||
*current_columns = new_columns;
|
||||
|
||||
// TODO: consolidate with above render function
|
||||
let render = instance
|
||||
.exports
|
||||
.get_function("render")
|
||||
.with_context(err_context)?;
|
||||
|
||||
render
|
||||
.call(&[
|
||||
Value::I32(*current_rows as i32),
|
||||
Value::I32(*current_columns as i32),
|
||||
])
|
||||
.with_context(err_context)?;
|
||||
let rendered_bytes = wasi_read_string(&plugin_env.wasi_env);
|
||||
drop(bus.senders.send_to_screen(ScreenInstruction::PluginBytes(
|
||||
*plugin_id,
|
||||
*client_id,
|
||||
rendered_bytes.as_bytes().to_vec(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
},
|
||||
PluginInstruction::AddClient(client_id) => {
|
||||
let err_context = || format!("failed to add plugins for client {client_id}");
|
||||
|
||||
|
|
@ -286,7 +315,7 @@ pub(crate) fn wasm_thread_main(
|
|||
|
||||
let mut seen = HashSet::new();
|
||||
let mut new_plugins = HashMap::new();
|
||||
for (&(plugin_id, _), (instance, plugin_env)) in &plugin_map {
|
||||
for (&(plugin_id, _), (instance, plugin_env, (rows, columns))) in &plugin_map {
|
||||
if seen.contains(&plugin_id) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -294,9 +323,14 @@ pub(crate) fn wasm_thread_main(
|
|||
let mut new_plugin_env = plugin_env.clone();
|
||||
|
||||
new_plugin_env.client_id = client_id;
|
||||
new_plugins.insert(plugin_id, (instance.module().clone(), new_plugin_env));
|
||||
new_plugins.insert(
|
||||
plugin_id,
|
||||
(instance.module().clone(), new_plugin_env, (*rows, *columns)),
|
||||
);
|
||||
}
|
||||
for (plugin_id, (module, mut new_plugin_env)) in new_plugins.drain() {
|
||||
for (plugin_id, (module, mut new_plugin_env, (rows, columns))) in
|
||||
new_plugins.drain()
|
||||
{
|
||||
let wasi = new_plugin_env
|
||||
.wasi_env
|
||||
.import_object(&module)
|
||||
|
|
@ -305,7 +339,10 @@ pub(crate) fn wasm_thread_main(
|
|||
let mut instance = Instance::new(&module, &zellij.chain_back(wasi))
|
||||
.with_context(err_context)?;
|
||||
load_plugin(&mut instance).with_context(err_context)?;
|
||||
plugin_map.insert((plugin_id, client_id), (instance, new_plugin_env));
|
||||
plugin_map.insert(
|
||||
(plugin_id, client_id),
|
||||
(instance, new_plugin_env, (rows, columns)),
|
||||
);
|
||||
}
|
||||
|
||||
// load headless plugins
|
||||
|
|
@ -640,7 +677,8 @@ pub fn wasi_read_string(wasi_env: &WasiEnv) -> String {
|
|||
let wasi_file = state.fs.stdout_mut().unwrap().as_mut().unwrap();
|
||||
let mut buf = String::new();
|
||||
wasi_file.read_to_string(&mut buf).unwrap();
|
||||
buf
|
||||
// https://stackoverflow.com/questions/66450942/in-rust-is-there-a-way-to-make-literal-newlines-in-r-using-windows-c
|
||||
buf.replace("\n", "\n\r")
|
||||
}
|
||||
|
||||
pub fn wasi_write_string(wasi_env: &WasiEnv, buf: &str) {
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ impl Display for ContextType {
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ScreenContext {
|
||||
HandlePtyBytes,
|
||||
PluginBytes,
|
||||
Render,
|
||||
NewPane,
|
||||
OpenInPlaceEditor,
|
||||
|
|
@ -336,6 +337,7 @@ pub enum PluginContext {
|
|||
Update,
|
||||
Render,
|
||||
Unload,
|
||||
Resize,
|
||||
Exit,
|
||||
AddClient,
|
||||
RemoveClient,
|
||||
|
|
|
|||
|
|
@ -176,3 +176,12 @@ impl From<Size> for Viewport {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PaneGeom> for Size {
|
||||
fn from(pane_geom: &PaneGeom) -> Self {
|
||||
Self {
|
||||
rows: pane_geom.rows.as_usize(),
|
||||
cols: pane_geom.cols.as_usize(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue