aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKunal Mohan <kunalmohan99@gmail.com>2021-04-01 22:40:40 +0530
committerKunal Mohan <kunalmohan99@gmail.com>2021-05-04 20:48:15 +0530
commit223ee743e1b503f7d81c06343c23b2c786b2dbee (patch)
tree0b7a6a996c93c80328e327197816a28447f7b65a /src
parentwip: helper functions for ServerInstruction (diff)
downloadsl0ppij-223ee743e1b503f7d81c06343c23b2c786b2dbee.tar.gz
sl0ppij-223ee743e1b503f7d81c06343c23b2c786b2dbee.tar.xz
sl0ppij-223ee743e1b503f7d81c06343c23b2c786b2dbee.zip
Move screen and plugins to client side.
Remove AppInstruction enum spawn pty thread, screen thread and plugin thread on demand
Diffstat (limited to 'src')
-rw-r--r--src/client/mod.rs440
-rw-r--r--src/client/panes/terminal_pane.rs2
-rw-r--r--src/client/tab.rs402
-rw-r--r--src/common/errors.rs83
-rw-r--r--src/common/input/handler.rs152
-rw-r--r--src/common/os_input_output.rs22
-rw-r--r--src/common/pty_bus.rs85
-rw-r--r--src/common/screen.rs36
-rw-r--r--src/common/wasm_vm.rs9
-rw-r--r--src/main.rs4
-rw-r--r--src/server/mod.rs710
-rw-r--r--src/tests/fakes.rs7
12 files changed, 921 insertions, 1031 deletions
diff --git a/src/client/mod.rs b/src/client/mod.rs
index e91ad07e..ae298a5e 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -4,66 +4,32 @@ pub mod pane_resizer;
pub mod panes;
pub mod tab;
+use serde::{Deserialize, Serialize};
+use std::io::Write;
use std::sync::mpsc;
use std::thread;
-use std::{collections::HashMap, fs};
-use std::{
- collections::HashSet,
- io::Write,
- str::FromStr,
- sync::{Arc, Mutex},
-};
-
-use directories_next::ProjectDirs;
-use serde::{Deserialize, Serialize};
-use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
-use wasmer_wasi::{Pipe, WasiState};
-use zellij_tile::data::{EventType, InputMode};
-use crate::cli::CliArgs;
use crate::common::{
command_is_executing::CommandIsExecuting,
- errors::{AppContext, ContextType, PluginContext, ScreenContext},
+ errors::{ClientContext, ContextType},
input::handler::input_loop,
os_input_output::ClientOsApi,
- pty_bus::PtyInstruction,
- screen::{Screen, ScreenInstruction},
- wasm_vm::{wasi_stdout, wasi_write_string, zellij_imports, PluginEnv, PluginInstruction},
- ChannelWithContext, SenderType, SenderWithContext, SyncChannelWithContext, OPENCALLS,
+ SenderType, SenderWithContext, SyncChannelWithContext, OPENCALLS,
};
-use crate::layout::Layout;
use crate::server::ServerInstruction;
-/// Instructions sent from server to client
+/// Instructions related to the client-side application and sent from server to client
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum ClientInstruction {
- ToScreen(ScreenInstruction),
- ClosePluginPane(u32),
Error(String),
+ Render(String),
DoneClosingPane,
+ DoneOpeningNewPane,
+ DoneUpdatingTabs,
Exit,
}
-/// Instructions related to the client-side application.
-#[derive(Clone)]
-pub enum AppInstruction {
- Exit,
- Error(String),
- ToPty(PtyInstruction),
- DoneClosingPane,
-}
-
-impl From<ClientInstruction> for AppInstruction {
- fn from(item: ClientInstruction) -> Self {
- match item {
- ClientInstruction::Error(e) => AppInstruction::Error(e),
- ClientInstruction::DoneClosingPane => AppInstruction::DoneClosingPane,
- _ => panic!("Unsupported AppInstruction"),
- }
- }
-}
-
-pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs) {
+pub fn start_client(mut os_input: Box<dyn ClientOsApi>) {
let take_snapshot = "\u{1b}[?1049h";
os_input.unset_raw_mode(0);
let _ = os_input
@@ -75,378 +41,68 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs) {
let full_screen_ws = os_input.get_terminal_size_using_fd(0);
os_input.set_raw_mode(0);
- let (send_screen_instructions, receive_screen_instructions): ChannelWithContext<
- ScreenInstruction,
- > = mpsc::channel();
let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
- let mut send_screen_instructions =
- SenderWithContext::new(err_ctx, SenderType::Sender(send_screen_instructions));
-
- let (send_plugin_instructions, receive_plugin_instructions): ChannelWithContext<
- PluginInstruction,
- > = mpsc::channel();
- let send_plugin_instructions =
- SenderWithContext::new(err_ctx, SenderType::Sender(send_plugin_instructions));
- let (send_app_instructions, receive_app_instructions): SyncChannelWithContext<AppInstruction> =
- mpsc::sync_channel(500);
- let mut send_app_instructions =
- SenderWithContext::new(err_ctx, SenderType::SyncSender(send_app_instructions));
+ let (send_client_instructions, receive_client_instructions): SyncChannelWithContext<
+ ClientInstruction,
+ > = mpsc::sync_channel(500);
+ let mut send_client_instructions =
+ SenderWithContext::new(err_ctx, SenderType::SyncSender(send_client_instructions));
- os_input.connect_to_server();
+ os_input.connect_to_server(full_screen_ws);
#[cfg(not(test))]
std::panic::set_hook({
use crate::errors::handle_panic;
- let send_app_instructions = send_app_instructions.clone();
+ let send_client_instructions = send_client_instructions.clone();
Box::new(move |info| {
- handle_panic(info, &send_app_instructions);
+ handle_panic(info, &send_client_instructions);
})
});
- let screen_thread = thread::Builder::new()
- .name("screen".to_string())
- .spawn({
- let mut command_is_executing = command_is_executing.clone();
- let os_input = os_input.clone();
- let send_plugin_instructions = send_plugin_instructions.clone();
- let send_app_instructions = send_app_instructions.clone();
- let max_panes = opts.max_panes;
-
- move || {
- let mut screen = Screen::new(
- receive_screen_instructions,
- send_plugin_instructions,
- send_app_instructions,
- &full_screen_ws,
- os_input,
- max_panes,
- InputMode::Normal,
- );
- loop {
- let (event, mut err_ctx) = screen
- .receiver
- .recv()
- .expect("failed to receive event on channel");
- err_ctx.add_call(ContextType::Screen(ScreenContext::from(&event)));
- screen.send_app_instructions.update(err_ctx);
- screen.os_api.update_senders(err_ctx);
- if let Some(t) = screen.get_active_tab_mut() {
- t.os_api.update_senders(err_ctx);
- }
- match event {
- ScreenInstruction::Pty(pid, vte_event) => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .handle_pty_event(pid, vte_event);
- }
- ScreenInstruction::Render => {
- screen.render();
- }
- ScreenInstruction::NewPane(pid) => {
- screen.get_active_tab_mut().unwrap().new_pane(pid);
- command_is_executing.done_opening_new_pane();
- }
- ScreenInstruction::HorizontalSplit(pid) => {
- screen.get_active_tab_mut().unwrap().horizontal_split(pid);
- command_is_executing.done_opening_new_pane();
- }
- ScreenInstruction::VerticalSplit(pid) => {
- screen.get_active_tab_mut().unwrap().vertical_split(pid);
- command_is_executing.done_opening_new_pane();
- }
- ScreenInstruction::WriteCharacter(bytes) => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .write_to_active_terminal(bytes);
- }
- ScreenInstruction::ResizeLeft => {
- screen.get_active_tab_mut().unwrap().resize_left();
- }
- ScreenInstruction::ResizeRight => {
- screen.get_active_tab_mut().unwrap().resize_right();
- }
- ScreenInstruction::ResizeDown => {
- screen.get_active_tab_mut().unwrap().resize_down();
- }
- ScreenInstruction::ResizeUp => {
- screen.get_active_tab_mut().unwrap().resize_up();
- }
- ScreenInstruction::MoveFocus => {
- screen.get_active_tab_mut().unwrap().move_focus();
- }
- ScreenInstruction::MoveFocusLeft => {
- screen.get_active_tab_mut().unwrap().move_focus_left();
- }
- ScreenInstruction::MoveFocusDown => {
- screen.get_active_tab_mut().unwrap().move_focus_down();
- }
- ScreenInstruction::MoveFocusRight => {
- screen.get_active_tab_mut().unwrap().move_focus_right();
- }
- ScreenInstruction::MoveFocusUp => {
- screen.get_active_tab_mut().unwrap().move_focus_up();
- }
- ScreenInstruction::ScrollUp => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .scroll_active_terminal_up();
- }
- ScreenInstruction::ScrollDown => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .scroll_active_terminal_down();
- }
- ScreenInstruction::ClearScroll => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .clear_active_terminal_scroll();
- }
- ScreenInstruction::CloseFocusedPane => {
- screen.get_active_tab_mut().unwrap().close_focused_pane();
- screen.render();
- }
- ScreenInstruction::SetSelectable(id, selectable) => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .set_pane_selectable(id, selectable);
- }
- ScreenInstruction::SetMaxHeight(id, max_height) => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .set_pane_max_height(id, max_height);
- }
- ScreenInstruction::SetInvisibleBorders(id, invisible_borders) => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .set_pane_invisible_borders(id, invisible_borders);
- screen.render();
- }
- ScreenInstruction::ClosePane(id) => {
- screen.get_active_tab_mut().unwrap().close_pane(id);
- screen.render();
- }
- ScreenInstruction::ToggleActiveTerminalFullscreen => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .toggle_active_pane_fullscreen();
- }
- ScreenInstruction::NewTab(pane_id) => {
- screen.new_tab(pane_id);
- command_is_executing.done_updating_tabs();
- }
- ScreenInstruction::SwitchTabNext => {
- screen.switch_tab_next();
- command_is_executing.done_updating_tabs();
- }
- ScreenInstruction::SwitchTabPrev => {
- screen.switch_tab_prev();
- command_is_executing.done_updating_tabs();
- }
- ScreenInstruction::CloseTab => {
- screen.close_tab();
- command_is_executing.done_updating_tabs();
- }
- ScreenInstruction::ApplyLayout((layout, new_pane_pids)) => {
- screen.apply_layout(Layout::new(layout), new_pane_pids);
- command_is_executing.done_updating_tabs();
- }
- ScreenInstruction::GoToTab(tab_index) => {
- screen.go_to_tab(tab_index as usize);
- command_is_executing.done_updating_tabs();
- }
- ScreenInstruction::UpdateTabName(c) => {
- screen.update_active_tab_name(c);
- command_is_executing.done_updating_tabs();
- }
- ScreenInstruction::ChangeInputMode(input_mode) => {
- screen.change_input_mode(input_mode);
- }
- ScreenInstruction::Exit => {
- break;
- }
- }
- }
- }
- })
- .unwrap();
-
- let wasm_thread = thread::Builder::new()
- .name("wasm".to_string())
- .spawn({
- let mut send_screen_instructions = send_screen_instructions.clone();
- let mut send_app_instructions = send_app_instructions.clone();
-
- let store = Store::default();
- let mut plugin_id = 0;
- let mut plugin_map = HashMap::new();
- move || loop {
- let (event, mut err_ctx) = receive_plugin_instructions
- .recv()
- .expect("failed to receive event on channel");
- err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
- send_screen_instructions.update(err_ctx);
- send_app_instructions.update(err_ctx);
- match event {
- PluginInstruction::Load(pid_tx, path) => {
- let project_dirs =
- ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
- let plugin_dir = project_dirs.data_dir().join("plugins/");
- let wasm_bytes = fs::read(&path)
- .or_else(|_| fs::read(&path.with_extension("wasm")))
- .or_else(|_| fs::read(&plugin_dir.join(&path).with_extension("wasm")))
- .unwrap_or_else(|_| panic!("cannot find plugin {}", &path.display()));
-
- // FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
- let module = Module::new(&store, &wasm_bytes).unwrap();
-
- let output = Pipe::new();
- let input = Pipe::new();
- let mut wasi_env = WasiState::new("Zellij")
- .env("CLICOLOR_FORCE", "1")
- .preopen(|p| {
- p.directory(".") // FIXME: Change this to a more meaningful dir
- .alias(".")
- .read(true)
- .write(true)
- .create(true)
- })
- .unwrap()
- .stdin(Box::new(input))
- .stdout(Box::new(output))
- .finalize()
- .unwrap();
-
- let wasi = wasi_env.import_object(&module).unwrap();
-
- let plugin_env = PluginEnv {
- plugin_id,
- send_screen_instructions: send_screen_instructions.clone(),
- send_app_instructions: send_app_instructions.clone(),
- wasi_env,
- subscriptions: Arc::new(Mutex::new(HashSet::new())),
- };
-
- let zellij = zellij_imports(&store, &plugin_env);
- let instance = Instance::new(&module, &zellij.chain_back(wasi)).unwrap();
-
- let start = instance.exports.get_function("_start").unwrap();
-
- // This eventually calls the `.init()` method
- start.call(&[]).unwrap();
-
- plugin_map.insert(plugin_id, (instance, plugin_env));
- pid_tx.send(plugin_id).unwrap();
- plugin_id += 1;
- }
- PluginInstruction::Update(pid, event) => {
- for (&i, (instance, plugin_env)) in &plugin_map {
- let subs = plugin_env.subscriptions.lock().unwrap();
- // FIXME: This is very janky... Maybe I should write my own macro for Event -> EventType?
- let event_type = EventType::from_str(&event.to_string()).unwrap();
- if (pid.is_none() || pid == Some(i)) && subs.contains(&event_type) {
- let update = instance.exports.get_function("update").unwrap();
- wasi_write_string(
- &plugin_env.wasi_env,
- &serde_json::to_string(&event).unwrap(),
- );
- update.call(&[]).unwrap();
- }
- }
- drop(send_screen_instructions.send(ScreenInstruction::Render));
- }
- PluginInstruction::Render(buf_tx, pid, rows, cols) => {
- let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
-
- let render = instance.exports.get_function("render").unwrap();
-
- render
- .call(&[Value::I32(rows as i32), Value::I32(cols as i32)])
- .unwrap();
-
- buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap();
- }
- PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
- PluginInstruction::Exit => break,
- }
- }
- })
- .unwrap();
-
let _stdin_thread = thread::Builder::new()
.name("stdin_handler".to_string())
.spawn({
- let send_screen_instructions = send_screen_instructions.clone();
- let send_plugin_instructions = send_plugin_instructions.clone();
- let send_app_instructions = send_app_instructions.clone();
+ let send_client_instructions = send_client_instructions.clone();
let command_is_executing = command_is_executing.clone();
let os_input = os_input.clone();
- move || {
- input_loop(
- os_input,
- command_is_executing,
- send_screen_instructions,
- send_plugin_instructions,
- send_app_instructions,
- )
- }
+ move || input_loop(os_input, command_is_executing, send_client_instructions)
});
let router_thread = thread::Builder::new()
.name("router".to_string())
.spawn({
- let mut send_screen_instructions = send_screen_instructions.clone();
- let mut send_plugin_instructions = send_plugin_instructions.clone();
let os_input = os_input.clone();
- move || loop {
- let (instruction, err_ctx) = os_input.client_recv();
- send_app_instructions.update(err_ctx);
- send_screen_instructions.update(err_ctx);
- send_plugin_instructions.update(err_ctx);
- match instruction {
- ClientInstruction::Exit => break,
- ClientInstruction::ClosePluginPane(p) => {
- send_plugin_instructions
- .send(PluginInstruction::Unload(p))
- .unwrap();
- }
- ClientInstruction::ToScreen(s) => send_screen_instructions.send(s).unwrap(),
- _ => {
- send_app_instructions
- .send(AppInstruction::from(instruction))
- .unwrap();
+ move || {
+ loop {
+ let (instruction, err_ctx) = os_input.client_recv();
+ send_client_instructions.update(err_ctx);
+ if let ClientInstruction::Exit = instruction {
+ break;
}
+ send_client_instructions.send(instruction).unwrap();
}
+ send_client_instructions
+ .send(ClientInstruction::Exit)
+ .unwrap();
}
})
.unwrap();
#[warn(clippy::never_loop)]
loop {
- let (app_instruction, mut err_ctx) = receive_app_instructions
+ let (client_instruction, mut err_ctx) = receive_client_instructions
.recv()
.expect("failed to receive app instruction on channel");
- err_ctx.add_call(ContextType::App(AppContext::from(&app_instruction)));
- send_screen_instructions.update(err_ctx);
+ err_ctx.add_call(ContextType::Client(ClientContext::from(
+ &client_instruction,
+ )));
os_input.update_senders(err_ctx);
- match app_instruction {
- AppInstruction::Exit => break,
- AppInstruction::Error(backtrace) => {
+ match client_instruction {
+ ClientInstruction::Exit => break,
+ ClientInstruction::Error(backtrace) => {
let _ = os_input.send_to_server(ServerInstruction::ClientExit);
- let _ = send_screen_instructions.send(ScreenInstruction::Exit);
- let _ = send_plugin_instructions.send(PluginInstruction::Exit);
- let _ = screen_thread.join();
- let _ = wasm_thread.join();
os_input.unset_raw_mode(0);
let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
let error = format!("{}\n{}", goto_start_of_last_line, backtrace);
@@ -456,18 +112,20 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs) {
.unwrap();
std::process::exit(1);
}
- AppInstruction::ToPty(instruction) => {
- os_input.send_to_server(ServerInstruction::ToPty(instruction));
+ ClientInstruction::Render(output) => {
+ let mut stdout = os_input.get_stdout_writer();
+ stdout
+ .write_all(&output.as_bytes())
+ .expect("cannot write to stdout");
+ stdout.flush().expect("could not flush");
}
- AppInstruction::DoneClosingPane => command_is_executing.done_closing_pane(),
+ ClientInstruction::DoneClosingPane => command_is_executing.done_closing_pane(),
+ ClientInstruction::DoneOpeningNewPane => command_is_executing.done_opening_new_pane(),
+ ClientInstruction::DoneUpdatingTabs => command_is_executing.done_updating_tabs(),
}
}
let _ = os_input.send_to_server(ServerInstruction::ClientExit);
- let _ = send_screen_instructions.send(ScreenInstruction::Exit);
- let _ = send_plugin_instructions.send(PluginInstruction::Exit);
- screen_thread.join().unwrap();
- wasm_thread.join().unwrap();
router_thread.join().unwrap();
// cleanup();
@@ -481,9 +139,7 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs) {
);
os_input.unset_raw_mode(0);
- let _ = os_input
- .get_stdout_writer()
- .write(goodbye_message.as_bytes())
- .unwrap();
- os_input.get_stdout_writer().flush().unwrap();
+ let mut stdout = os_input.get_stdout_writer();
+ let _ = stdout.write(goodbye_message.as_bytes()).unwrap();
+ stdout.flush().unwrap();
}
diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs
index 1834e722..9fcddc82 100644
--- a/src/client/panes/terminal_pane.rs
+++ b/src/client/panes/terminal_pane.rs
@@ -20,7 +20,7 @@ pub enum PaneId {
/// 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)]
+#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
pub struct PositionAndSize {
pub x: usize,
pub y: usize,
diff --git a/src/client/tab.rs b/src/client/tab.rs
index c285cc0b..4eabab10 100644
--- a/src/client/tab.rs
+++ b/src/client/tab.rs
@@ -2,10 +2,9 @@
//! as well as how they should be resized
use crate::boundaries::colors;
-use crate::client::AppInstruction;
use crate::common::{input::handler::parse_keys, SenderWithContext};
use crate::layout::Layout;
-use crate::os_input_output::{ClientOsApi, ServerOsApiInstruction};
+use crate::os_input_output::ServerOsApi;
use crate::panes::{PaneId, PositionAndSize, TerminalPane};
use crate::pty_bus::{PtyInstruction, VteEvent};
use crate::server::ServerInstruction;
@@ -13,13 +12,12 @@ use crate::utils::shared::pad_to_size;
use crate::wasm_vm::PluginInstruction;
use crate::{boundaries::Boundaries, panes::PluginPane};
use std::os::unix::io::RawFd;
-use std::time::Instant;
+use std::sync::mpsc::channel;
use std::{
cmp::Reverse,
collections::{BTreeMap, HashSet},
};
-use std::{io::Write, sync::mpsc::channel};
-use zellij_tile::data::{Event, InputMode, ModeInfo, Palette};
+use zellij_tile::data::{Event, InputMode};
const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
@@ -68,9 +66,11 @@ pub struct Tab {
max_panes: Option<usize>,
full_screen_ws: PositionAndSize,
fullscreen_is_active: bool,
- pub os_api: Box<dyn ClientOsApi>,
- pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
- pub send_app_instructions: SenderWithContext<AppInstruction>,
+ os_api: Box<dyn ServerOsApi>,
+ send_plugin_instructions: SenderWithContext<PluginInstruction>,
+ send_pty_instructions: SenderWithContext<PtyInstruction>,
+ send_server_instructions: SenderWithContext<ServerInstruction>,
+ expansion_boundary: Option<PositionAndSize>,
should_clear_display_before_rendering: bool,
pub mode_info: ModeInfo,
pub input_mode: InputMode,
@@ -224,9 +224,10 @@ impl Tab {
position: usize,
name: String,
full_screen_ws: &PositionAndSize,
- os_api: Box<dyn ClientOsApi>,
+ mut os_api: Box<dyn ServerOsApi>,
send_plugin_instructions: SenderWithContext<PluginInstruction>,
- send_app_instructions: SenderWithContext<AppInstruction>,
+ send_pty_instructions: SenderWithContext<PtyInstruction>,
+ send_server_instructions: SenderWithContext<ServerInstruction>,
max_panes: Option<usize>,
pane_id: Option<PaneId>,
mode_info: ModeInfo,
@@ -235,13 +236,11 @@ impl Tab {
) -> Self {
let panes = if let Some(PaneId::Terminal(pid)) = pane_id {
let new_terminal = TerminalPane::new(pid, *full_screen_ws);
- os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- ),
- ));
+ os_api.set_terminal_size_using_fd(
+ new_terminal.pid,
+ new_terminal.columns() as u16,
+ new_terminal.rows() as u16,
+ );
let mut panes: BTreeMap<PaneId, Box<dyn Pane>> = BTreeMap::new();
panes.insert(PaneId::Terminal(pid), Box::new(new_terminal));
panes
@@ -260,8 +259,10 @@ impl Tab {
fullscreen_is_active: false,
synchronize_is_active: false,
os_api,
- send_app_instructions,
send_plugin_instructions,
+ send_pty_instructions,
+ send_server_instructions,
+ expansion_boundary: None,
should_clear_display_before_rendering: false,
mode_info,
input_mode,
@@ -294,13 +295,11 @@ impl Tab {
terminal_pane.set_max_width(max_columns);
}
terminal_pane.change_pos_and_size(&position_and_size);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- *pid,
- position_and_size.columns as u16,
- position_and_size.rows as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ *pid,
+ position_and_size.columns as u16,
+ position_and_size.rows as u16,
+ );
}
None => {
// we filled the entire layout, no room for this pane
@@ -342,13 +341,11 @@ impl Tab {
// there are still panes left to fill, use the pids we received in this method
let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout
let new_terminal = TerminalPane::new(*pid, *position_and_size);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ new_terminal.pid,
+ new_terminal.columns() as u16,
+ new_terminal.rows() as u16,
+ );
self.panes
.insert(PaneId::Terminal(*pid), Box::new(new_terminal));
}
@@ -357,10 +354,9 @@ impl Tab {
// this is a bit of a hack and happens because we don't have any central location that
// can query the screen as to how many panes it needs to create a layout
// fixing this will require a bit of an architecture change
- self.os_api
- .send_to_server(ServerInstruction::pty_close_pane(PaneId::Terminal(
- *unused_pid,
- )));
+ self.send_pty_instructions
+ .send(PtyInstruction::ClosePane(PaneId::Terminal(*unused_pid)))
+ .unwrap();
}
self.active_terminal = self.panes.iter().map(|(id, _)| id.to_owned()).next();
self.render();
@@ -373,13 +369,11 @@ impl Tab {
if !self.has_panes() {
if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ new_terminal.pid,
+ new_terminal.columns() as u16,
+ new_terminal.rows() as u16,
+ );
self.panes.insert(pid, Box::new(new_terminal));
self.active_terminal = Some(pid);
}
@@ -405,8 +399,9 @@ impl Tab {
},
);
if terminal_id_to_split.is_none() {
- self.os_api
- .send_to_server(ServerInstruction::pty_close_pane(pid)); // we can't open this pane, close the pty
+ self.send_pty_instructions
+ .send(PtyInstruction::ClosePane(pid))
+ .unwrap(); // we can't open this pane, close the pty
return; // likely no terminal large enough to split
}
let terminal_id_to_split = terminal_id_to_split.unwrap();
@@ -424,23 +419,19 @@ impl Tab {
if let PaneId::Terminal(term_pid) = pid {
let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws);
let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- new_terminal.pid,
- bottom_winsize.columns as u16,
- bottom_winsize.rows as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ new_terminal.pid,
+ bottom_winsize.columns as u16,
+ bottom_winsize.rows as u16,
+ );
terminal_to_split.change_pos_and_size(&top_winsize);
self.panes.insert(pid, Box::new(new_terminal));
if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- terminal_id_to_split,
- top_winsize.columns as u16,
- top_winsize.rows as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ terminal_id_to_split,
+ top_winsize.columns as u16,
+ top_winsize.rows as u16,
+ );
}
self.active_terminal = Some(pid);
}
@@ -448,23 +439,19 @@ impl Tab {
if let PaneId::Terminal(term_pid) = pid {
let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws);
let new_terminal = TerminalPane::new(term_pid, right_winsize);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- new_terminal.pid,
- right_winsize.columns as u16,
- right_winsize.rows as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ new_terminal.pid,
+ right_winsize.columns as u16,
+ right_winsize.rows as u16,
+ );
terminal_to_split.change_pos_and_size(&left_winsize);
self.panes.insert(pid, Box::new(new_terminal));
if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- terminal_id_to_split,
- left_winsize.columns as u16,
- left_winsize.rows as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ terminal_id_to_split,
+ left_winsize.columns as u16,
+ left_winsize.rows as u16,
+ );
}
}
}
@@ -480,13 +467,11 @@ impl Tab {
if !self.has_panes() {
if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ new_terminal.pid,
+ new_terminal.columns() as u16,
+ new_terminal.rows() as u16,
+ );
self.panes.insert(pid, Box::new(new_terminal));
self.active_terminal = Some(pid);
}
@@ -495,8 +480,9 @@ impl Tab {
let active_pane_id = &self.get_active_pane_id().unwrap();
let active_pane = self.panes.get_mut(active_pane_id).unwrap();
if active_pane.rows() < MIN_TERMINAL_HEIGHT * 2 + 1 {
- self.os_api
- .send_to_server(ServerInstruction::pty_close_pane(pid)); // we can't open this pane, close the pty
+ self.send_pty_instructions
+ .send(PtyInstruction::ClosePane(pid))
+ .unwrap(); // we can't open this pane, close the pty
return;
}
let terminal_ws = PositionAndSize {
@@ -510,23 +496,19 @@ impl Tab {
active_pane.change_pos_and_size(&top_winsize);
let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- new_terminal.pid,
- bottom_winsize.columns as u16,
- bottom_winsize.rows as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ new_terminal.pid,
+ bottom_winsize.columns as u16,
+ bottom_winsize.rows as u16,
+ );
self.panes.insert(pid, Box::new(new_terminal));
if let PaneId::Terminal(active_terminal_pid) = active_pane_id {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- *active_terminal_pid,
- top_winsize.columns as u16,
- top_winsize.rows as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ *active_terminal_pid,
+ top_winsize.columns as u16,
+ top_winsize.rows as u16,
+ );
}
self.active_terminal = Some(pid);
@@ -541,13 +523,11 @@ impl Tab {
if !self.has_panes() {
if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ new_terminal.pid,
+ new_terminal.columns() as u16,
+ new_terminal.rows() as u16,
+ );
self.panes.insert(pid, Box::new(new_terminal));
self.active_terminal = Some(pid);
}
@@ -556,8 +536,9 @@ impl Tab {
let active_pane_id = &self.get_active_pane_id().unwrap();
let active_pane = self.panes.get_mut(active_pane_id).unwrap();
if active_pane.columns() < MIN_TERMINAL_WIDTH * 2 + 1 {
- self.os_api
- .send_to_server(ServerInstruction::pty_close_pane(pid)); // we can't open this pane, close the pty
+ self.send_pty_instructions
+ .send(PtyInstruction::ClosePane(pid))
+ .unwrap(); // we can't open this pane, close the pty
return;
}
let terminal_ws = PositionAndSize {
@@ -571,23 +552,19 @@ impl Tab {
active_pane.change_pos_and_size(&left_winsize);
let new_terminal = TerminalPane::new(term_pid, right_winsize);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- new_terminal.pid,
- right_winsize.columns as u16,
- right_winsize.rows as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ new_terminal.pid,
+ right_winsize.columns as u16,
+ right_winsize.rows as u16,
+ );
self.panes.insert(pid, Box::new(new_terminal));
if let PaneId::Terminal(active_terminal_pid) = active_pane_id {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- *active_terminal_pid,
- left_winsize.columns as u16,
- left_winsize.rows as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ *active_terminal_pid,
+ left_winsize.columns as u16,
+ left_winsize.rows as u16,
+ );
}
self.active_terminal = Some(pid);
@@ -644,13 +621,13 @@ impl Tab {
match self.get_active_pane_id() {
Some(PaneId::Terminal(active_terminal_id)) => {
let active_terminal = self.get_active_pane().unwrap();
- let adjusted_input = active_terminal.adjust_input_to_terminal(input_bytes);
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::WriteToTtyStdin(active_terminal_id, adjusted_input),
- ));
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::TcDrain(active_terminal_id),
- ));
+ let mut adjusted_input = active_terminal.adjust_input_to_terminal(input_bytes);
+ self.os_api
+ .write_to_tty_stdin(active_terminal_id, &mut adjusted_input)
+ .expect("failed to write to terminal");
+ self.os_api
+ .tcdrain(active_terminal_id)
+ .expect("failed to drain terminal");
}
Some(PaneId::Plugin(pid)) => {
for key in parse_keys(&input_bytes) {
@@ -712,13 +689,11 @@ impl Tab {
}
let active_terminal = self.panes.get(&active_pane_id).unwrap();
if let PaneId::Terminal(active_pid) = active_pane_id {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- active_pid,
- active_terminal.columns() as u16,
- active_terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ active_pid,
+ active_terminal.columns() as u16,
+ active_terminal.rows() as u16,
+ );
}
self.render();
self.toggle_fullscreen_is_active();
@@ -747,30 +722,16 @@ impl Tab {
// in that case, we should not render as the app is exiting
return;
}
- // if any pane contain widechar, all pane in the same row will messup. We should render them every time
- // FIXME: remove this when we can handle widechars correctly
- if self.panes_contain_widechar() {
- self.set_force_render()
- }
- let mut stdout = self.os_api.get_stdout_writer();
+ let mut output = String::new();
let mut boundaries = Boundaries::new(
self.full_screen_ws.columns as u16,
self.full_screen_ws.rows as u16,
);
let hide_cursor = "\u{1b}[?25l";
- stdout
- .write_all(&hide_cursor.as_bytes())
- .expect("cannot write to stdout");
- if self.should_clear_display_before_rendering {
- let clear_display = "\u{1b}[2J";
- stdout
- .write_all(&clear_display.as_bytes())
- .expect("cannot write to stdout");
- self.should_clear_display_before_rendering = false;
- }
- for (kind, pane) in self.panes.iter_mut() {
- if !self.panes_to_hide.contains(&pane.pid()) {
- match self.active_terminal.unwrap() == pane.pid() {
+ output.push_str(hide_cursor);
+ for (kind, terminal) in self.panes.iter_mut() {
+ if !self.panes_to_hide.contains(&terminal.pid()) {
+ match self.active_terminal.unwrap() == terminal.pid() {
true => {
pane.set_active_at(Instant::now());
boundaries.add_rect(pane.as_ref(), self.mode_info.mode, Some(self.colors))
@@ -784,48 +745,39 @@ impl Tab {
adjust_to_size(&vte_output, pane.rows(), pane.columns())
};
// FIXME: Use Termion for cursor and style clearing?
- write!(
- stdout,
+ output.push_str(&format!(
"\u{1b}[{};{}H\u{1b}[m{}",
pane.y() + 1,
pane.x() + 1,
vte_output
- )
- .expect("cannot write to stdout");
+ ));
}
}
}
// TODO: only render (and calculate) boundaries if there was a resize
- let vte_output = boundaries.vte_output();
- stdout
- .write_all(&vte_output.as_bytes())
- .expect("cannot write to stdout");
+ output.push_str(&boundaries.vte_output());
match self.get_active_terminal_cursor_position() {
Some((cursor_position_x, cursor_position_y)) => {
let show_cursor = "\u{1b}[?25h";
- let goto_cursor_position = format!(
+ let goto_cursor_position = &format!(
"\u{1b}[{};{}H\u{1b}[m",
cursor_position_y + 1,
cursor_position_x + 1
); // goto row/col
- stdout
- .write_all(&show_cursor.as_bytes())
- .expect("cannot write to stdout");
- stdout
- .write_all(&goto_cursor_position.as_bytes())
- .expect("cannot write to stdout");
- stdout.flush().expect("could not flush");
+ output.push_str(show_cursor);
+ output.push_str(goto_cursor_position);
}
None => {
let hide_cursor = "\u{1b}[?25l";
- stdout
- .write_all(&hide_cursor.as_bytes())
- .expect("cannot write to stdout");
- stdout.flush().expect("could not flush");
+ output.push_str(hide_cursor);
}
}
+
+ self.send_server_instructions
+ .send(ServerInstruction::Render(output))
+ .unwrap();
}
fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
self.panes.iter()
@@ -1278,104 +1230,88 @@ impl Tab {
let terminal = self.panes.get_mut(id).unwrap();
terminal.reduce_height_down(count);
if let PaneId::Terminal(pid) = id {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- *pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ *pid,
+ terminal.columns() as u16,
+ terminal.rows() as u16,
+ );
}
}
fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) {
let terminal = self.panes.get_mut(id).unwrap();
terminal.reduce_height_up(count);
if let PaneId::Terminal(pid) = id {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- *pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ *pid,
+ terminal.columns() as u16,
+ terminal.rows() as u16,
+ );
}
}
fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) {
let terminal = self.panes.get_mut(id).unwrap();
terminal.increase_height_down(count);
if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ pid,
+ terminal.columns() as u16,
+ terminal.rows() as u16,
+ );
}
}
fn increase_pane_height_up(&mut self, id: &PaneId, count: usize) {
let terminal = self.panes.get_mut(id).unwrap();
terminal.increase_height_up(count);
if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ pid,
+ terminal.columns() as u16,
+ terminal.rows() as u16,
+ );
}
}
fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) {
let terminal = self.panes.get_mut(id).unwrap();
terminal.increase_width_right(count);
if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ pid,
+ terminal.columns() as u16,
+ terminal.rows() as u16,
+ );
}
}
fn increase_pane_width_left(&mut self, id: &PaneId, count: usize) {
let terminal = self.panes.get_mut(id).unwrap();
terminal.increase_width_left(count);
if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ pid,
+ terminal.columns() as u16,
+ terminal.rows() as u16,
+ );
}
}
fn reduce_pane_width_right(&mut self, id: &PaneId, count: usize) {
let terminal = self.panes.get_mut(id).unwrap();
terminal.reduce_width_right(count);
if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ pid,
+ terminal.columns() as u16,
+ terminal.rows() as u16,
+ );
}
}
fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) {
let terminal = self.panes.get_mut(id).unwrap();
terminal.reduce_width_left(count);
if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.send_to_server(ServerInstruction::OsApi(
- ServerOsApiInstruction::SetTerminalSizeUsingFd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- ),
- ));
+ self.os_api.set_terminal_size_using_fd(
+ pid,
+ terminal.columns() as u16,
+ terminal.rows() as u16,
+ );
}
}
fn pane_is_between_vertical_borders(
@@ -2144,8 +2080,9 @@ impl Tab {
if let Some(max_panes) = self.max_panes {
let terminals = self.get_pane_ids();
for &pid in terminals.iter().skip(max_panes - 1) {
- self.os_api
- .send_to_server(ServerInstruction::pty_close_pane(pid));
+ self.send_pty_instructions
+ .send(PtyInstruction::ClosePane(pid))
+ .unwrap();
self.close_pane_without_rerender(pid);
}
}
@@ -2255,8 +2192,9 @@ impl Tab {
pub fn close_focused_pane(&mut self) {
if let Some(active_pane_id) = self.get_active_pane_id() {
self.close_pane(active_pane_id);
- self.os_api
- .send_to_server(ServerInstruction::pty_close_pane(active_pane_id));
+ self.send_pty_instructions
+ .send(PtyInstruction::ClosePane(active_pane_id))
+ .unwrap();
}
}
pub fn scroll_active_terminal_up(&mut self) {
diff --git a/src/common/errors.rs b/src/common/errors.rs
index 3ee36752..720341a4 100644
--- a/src/common/errors.rs
+++ b/src/common/errors.rs
@@ -1,8 +1,8 @@
//! Error context system based on a thread-local representation of the call stack, itself based on
//! the instructions that are sent between threads.
-use super::{os_input_output::ServerOsApiInstruction, ServerInstruction, OPENCALLS};
-use crate::client::AppInstruction;
+use super::{ServerInstruction, OPENCALLS};
+use crate::client::ClientInstruction;
use crate::pty_bus::PtyInstruction;
use crate::screen::ScreenInstruction;
use serde::{Deserialize, Serialize};
@@ -21,7 +21,7 @@ use std::panic::PanicInfo;
#[cfg(not(test))]
pub fn handle_panic(
info: &PanicInfo<'_>,
- send_app_instructions: &SenderWithContext<AppInstruction>,
+ send_app_instructions: &SenderWithContext<ClientInstruction>,
) {
use backtrace::Backtrace;
use std::{process, thread};
@@ -68,9 +68,7 @@ pub fn handle_panic(
println!("{}", backtrace);
process::exit(1);
} else {
- send_app_instructions
- .send(AppInstruction::Error(backtrace))
- .unwrap();
+ let _ = send_app_instructions.send(ClientInstruction::Error(backtrace));
}
}
@@ -132,19 +130,17 @@ impl Display for ErrorContext {
///
/// Complex variants store a variant of a related enum, whose variants can be built from
/// the corresponding Zellij MSPC instruction enum variants ([`ScreenInstruction`],
-/// [`PtyInstruction`], [`AppInstruction`], etc).
+/// [`PtyInstruction`], [`ClientInstruction`], etc).
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum ContextType {
/// A screen-related call.
Screen(ScreenContext),
/// A PTY-related call.
Pty(PtyContext),
- /// An OS-related call.
- Os(OsContext),
/// A plugin-related call.
Plugin(PluginContext),
/// An app-related call.
- App(AppContext),
+ Client(ClientContext),
/// A server-related call.
IPCServer(ServerContext),
StdinHandler,
@@ -162,9 +158,8 @@ impl Display for ContextType {
match *self {
ContextType::Screen(c) => write!(f, "{}screen_thread: {}{:?}", purple, green, c),
ContextType::Pty(c) => write!(f, "{}pty_thread: {}{:?}", purple, green, c),
- ContextType::Os(c) => write!(f, "{}os_thread: {}{:?}", purple, green, c),
ContextType::Plugin(c) => write!(f, "{}plugin_thread: {}{:?}", purple, green, c),
- ContextType::App(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c),
+ ContextType::Client(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c),
ContextType::IPCServer(c) => write!(f, "{}ipc_server: {}{:?}", purple, green, c),
ContextType::StdinHandler => {
write!(f, "{}stdin_handler_thread: {}AcceptInput", purple, green)
@@ -257,7 +252,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::SetInvisibleBorders(..) => ScreenContext::SetInvisibleBorders,
ScreenInstruction::SetMaxHeight(..) => ScreenContext::SetMaxHeight,
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
- ScreenInstruction::ApplyLayout(_) => ScreenContext::ApplyLayout,
+ ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout,
ScreenInstruction::NewTab(_) => ScreenContext::NewTab,
ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext,
ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev,
@@ -297,28 +292,6 @@ impl From<&PtyInstruction> for PtyContext {
}
}
-/// Stack call representations corresponding to the different types of [`ServerOsApiInstruction`]s.
-#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
-pub enum OsContext {
- SetTerminalSizeUsingFd,
- WriteToTtyStdin,
- TcDrain,
- Exit,
-}
-
-impl From<&ServerOsApiInstruction> for OsContext {
- fn from(os_instruction: &ServerOsApiInstruction) -> Self {
- match *os_instruction {
- ServerOsApiInstruction::SetTerminalSizeUsingFd(_, _, _) => {
- OsContext::SetTerminalSizeUsingFd
- }
- ServerOsApiInstruction::WriteToTtyStdin(_, _) => OsContext::WriteToTtyStdin,
- ServerOsApiInstruction::TcDrain(_) => OsContext::TcDrain,
- ServerOsApiInstruction::Exit => OsContext::Exit,
- }
- }
-}
-
// FIXME: This whole pattern *needs* a macro eventually, it's soul-crushing to write
use crate::wasm_vm::PluginInstruction;
@@ -345,22 +318,26 @@ impl From<&PluginInstruction> for PluginContext {
}
}
-/// Stack call representations corresponding to the different types of [`AppInstruction`]s.
+/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
-pub enum AppContext {
+pub enum ClientContext {
Exit,
Error,
DoneClosingPane,
- ToPty,
+ DoneOpeningNewPane,
+ DoneUpdatingTabs,
+ Render,
}
-impl From<&AppInstruction> for AppContext {
- fn from(app_instruction: &AppInstruction) -> Self {
- match *app_instruction {
- AppInstruction::Exit => AppContext::Exit,
- AppInstruction::Error(_) => AppContext::Error,
- AppInstruction::DoneClosingPane => AppContext::DoneClosingPane,
- AppInstruction::ToPty(_) => AppContext::ToPty,
+impl From<&ClientInstruction> for ClientContext {
+ fn from(client_instruction: &ClientInstruction) -> Self {
+ match *client_instruction {
+ ClientInstruction::Exit => ClientContext::Exit,
+ ClientInstruction::Error(_) => ClientContext::Error,
+ ClientInstruction::Render(_) => ClientContext::Render,
+ ClientInstruction::DoneClosingPane => ClientContext::DoneClosingPane,
+ ClientInstruction::DoneOpeningNewPane => ClientContext::DoneOpeningNewPane,
+ ClientInstruction::DoneUpdatingTabs => ClientContext::DoneUpdatingTabs,
}
}
}
@@ -375,10 +352,13 @@ pub enum ServerContext {
NewClient,
ToPty,
ToScreen,
- OsApi,
+ Render,
+ PluginUpdate,
DoneClosingPane,
- ClosePluginPane,
+ DoneOpeningNewPane,
+ DoneUpdatingTabs,
ClientExit,
+ ClientShouldExit,
Exit,
}
@@ -389,13 +369,16 @@ impl From<&ServerInstruction> for ServerContext {
ServerInstruction::SplitHorizontally => ServerContext::SplitHorizontally,
ServerInstruction::SplitVertically => ServerContext::SplitVertically,
ServerInstruction::MoveFocus => ServerContext::MoveFocus,
- ServerInstruction::NewClient(_) => ServerContext::NewClient,
+ ServerInstruction::NewClient(..) => ServerContext::NewClient,
ServerInstruction::ToPty(_) => ServerContext::ToPty,
ServerInstruction::ToScreen(_) => ServerContext::ToScreen,
- ServerInstruction::OsApi(_) => ServerContext::OsApi,
+ ServerInstruction::PluginUpdate(..) => ServerContext::PluginUpdate,
+ ServerInstruction::Render(_) => ServerContext::Render,
ServerInstruction::DoneClosingPane => ServerContext::DoneClosingPane,
- ServerInstruction::ClosePluginPane(_) => ServerContext::ClosePluginPane,
+ ServerInstruction::DoneOpeningNewPane => ServerContext::DoneOpeningNewPane,
+ ServerInstruction::DoneUpdatingTabs => ServerContext::DoneUpdatingTabs,
ServerInstruction::ClientExit => ServerContext::ClientExit,
+ ServerInstruction::ClientShouldExit => ServerContext::ClientShouldExit,
ServerInstruction::Exit => ServerContext::Exit,
}
}
diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs
index d4ae3080..4015938a 100644
--- a/src/common/input/handler.rs
+++ b/src/common/input/handler.rs
@@ -2,14 +2,12 @@
use super::actions::Action;
use super::keybinds::get_default_keybinds;
-use crate::client::AppInstruction;
+use crate::client::ClientInstruction;
use crate::common::{SenderWithContext, OPENCALLS};
use crate::errors::ContextType;
use crate::os_input_output::ClientOsApi;
use crate::pty_bus::PtyInstruction;
-use crate::screen::ScreenInstruction;
use crate::server::ServerInstruction;
-use crate::wasm_vm::PluginInstruction;
use crate::CommandIsExecuting;
use termion::input::{TermRead, TermReadEventsAndRaw};
@@ -22,10 +20,7 @@ struct InputHandler {
mode: InputMode,
os_input: Box<dyn ClientOsApi>,
command_is_executing: CommandIsExecuting,
- send_screen_instructions: SenderWithContext<ScreenInstruction>,
- send_plugin_instructions: SenderWithContext<PluginInstruction>,
- send_app_instructions: SenderWithContext<AppInstruction>,
- should_exit: bool,
+ send_client_instructions: SenderWithContext<ClientInstruction>,
}
impl InputHandler {
@@ -33,20 +28,14 @@ impl InputHandler {
fn new(
os_input: Box<dyn ClientOsApi>,
command_is_executing: CommandIsExecuting,
- config: Config,
- send_screen_instructions: SenderWithContext<ScreenInstruction>,
- send_plugin_instructions: SenderWithContext<PluginInstruction>,
- send_app_instructions: SenderWithContext<AppInstruction>,
+ send_client_instructions: SenderWithContext<ClientInstruction>,
) -> Self {
InputHandler {
mode: InputMode::Normal,
os_input,
config,
command_is_executing,
- send_screen_instructions,
- send_plugin_instructions,
- send_app_instructions,
- should_exit: false,
+ send_client_instructions,
}
}
@@ -55,8 +44,7 @@ impl InputHandler {
fn handle_input(&mut self) {
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
err_ctx.add_call(ContextType::StdinHandler);
- self.send_app_instructions.update(err_ctx);
- self.send_screen_instructions.update(err_ctx);
+ self.send_client_instructions.update(err_ctx);
self.os_input.update_senders(err_ctx);
if let Ok(keybinds) = get_default_keybinds() {
'input_loop: loop {
@@ -122,12 +110,10 @@ impl InputHandler {
match action {
Action::Write(val) => {
- self.send_screen_instructions
- .send(ScreenInstruction::ClearScroll)
- .unwrap();
- self.send_screen_instructions
- .send(ScreenInstruction::WriteCharacter(val))
- .unwrap();
+ self.os_input
+ .send_to_server(ServerInstruction::clear_scroll());
+ self.os_input
+ .send_to_server(ServerInstruction::write_character(val));
}
Action::Quit => {
self.exit();
@@ -135,64 +121,42 @@ impl InputHandler {
}
Action::SwitchToMode(mode) => {
self.mode = mode;
- self.send_plugin_instructions
- .send(PluginInstruction::Update(
+ self.os_input
+ .send_to_server(ServerInstruction::PluginUpdate(
None,
- Event::ModeUpdate(get_mode_info(mode, self.os_input.load_palette())),
- ))
- .unwrap();
- self.send_screen_instructions
- .send(ScreenInstruction::ChangeMode(get_mode_info(
- mode,
- self.os_input.load_palette(),
- )))
- .unwrap();
- self.send_screen_instructions
- .send(ScreenInstruction::Render)
- .unwrap();
+ Event::ModeUpdate(get_mode_info(mode)),
+ ));
+ self.os_input
+ .send_to_server(ServerInstruction::change_input_mode(mode));
+ self.os_input.send_to_server(ServerInstruction::render());
}
Action::Resize(direction) => {
let screen_instr = match direction {
- super::actions::Direction::Left => ScreenInstruction::ResizeLeft,
- super::actions::Direction::Right => ScreenInstruction::ResizeRight,
- super::actions::Direction::Up => ScreenInstruction::ResizeUp,
- super::actions::Direction::Down => ScreenInstruction::ResizeDown,
+ super::actions::Direction::Left => ServerInstruction::resize_left(),
+ super::actions::Direction::Right => ServerInstruction::resize_right(),
+ super::actions::Direction::Up => ServerInstruction::resize_up(),
+ super::actions::Direction::Down => ServerInstruction::resize_down(),
};
- self.send_screen_instructions.send(screen_instr).unwrap();
- }
- Action::SwitchFocus => {
- self.send_screen_instructions
- .send(ScreenInstruction::SwitchFocus)
- .unwrap();
- }
- Action::FocusNextPane => {
- self.send_screen_instructions
- .send(ScreenInstruction::FocusNextPane)
- .unwrap();
+ self.os_input.send_to_server(screen_instr);
}
- Action::FocusPreviousPane => {
- self.send_screen_instructions
- .send(ScreenInstruction::FocusPreviousPane)
- .unwrap();
+ Action::SwitchFocus(_) => {
+ self.os_input.send_to_server(ServerInstruction::MoveFocus);
}
Action::MoveFocus(direction) => {
let screen_instr = match direction {
- super::actions::Direction::Left => ScreenInstruction::MoveFocusLeft,
- super::actions::Direction::Right => ScreenInstruction::MoveFocusRight,
- super::actions::Direction::Up => ScreenInstruction::MoveFocusUp,
- super::actions::Direction::Down => ScreenInstruction::MoveFocusDown,
+ super::actions::Direction::Left => ServerInstruction::move_focus_left(),
+ super::actions::Direction::Right => ServerInstruction::move_focus_right(),
+ super::actions::Direction::Up => ServerInstruction::move_focus_up(),
+ super::actions::Direction::Down => ServerInstruction::move_focus_down(),
};
- self.send_screen_instructions.send(screen_instr).unwrap();
+ self.os_input.send_to_server(screen_instr);
}
Action::ScrollUp => {
- self.send_screen_instructions
- .send(ScreenInstruction::ScrollUp)
- .unwrap();
+ self.os_input.send_to_server(ServerInstruction::scroll_up());
}
Action::ScrollDown => {
- self.send_screen_instructions
- .send(ScreenInstruction::ScrollDown)
- .unwrap();
+ self.os_input
+ .send_to_server(ServerInstruction::scroll_down());
}
Action::PageScrollUp => {
self.send_screen_instructions
@@ -205,9 +169,8 @@ impl InputHandler {
.unwrap();
}
Action::ToggleFocusFullscreen => {
- self.send_screen_instructions
- .send(ScreenInstruction::ToggleActiveTerminalFullscreen)
- .unwrap();
+ self.os_input
+ .send_to_server(ServerInstruction::toggle_active_terminal_fullscreen());
}
Action::NewPane(direction) => {
let pty_instr = match direction {
@@ -233,9 +196,8 @@ impl InputHandler {
}
Action::CloseFocus => {
self.command_is_executing.closing_pane();
- self.send_screen_instructions
- .send(ScreenInstruction::CloseFocusedPane)
- .unwrap();
+ self.os_input
+ .send_to_server(ServerInstruction::close_focused_pane());
self.command_is_executing.wait_until_pane_is_closed();
}
Action::NewTab => {
@@ -246,16 +208,14 @@ impl InputHandler {
}
Action::GoToNextTab => {
self.command_is_executing.updating_tabs();
- self.send_screen_instructions
- .send(ScreenInstruction::SwitchTabNext)
- .unwrap();
+ self.os_input
+ .send_to_server(ServerInstruction::switch_tab_next());
self.command_is_executing.wait_until_tabs_are_updated();
}
Action::GoToPreviousTab => {
self.command_is_executing.updating_tabs();
- self.send_screen_instructions
- .send(ScreenInstruction::SwitchTabPrev)
- .unwrap();
+ self.os_input
+ .send_to_server(ServerInstruction::switch_tab_prev());
self.command_is_executing.wait_until_tabs_are_updated();
}
Action::ToggleActiveSyncPanes => {
@@ -265,22 +225,19 @@ impl InputHandler {
}
Action::CloseTab => {
self.command_is_executing.updating_tabs();
- self.send_screen_instructions
- .send(ScreenInstruction::CloseTab)
- .unwrap();
+ self.os_input
+ .send_to_server(ServerInstruction::screen_close_tab());
self.command_is_executing.wait_until_tabs_are_updated();
}
Action::GoToTab(i) => {
self.command_is_executing.updating_tabs();
- self.send_screen_instructions
- .send(ScreenInstruction::GoToTab(i))
- .unwrap();
+ self.os_input
+ .send_to_server(ServerInstruction::go_to_tab(i));
self.command_is_executing.wait_until_tabs_are_updated();
}
Action::TabNameInput(c) => {
- self.send_screen_instructions
- .send(ScreenInstruction::UpdateTabName(c))
- .unwrap();
+ self.os_input
+ .send_to_server(ServerInstruction::update_tab_name(c));
}
Action::NoOp => {}
}
@@ -291,8 +248,8 @@ impl InputHandler {
/// Routine to be called when the input handler exits (at the moment this is the
/// same as quitting Zellij).
fn exit(&mut self) {
- self.send_app_instructions
- .send(AppInstruction::Exit)
+ self.send_client_instructions
+ .send(ClientInstruction::Exit)
.unwrap();
}
}
@@ -343,19 +300,10 @@ pub fn get_mode_info(mode: InputMode, palette: Palette) -> ModeInfo {
pub fn input_loop(
os_input: Box<dyn ClientOsApi>,
command_is_executing: CommandIsExecuting,
- send_screen_instructions: SenderWithContext<ScreenInstruction>,
- send_plugin_instructions: SenderWithContext<PluginInstruction>,
- send_app_instructions: SenderWithContext<AppInstruction>,
+ send_client_instructions: SenderWithContext<ClientInstruction>,
) {
- let _handler = InputHandler::new(
- os_input,
- command_is_executing,
- config,
- send_screen_instructions,
- send_plugin_instructions,
- send_app_instructions,
- )
- .handle_input();
+ let _handler =
+ InputHandler::new(os_input, command_is_executing, send_client_instructions).handle_input();
}
pub fn parse_keys(input_bytes: &[u8]) -> Vec<Key> {
diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs
index 7cc43e27..53300424 100644
--- a/src/common/os_input_output.rs
+++ b/src/common/os_input_output.rs
@@ -6,7 +6,7 @@ use nix::sys::termios;
use nix::sys::wait::waitpid;
use nix::unistd;
use nix::unistd::{ForkResult, Pid};
-use serde::{Deserialize, Serialize};
+use serde::Serialize;
use std::env;
use std::io;
use std::io::prelude::*;
@@ -22,7 +22,7 @@ use crate::panes::PositionAndSize;
use crate::server::ServerInstruction;
use crate::utils::consts::ZELLIJ_IPC_PIPE;
-const IPC_BUFFER_SIZE: u32 = 8192;
+const IPC_BUFFER_SIZE: u32 = 8388608;
fn into_raw_mode(pid: RawFd) {
let mut tio = termios::tcgetattr(pid).expect("could not get terminal attribute");
@@ -300,15 +300,6 @@ pub fn get_server_os_input() -> ServerOsInputOutput {
}
}
-/// OS Instructions sent to the Server by clients
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub enum ServerOsApiInstruction {
- SetTerminalSizeUsingFd(RawFd, u16, u16),
- WriteToTtyStdin(RawFd, Vec<u8>),
- TcDrain(RawFd),
- Exit,
-}
-
#[derive(Clone)]
pub struct ClientOsInputOutput {
orig_termios: Arc<Mutex<termios::Termios>>,
@@ -342,7 +333,7 @@ pub trait ClientOsApi: Send + Sync {
// This should be called from the client-side router thread only.
fn client_recv(&self) -> (ClientInstruction, ErrorContext);
/// Setup the client IpcChannel and notify server of new client
- fn connect_to_server(&mut self);
+ fn connect_to_server(&mut self, full_screen_ws: PositionAndSize);
}
impl ClientOsApi for ClientOsInputOutput {
@@ -378,13 +369,16 @@ impl ClientOsApi for ClientOsInputOutput {
fn update_senders(&mut self, new_ctx: ErrorContext) {
self.server_sender.update(new_ctx);
}
- fn connect_to_server(&mut self) {
+ fn connect_to_server(&mut self, full_screen_ws: PositionAndSize) {
let (client_buffer_path, client_buffer) =
SharedRingBuffer::create_temp(IPC_BUFFER_SIZE).unwrap();
self.client_receiver = Some(Arc::new(Mutex::new(IpcReceiver::new(
client_buffer.clone(),
))));
- self.send_to_server(ServerInstruction::NewClient(client_buffer_path));
+ self.send_to_server(ServerInstruction::NewClient(
+ client_buffer_path,
+ full_screen_ws,
+ ));
}
fn client_recv(&self) -> (ClientInstruction, ErrorContext) {
self.client_receiver
diff --git a/src/common/pty_bus.rs b/src/common/pty_bus.rs
index bc49e9bd..b9bb422b 100644
--- a/src/common/pty_bus.rs
+++ b/src/common/pty_bus.rs
@@ -18,7 +18,7 @@ use crate::{
errors::{ContextType, ErrorContext},
panes::PaneId,
screen::ScreenInstruction,
- server::ServerInstruction,
+ wasm_vm::PluginInstruction,
};
pub struct ReadFromPid {
@@ -82,35 +82,35 @@ pub enum VteEvent {
struct VteEventSender {
id: RawFd,
- send_server_instructions: SenderWithContext<ServerInstruction>,
+ send_screen_instructions: SenderWithContext<ScreenInstruction>,
}
impl VteEventSender {
- pub fn new(id: RawFd, send_server_instructions: SenderWithContext<ServerInstruction>) -> Self {
+ pub fn new(id: RawFd, send_screen_instructions: SenderWithContext<ScreenInstruction>) -> Self {
VteEventSender {
id,
- send_server_instructions,
+ send_screen_instructions,
}
}
}
impl vte::Perform for VteEventSender {
fn print(&mut self, c: char) {
- self.send_server_instructions
- .send(ServerInstruction::pty(self.id, VteEvent::Print(c)))
+ self.send_screen_instructions
+ .send(ScreenInstruction::Pty(self.id, VteEvent::Print(c)))
.unwrap();
}
fn execute(&mut self, byte: u8) {
- self.send_server_instructions
- .send(ServerInstruction::pty(self.id, VteEvent::Execute(byte)))
+ self.send_screen_instructions
+ .send(ScreenInstruction::Pty(self.id, VteEvent::Execute(byte)))
.unwrap();
}
fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
let params = params.iter().copied().collect();
let intermediates = intermediates.iter().copied().collect();
- self.send_server_instructions
- .send(ServerInstruction::pty(
+ self.send_screen_instructions
+ .send(ScreenInstruction::Pty(
self.id,
VteEvent::Hook(params, intermediates, ignore, c),
))
@@ -118,21 +118,21 @@ impl vte::Perform for VteEventSender {
}
fn put(&mut self, byte: u8) {
- self.send_server_instructions
- .send(ServerInstruction::pty(self.id, VteEvent::Put(byte)))
+ self.send_screen_instructions
+ .send(ScreenInstruction::Pty(self.id, VteEvent::Put(byte)))
.unwrap();
}
fn unhook(&mut self) {
- self.send_server_instructions
- .send(ServerInstruction::pty(self.id, VteEvent::Unhook))
+ self.send_screen_instructions
+ .send(ScreenInstruction::Pty(self.id, VteEvent::Unhook))
.unwrap();
}
fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
let params = params.iter().map(|p| p.to_vec()).collect();
- self.send_server_instructions
- .send(ServerInstruction::pty(
+ self.send_screen_instructions
+ .send(ScreenInstruction::Pty(
self.id,
VteEvent::OscDispatch(params, bell_terminated),
))
@@ -142,8 +142,8 @@ impl vte::Perform for VteEventSender {
fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
let params = params.iter().copied().collect();
let intermediates = intermediates.iter().copied().collect();
- self.send_server_instructions
- .send(ServerInstruction::pty(
+ self.send_screen_instructions
+ .send(ScreenInstruction::Pty(
self.id,
VteEvent::CsiDispatch(params, intermediates, ignore, c),
))
@@ -152,8 +152,8 @@ impl vte::Perform for VteEventSender {
fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
let intermediates = intermediates.iter().copied().collect();
- self.send_server_instructions
- .send(ServerInstruction::pty(
+ self.send_screen_instructions
+ .send(ScreenInstruction::Pty(
self.id,
VteEvent::EscDispatch(intermediates, ignore, byte),
))
@@ -176,7 +176,8 @@ pub enum PtyInstruction {
pub struct PtyBus {
pub receive_pty_instructions: Receiver<(PtyInstruction, ErrorContext)>,
pub id_to_child_pid: HashMap<RawFd, RawFd>,
- pub send_server_instructions: SenderWithContext<ServerInstruction>,
+ pub send_screen_instructions: SenderWithContext<ScreenInstruction>,
+ send_plugin_instructions: SenderWithContext<PluginInstruction>,
os_input: Box<dyn ServerOsApi>,
debug_to_file: bool,
task_handles: HashMap<RawFd, JoinHandle<()>>,
@@ -185,16 +186,16 @@ pub struct PtyBus {
fn stream_terminal_bytes(
pid: RawFd,
os_input: Box<dyn ServerOsApi>,
- mut send_server_instructions: SenderWithContext<ServerInstruction>,
+ mut send_screen_instructions: SenderWithContext<ScreenInstruction>,
debug: bool,
) -> JoinHandle<()> {
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
task::spawn({
async move {
err_ctx.add_call(ContextType::AsyncTask);
- send_server_instructions.update(err_ctx);
+ send_screen_instructions.update(err_ctx);
let mut vte_parser = vte::Parser::new();
- let mut vte_event_sender = VteEventSender::new(pid, send_server_instructions.clone());
+ let mut vte_event_sender = VteEventSender::new(pid, send_screen_instructions.clone());
let mut terminal_bytes = ReadFromPid::new(&pid, os_input.clone());
let mut last_byte_receive_time: Option<Instant> = None;
@@ -220,8 +221,8 @@ fn stream_terminal_bytes(
Some(receive_time) => {
if receive_time.elapsed() > max_render_pause {
pending_render = false;
- send_server_instructions
- .send(ServerInstruction::render())
+ send_screen_instructions
+ .send(ScreenInstruction::Render)
.unwrap();
last_byte_receive_time = Some(Instant::now());
} else {
@@ -236,23 +237,23 @@ fn stream_terminal_bytes(
} else {
if pending_render {
pending_render = false;
- send_server_instructions
- .send(ServerInstruction::render())
+ send_screen_instructions
+ .send(ScreenInstruction::Render)
.unwrap();
}
last_byte_receive_time = None;
task::sleep(::std::time::Duration::from_millis(10)).await;
}
}
- send_server_instructions
- .send(ServerInstruction::render())
+ send_screen_instructions
+ .send(ScreenInstruction::Render)
.unwrap();
#[cfg(not(test))]
// this is a little hacky, and is because the tests end the file as soon as
// we read everything, rather than hanging until there is new data
// a better solution would be to fix the test fakes, but this will do for now
- send_server_instructions
- .send(ServerInstruction::screen_close_pane(PaneId::Terminal(pid)))
+ send_screen_instructions
+ .send(ScreenInstruction::ClosePane(PaneId::Terminal(pid)))
.unwrap();
}
})
@@ -262,14 +263,16 @@ impl PtyBus {
pub fn new(
receive_pty_instructions: Receiver<(PtyInstruction, ErrorContext)>,
os_input: Box<dyn ServerOsApi>,
- send_server_instructions: SenderWithContext<ServerInstruction>,
+ send_screen_instructions: SenderWithContext<ScreenInstruction>,
+ send_plugin_instructions: SenderWithContext<PluginInstruction>,
debug_to_file: bool,
) -> Self {
PtyBus {
receive_pty_instructions,
os_input,
id_to_child_pid: HashMap::new(),
- send_server_instructions,
+ send_screen_instructions,
+ send_plugin_instructions,
debug_to_file,
task_handles: HashMap::new(),
}
@@ -280,7 +283,7 @@ impl PtyBus {
let task_handle = stream_terminal_bytes(
pid_primary,
self.os_input.clone(),
- self.send_server_instructions.clone(),
+ self.send_screen_instructions.clone(),
self.debug_to_file,
);
self.task_handles.insert(pid_primary, task_handle);
@@ -296,17 +299,17 @@ impl PtyBus {
self.id_to_child_pid.insert(pid_primary, pid_secondary);
new_pane_pids.push(pid_primary);
}
- self.send_server_instructions
- .send(ServerInstruction::apply_layout((
+ self.send_screen_instructions
+ .send(ScreenInstruction::ApplyLayout(
layout_path,
new_pane_pids.clone(),
- )))
+ ))
.unwrap();
for id in new_pane_pids {
let task_handle = stream_terminal_bytes(
id,
self.os_input.clone(),
- self.send_server_instructions.clone(),
+ self.send_screen_instructions.clone(),
self.debug_to_file,
);
self.task_handles.insert(id, task_handle);
@@ -323,8 +326,8 @@ impl PtyBus {
});
}
PaneId::Plugin(pid) => self
- .send_server_instructions
- .send(ServerInstruction::ClosePluginPane(pid))
+ .send_plugin_instructions
+ .send(PluginInstruction::Unload(pid))
.unwrap(),
}
}
diff --git a/src/common/screen.rs b/src/common/screen.rs
index 549eccd1..8868febb 100644
--- a/src/common/screen.rs
+++ b/src/common/screen.rs
@@ -7,9 +7,8 @@ use std::path::PathBuf;
use std::str;
use std::sync::mpsc::Receiver;
-use crate::client::AppInstruction;
use crate::common::SenderWithContext;
-use crate::os_input_output::ClientOsApi;
+use crate::os_input_output::ServerOsApi;
use crate::panes::PositionAndSize;
use crate::pty_bus::{PtyInstruction, VteEvent};
use crate::server::ServerInstruction;
@@ -51,7 +50,7 @@ pub enum ScreenInstruction {
SetMaxHeight(PaneId, usize),
SetInvisibleBorders(PaneId, bool),
ClosePane(PaneId),
- ApplyLayout((PathBuf, Vec<RawFd>)),
+ ApplyLayout(PathBuf, Vec<RawFd>),
NewTab(RawFd),
SwitchTabNext,
SwitchTabPrev,
@@ -74,14 +73,16 @@ pub struct Screen {
tabs: BTreeMap<usize, Tab>,
/// A [`PluginInstruction`] and [`ErrorContext`] sender.
pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
- /// An [`AppInstruction`] and [`ErrorContext`] sender.
- pub send_app_instructions: SenderWithContext<AppInstruction>,
+ /// An [`PtyInstruction`] and [`ErrorContext`] sender.
+ pub send_pty_instructions: SenderWithContext<PtyInstruction>,
+ /// An [`ServerInstruction`] and [`ErrorContext`] sender.
+ pub send_server_instructions: SenderWithContext<ServerInstruction>,
/// The full size of this [`Screen`].
full_screen_ws: PositionAndSize,
/// The index of this [`Screen`]'s active [`Tab`].
active_tab_index: Option<usize>,
/// The [`ClientOsApi`] this [`Screen`] uses.
- pub os_api: Box<dyn ClientOsApi>,
+ os_api: Box<dyn ServerOsApi>,
input_mode: InputMode,
}
@@ -92,9 +93,10 @@ impl Screen {
pub fn new(
receive_screen_instructions: Receiver<(ScreenInstruction, ErrorContext)>,
send_plugin_instructions: SenderWithContext<PluginInstruction>,
- send_app_instructions: SenderWithContext<AppInstruction>,
+ send_pty_instructions: SenderWithContext<PtyInstruction>,
+ send_server_instructions: SenderWithContext<ServerInstruction>,
full_screen_ws: &PositionAndSize,
- os_api: Box<dyn ClientOsApi>,
+ os_api: Box<dyn ServerOsApi>,
max_panes: Option<usize>,
mode_info: ModeInfo,
input_mode: InputMode,
@@ -104,7 +106,8 @@ impl Screen {
receiver: receive_screen_instructions,
max_panes,
send_plugin_instructions,
- send_app_instructions,
+ send_pty_instructions,
+ send_server_instructions,
full_screen_ws: *full_screen_ws,
active_tab_index: None,
tabs: BTreeMap::new(),
@@ -127,7 +130,8 @@ impl Screen {
&self.full_screen_ws,
self.os_api.clone(),
self.send_plugin_instructions.clone(),
- self.send_app_instructions.clone(),
+ self.send_pty_instructions.clone(),
+ self.send_server_instructions.clone(),
self.max_panes,
Some(PaneId::Terminal(pane_id)),
self.mode_info.clone(),
@@ -211,12 +215,13 @@ impl Screen {
// below we don't check the result of sending the CloseTab instruction to the pty thread
// because this might be happening when the app is closing, at which point the pty thread
// has already closed and this would result in an error
- self.os_api
- .send_to_server(ServerInstruction::pty_close_tab(pane_ids));
+ self.send_pty_instructions
+ .send(PtyInstruction::CloseTab(pane_ids))
+ .unwrap();
if self.tabs.is_empty() {
self.active_tab_index = None;
- self.send_app_instructions
- .send(AppInstruction::Exit)
+ self.send_server_instructions
+ .send(ServerInstruction::ClientShouldExit)
.unwrap();
} else {
for t in self.tabs.values_mut() {
@@ -282,7 +287,8 @@ impl Screen {
&self.full_screen_ws,
self.os_api.clone(),
self.send_plugin_instructions.clone(),
- self.send_app_instructions.clone(),
+ self.send_pty_instructions.clone(),
+ self.send_server_instructions.clone(),
self.max_panes,
None,
self.mode_info.clone(),
diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs
index a7cd7543..5c234403 100644
--- a/src/common/wasm_vm.rs
+++ b/src/common/wasm_vm.rs
@@ -12,7 +12,6 @@ use wasmer_wasi::WasiEnv;
use zellij_tile::data::{Event, EventType, PluginIds};
use super::{pty_bus::PtyInstruction, screen::ScreenInstruction, PaneId, SenderWithContext};
-use crate::client::AppInstruction;
#[derive(Clone, Debug)]
pub enum PluginInstruction {
@@ -28,7 +27,7 @@ pub struct PluginEnv {
pub plugin_id: u32,
// FIXME: This should be a big bundle of all of the channels
pub send_screen_instructions: SenderWithContext<ScreenInstruction>,
- pub send_app_instructions: SenderWithContext<AppInstruction>, // FIXME: This should be a big bundle of all of the channels
+ pub send_pty_instructions: SenderWithContext<PtyInstruction>, // FIXME: This should be a big bundle of all of the channels
pub wasi_env: WasiEnv,
pub subscriptions: Arc<Mutex<HashSet<EventType>>>,
}
@@ -74,10 +73,8 @@ fn host_unsubscribe(plugin_env: &PluginEnv) {
fn host_open_file(plugin_env: &PluginEnv) {
let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap());
plugin_env
- .send_app_instructions
- .send(AppInstruction::ToPty(PtyInstruction::SpawnTerminal(Some(
- path,
- ))))
+ .send_pty_instructions
+ .send(PtyInstruction::SpawnTerminal(Some(path)))
.unwrap();
}
diff --git a/src/main.rs b/src/main.rs
index 77dd6f9a..d89ff577 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -89,7 +89,7 @@ pub fn start(
opts: CliArgs,
server_os_input: Box<dyn ServerOsApi>,
) {
- let ipc_thread = start_server(server_os_input, opts.clone());
- start_client(client_os_input, opts);
+ let ipc_thread = start_server(server_os_input, opts);
+ start_client(client_os_input);
drop(ipc_thread.join());
}
diff --git a/src/server/mod.rs b/src/server/mod.rs
index eedc4c3f..5f122799 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -1,17 +1,33 @@
-use crate::client::ClientInstruction;
-use crate::common::{ChannelWithContext, SenderType, SenderWithContext};
-use crate::errors::{ContextType, ErrorContext, OsContext, PtyContext, ServerContext};
-use crate::os_input_output::{ServerOsApi, ServerOsApiInstruction};
-use crate::panes::PaneId;
-use crate::pty_bus::{PtyBus, PtyInstruction};
-use crate::screen::ScreenInstruction;
-use crate::{cli::CliArgs, common::pty_bus::VteEvent};
+use directories_next::ProjectDirs;
use serde::{Deserialize, Serialize};
use std::os::unix::io::RawFd;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::thread;
-use zellij_tile::prelude::InputMode;
+use std::{collections::HashMap, fs};
+use std::{
+ collections::HashSet,
+ str::FromStr,
+ sync::{Arc, Mutex},
+};
+use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
+use wasmer_wasi::{Pipe, WasiState};
+use zellij_tile::data::{Event, EventType, InputMode};
+
+use crate::cli::CliArgs;
+use crate::client::ClientInstruction;
+use crate::common::pty_bus::VteEvent;
+use crate::common::{
+ errors::{ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext, ServerContext},
+ os_input_output::ServerOsApi,
+ pty_bus::{PtyBus, PtyInstruction},
+ screen::{Screen, ScreenInstruction},
+ wasm_vm::{wasi_stdout, wasi_write_string, zellij_imports, PluginEnv, PluginInstruction},
+ ChannelWithContext, SenderType, SenderWithContext, OPENCALLS,
+};
+use crate::layout::Layout;
+use crate::panes::PaneId;
+use crate::panes::PositionAndSize;
/// Instructions related to server-side application including the
/// ones sent by client to server
@@ -21,13 +37,17 @@ pub enum ServerInstruction {
SplitHorizontally,
SplitVertically,
MoveFocus,
- NewClient(String),
+ NewClient(String, PositionAndSize),
ToPty(PtyInstruction),
ToScreen(ScreenInstruction),
- OsApi(ServerOsApiInstruction),
+ Render(String),
+ PluginUpdate(Option<u32>, Event),
DoneClosingPane,
- ClosePluginPane(u32),
+ DoneOpeningNewPane,
+ DoneUpdatingTabs,
ClientExit,
+ ClientShouldExit,
+ // notify router thread to exit
Exit,
}
impl ServerInstruction {
@@ -127,8 +147,8 @@ impl ServerInstruction {
pub fn screen_close_pane(pane_id: PaneId) -> Self {
Self::ToScreen(ScreenInstruction::ClosePane(pane_id))
}
- pub fn apply_layout(layout: (PathBuf, Vec<RawFd>)) -> Self {
- Self::ToScreen(ScreenInstruction::ApplyLayout(layout))
+ pub fn apply_layout(layout: PathBuf, pids: Vec<RawFd>) -> Self {
+ Self::ToScreen(ScreenInstruction::ApplyLayout(layout, pids))
}
pub fn screen_new_tab(fd: RawFd) -> Self {
Self::ToScreen(ScreenInstruction::NewTab(fd))
@@ -154,169 +174,54 @@ impl ServerInstruction {
pub fn pty(fd: RawFd, event: VteEvent) -> Self {
Self::ToScreen(ScreenInstruction::Pty(fd, event))
}
+}
- // OsApi
- pub fn set_terminal_size_using_fd(fd: RawFd, cols: u16, rows: u16) -> Self {
- Self::OsApi(ServerOsApiInstruction::SetTerminalSizeUsingFd(
- fd, cols, rows,
- ))
- }
- pub fn write_to_tty_stdin(fd: RawFd, buf: Vec<u8>) -> Self {
- Self::OsApi(ServerOsApiInstruction::WriteToTtyStdin(fd, buf))
- }
- pub fn tc_drain(fd: RawFd) -> Self {
- Self::OsApi(ServerOsApiInstruction::TcDrain(fd))
+struct ClientMetaData {
+ pub send_pty_instructions: SenderWithContext<PtyInstruction>,
+ pub send_screen_instructions: SenderWithContext<ScreenInstruction>,
+ pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
+ screen_thread: Option<thread::JoinHandle<()>>,
+ pty_thread: Option<thread::JoinHandle<()>>,
+ wasm_thread: Option<thread::JoinHandle<()>>,
+}
+
+impl ClientMetaData {
+ fn update(&mut self, err_ctx: ErrorContext) {
+ self.send_plugin_instructions.update(err_ctx);
+ self.send_screen_instructions.update(err_ctx);
+ self.send_pty_instructions.update(err_ctx);
}
- pub fn os_exit() -> Self {
- Self::OsApi(ServerOsApiInstruction::Exit)
+}
+
+impl Drop for ClientMetaData {
+ fn drop(&mut self) {
+ let _ = self.send_pty_instructions.send(PtyInstruction::Exit);
+ let _ = self.send_screen_instructions.send(ScreenInstruction::Exit);
+ let _ = self.send_plugin_instructions.send(PluginInstruction::Exit);
+ let _ = self.screen_thread.take().unwrap().join();
+ let _ = self.pty_thread.take().unwrap().join();
+ let _ = self.wasm_thread.take().unwrap().join();
}
}
pub fn start_server(mut os_input: Box<dyn ServerOsApi>, opts: CliArgs) -> thread::JoinHandle<()> {
- let (send_pty_instructions, receive_pty_instructions): ChannelWithContext<PtyInstruction> =
- channel();
- let mut send_pty_instructions = SenderWithContext::new(
- ErrorContext::new(),
- SenderType::Sender(send_pty_instructions),
- );
-
- let (send_os_instructions, receive_os_instructions): ChannelWithContext<
- ServerOsApiInstruction,
- > = channel();
- let mut send_os_instructions = SenderWithContext::new(
- ErrorContext::new(),
- SenderType::Sender(send_os_instructions),
- );
-
let (send_server_instructions, receive_server_instructions): ChannelWithContext<
ServerInstruction,
> = channel();
- let mut send_server_instructions = SenderWithContext::new(
+ let send_server_instructions = SenderWithContext::new(
ErrorContext::new(),
SenderType::Sender(send_server_instructions),
);
-
- // Don't use default layouts in tests, but do everywhere else
- #[cfg(not(test))]
- let default_layout = Some(PathBuf::from("default"));
- #[cfg(test)]
- let default_layout = None;
- let maybe_layout = opts.layout.or(default_layout);
-
- let mut pty_bus = PtyBus::new(
- receive_pty_instructions,
- os_input.clone(),
- send_server_instructions.clone(),
- opts.debug,
- );
-
- let pty_thread = thread::Builder::new()
- .name("pty".to_string())
- .spawn(move || loop {
- let (event, mut err_ctx) = pty_bus
- .receive_pty_instructions
- .recv()
- .expect("failed to receive event on channel");
- err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
- match event {
- PtyInstruction::SpawnTerminal(file_to_open) => {
- let pid = pty_bus.spawn_terminal(file_to_open);
- pty_bus
- .send_server_instructions
- .send(ServerInstruction::new_pane(PaneId::Terminal(pid)))
- .unwrap();
- }
- PtyInstruction::SpawnTerminalVertically(file_to_open) => {
- let pid = pty_bus.spawn_terminal(file_to_open);
- pty_bus
- .send_server_instructions
- .send(ServerInstruction::vertical_split(PaneId::Terminal(pid)))
- .unwrap();
- }
- PtyInstruction::SpawnTerminalHorizontally(file_to_open) => {
- let pid = pty_bus.spawn_terminal(file_to_open);
- pty_bus
- .send_server_instructions
- .send(ServerInstruction::horizontal_split(PaneId::Terminal(pid)))
- .unwrap();
- }
- PtyInstruction::NewTab => {
- if let Some(layout) = maybe_layout.clone() {
- pty_bus.spawn_terminals_for_layout(layout);
- } else {
- let pid = pty_bus.spawn_terminal(None);
- pty_bus
- .send_server_instructions
- .send(ServerInstruction::screen_new_tab(pid))
- .unwrap();
- }
- }
- PtyInstruction::ClosePane(id) => {
- pty_bus.close_pane(id);
- pty_bus
- .send_server_instructions
- .send(ServerInstruction::DoneClosingPane)
- .unwrap();
- }
- PtyInstruction::CloseTab(ids) => {
- pty_bus.close_tab(ids);
- pty_bus
- .send_server_instructions
- .send(ServerInstruction::DoneClosingPane)
- .unwrap();
- }
- PtyInstruction::Exit => {
- break;
- }
- }
- })
- .unwrap();
-
- let os_thread = thread::Builder::new()
- .name("os".to_string())
- .spawn({
- let mut os_input = os_input.clone();
- move || loop {
- let (event, mut err_ctx) = receive_os_instructions
- .recv()
- .expect("failed to receive an event on the channel");
- err_ctx.add_call(ContextType::Os(OsContext::from(&event)));
- match event {
- ServerOsApiInstruction::SetTerminalSizeUsingFd(fd, cols, rows) => {
- os_input.set_terminal_size_using_fd(fd, cols, rows);
- }
- ServerOsApiInstruction::WriteToTtyStdin(fd, mut buf) => {
- let slice = buf.as_mut_slice();
- os_input.write_to_tty_stdin(fd, slice).unwrap();
- }
- ServerOsApiInstruction::TcDrain(fd) => {
- os_input.tcdrain(fd).unwrap();
- }
- ServerOsApiInstruction::Exit => break,
- }
- }
- })
- .unwrap();
-
let router_thread = thread::Builder::new()
.name("server_router".to_string())
.spawn({
let os_input = os_input.clone();
- let mut send_os_instructions = send_os_instructions.clone();
- let mut send_pty_instructions = send_pty_instructions.clone();
+ let mut send_server_instructions = send_server_instructions.clone();
move || loop {
let (instruction, err_ctx) = os_input.server_recv();
send_server_instructions.update(err_ctx);
- send_pty_instructions.update(err_ctx);
- send_os_instructions.update(err_ctx);
match instruction {
ServerInstruction::Exit => break,
- ServerInstruction::ToPty(instruction) => {
- send_pty_instructions.send(instruction).unwrap();
- }
- ServerInstruction::OsApi(instruction) => {
- send_os_instructions.send(instruction).unwrap();
- }
_ => {
send_server_instructions.send(instruction).unwrap();
}
@@ -328,60 +233,517 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, opts: CliArgs) -> thread
thread::Builder::new()
.name("ipc_server".to_string())
.spawn({
+ let mut clients: HashMap<String, ClientMetaData> = HashMap::new();
+ // We handle only single client for now
+ let mut client: Option<String> = None;
move || loop {
let (instruction, mut err_ctx) = receive_server_instructions.recv().unwrap();
err_ctx.add_call(ContextType::IPCServer(ServerContext::from(&instruction)));
- send_pty_instructions.update(err_ctx);
- send_os_instructions.update(err_ctx);
os_input.update_senders(err_ctx);
+ if let Some(ref c) = client {
+ clients.get_mut(c).unwrap().update(err_ctx);
+ }
match instruction {
ServerInstruction::OpenFile(file_name) => {
let path = PathBuf::from(file_name);
- send_pty_instructions
+ clients[client.as_ref().unwrap()]
+ .send_pty_instructions
.send(PtyInstruction::SpawnTerminal(Some(path)))
.unwrap();
}
ServerInstruction::SplitHorizontally => {
- send_pty_instructions
+ clients[client.as_ref().unwrap()]
+ .send_pty_instructions
.send(PtyInstruction::SpawnTerminalHorizontally(None))
.unwrap();
}
ServerInstruction::SplitVertically => {
- send_pty_instructions
+ clients[client.as_ref().unwrap()]
+ .send_pty_instructions
.send(PtyInstruction::SpawnTerminalVertically(None))
.unwrap();
}
ServerInstruction::MoveFocus => {
- os_input.send_to_client(ClientInstruction::ToScreen(
- ScreenInstruction::MoveFocus,
- ));
+ clients[client.as_ref().unwrap()]
+ .send_screen_instructions
+ .send(ScreenInstruction::MoveFocus)
+ .unwrap();
}
- ServerInstruction::NewClient(buffer_path) => {
- send_pty_instructions.send(PtyInstruction::NewTab).unwrap();
+ ServerInstruction::NewClient(buffer_path, full_screen_ws) => {
+ client = Some(buffer_path.clone());
+ let client_data = init_client(
+ os_input.clone(),
+ opts.clone(),
+ send_server_instructions.clone(),
+ full_screen_ws,
+ );
+ clients.insert(buffer_path.clone(), client_data);
+ clients[client.as_ref().unwrap()]
+ .send_pty_instructions
+ .send(PtyInstruction::NewTab)
+ .unwrap();
os_input.add_client_sender(buffer_path);
}
- ServerInstruction::ToScreen(instr) => {
- os_input.send_to_client(ClientInstruction::ToScreen(instr));
+ ServerInstruction::ToScreen(instruction) => {
+ clients[client.as_ref().unwrap()]
+ .send_screen_instructions
+ .send(instruction)
+ .unwrap();
+ }
+ ServerInstruction::ToPty(instruction) => {
+ clients[client.as_ref().unwrap()]
+ .send_pty_instructions
+ .send(instruction)
+ .unwrap();
}
ServerInstruction::DoneClosingPane => {
os_input.send_to_client(ClientInstruction::DoneClosingPane);
}
- ServerInstruction::ClosePluginPane(pid) => {
- os_input.send_to_client(ClientInstruction::ClosePluginPane(pid));
+ ServerInstruction::DoneOpeningNewPane => {
+ os_input.send_to_client(ClientInstruction::DoneOpeningNewPane);
+ }
+ ServerInstruction::DoneUpdatingTabs => {
+ os_input.send_to_client(ClientInstruction::DoneUpdatingTabs);
+ }
+ ServerInstruction::ClientShouldExit => {
+ os_input.send_to_client(ClientInstruction::Exit);
+ }
+ ServerInstruction::PluginUpdate(pid, event) => {
+ clients[client.as_ref().unwrap()]
+ .send_plugin_instructions
+ .send(PluginInstruction::Update(pid, event))
+ .unwrap();
}
ServerInstruction::ClientExit => {
- let _ = send_pty_instructions.send(PtyInstruction::Exit);
- let _ = send_os_instructions.send(ServerOsApiInstruction::Exit);
+ clients.remove(client.as_ref().unwrap()).unwrap();
os_input.server_exit();
- let _ = pty_thread.join();
- let _ = os_thread.join();
let _ = router_thread.join();
let _ = os_input.send_to_client(ClientInstruction::Exit);
break;
}
+ ServerInstruction::Render(output) => {
+ os_input.send_to_client(ClientInstruction::Render(output))
+ }
_ => {}
}
}
})
.unwrap()
}
+
+fn init_client(
+ os_input: Box<dyn ServerOsApi>,
+ opts: CliArgs,
+ send_server_instructions: SenderWithContext<ServerInstruction>,
+ full_screen_ws: PositionAndSize,
+) -> ClientMetaData {
+ let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
+ let (send_screen_instructions, receive_screen_instructions): ChannelWithContext<
+ ScreenInstruction,
+ > = channel();
+ let send_screen_instructions =
+ SenderWithContext::new(err_ctx, SenderType::Sender(send_screen_instructions));
+
+ let (send_plugin_instructions, receive_plugin_instructions): ChannelWithContext<
+ PluginInstruction,
+ > = channel();
+ let send_plugin_instructions =
+ SenderWithContext::new(err_ctx, SenderType::Sender(send_plugin_instructions));
+ let (send_pty_instructions, receive_pty_instructions): ChannelWithContext<PtyInstruction> =
+ channel();
+ let send_pty_instructions =
+ SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions));
+
+ // Don't use default layouts in tests, but do everywhere else
+ #[cfg(not(test))]
+ let default_layout = Some(PathBuf::from("default"));
+ #[cfg(test)]
+ let default_layout = None;
+ let maybe_layout = opts.layout.or(default_layout);
+
+ let mut pty_bus = PtyBus::new(
+ receive_pty_instructions,
+ os_input.clone(),
+ send_screen_instructions.clone(),
+ send_plugin_instructions.clone(),
+ opts.debug,
+ );
+
+ let pty_thread = thread::Builder::new()
+ .name("pty".to_string())
+ .spawn({
+ let send_server_instructions = send_server_instructions.clone();
+ move || loop {
+ let (event, mut err_ctx) = pty_bus
+ .receive_pty_instructions
+ .recv()
+ .expect("failed to receive event on channel");
+ err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
+ match event {
+ PtyInstruction::SpawnTerminal(file_to_open) => {
+ let pid = pty_bus.spawn_terminal(file_to_open);
+ pty_bus
+ .send_screen_instructions
+ .send(ScreenInstruction::NewPane(PaneId::Terminal(pid)))
+ .unwrap();
+ }
+ PtyInstruction::SpawnTerminalVertically(file_to_open) => {
+ let pid = pty_bus.spawn_terminal(file_to_open);
+ pty_bus
+ .send_screen_instructions
+ .send(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)))
+ .unwrap();
+ }
+ PtyInstruction::SpawnTerminalHorizontally(file_to_open) => {
+ let pid = pty_bus.spawn_terminal(file_to_open);
+ pty_bus
+ .send_screen_instructions
+ .send(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
+ .unwrap();
+ }
+ PtyInstruction::NewTab => {
+ if let Some(layout) = maybe_layout.clone() {
+ pty_bus.spawn_terminals_for_layout(layout);
+ } else {
+ let pid = pty_bus.spawn_terminal(None);
+ pty_bus
+ .send_screen_instructions
+ .send(ScreenInstruction::NewTab(pid))
+ .unwrap();
+ }
+ }
+ PtyInstruction::ClosePane(id) => {
+ pty_bus.close_pane(id);
+ send_server_instructions
+ .send(ServerInstruction::DoneClosingPane)
+ .unwrap();
+ }
+ PtyInstruction::CloseTab(ids) => {
+ pty_bus.close_tab(ids);
+ send_server_instructions
+ .send(ServerInstruction::DoneClosingPane)
+ .unwrap();
+ }
+ PtyInstruction::Exit => {
+ break;
+ }
+ }
+ }
+ })
+ .unwrap();
+
+ let screen_thread = thread::Builder::new()
+ .name("screen".to_string())
+ .spawn({
+ let os_input = os_input.clone();
+ let send_plugin_instructions = send_plugin_instructions.clone();
+ let send_pty_instructions = send_pty_instructions.clone();
+ let send_server_instructions = send_server_instructions.clone();
+ let max_panes = opts.max_panes;
+
+ move || {
+ let mut screen = Screen::new(
+ receive_screen_instructions,
+ send_plugin_instructions,
+ send_pty_instructions,
+ send_server_instructions,
+ &full_screen_ws,
+ os_input,
+ max_panes,
+ InputMode::Normal,
+ );
+ loop {
+ let (event, mut err_ctx) = screen
+ .receiver
+ .recv()
+ .expect("failed to receive event on channel");
+ err_ctx.add_call(ContextType::Screen(ScreenContext::from(&event)));
+ screen.send_server_instructions.update(err_ctx);
+ screen.send_pty_instructions.update(err_ctx);
+ screen.send_plugin_instructions.update(err_ctx);
+ match event {
+ ScreenInstruction::Pty(pid, vte_event) => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .handle_pty_event(pid, vte_event);
+ }
+ ScreenInstruction::Render => {
+ screen.render();
+ }
+ ScreenInstruction::NewPane(pid) => {
+ screen.get_active_tab_mut().unwrap().new_pane(pid);
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneOpeningNewPane)
+ .unwrap();
+ }
+ ScreenInstruction::HorizontalSplit(pid) => {
+ screen.get_active_tab_mut().unwrap().horizontal_split(pid);
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneOpeningNewPane)
+ .unwrap();
+ }
+ ScreenInstruction::VerticalSplit(pid) => {
+ screen.get_active_tab_mut().unwrap().vertical_split(pid);
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneOpeningNewPane)
+ .unwrap();
+ }
+ ScreenInstruction::WriteCharacter(bytes) => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .write_to_active_terminal(bytes);
+ }
+ ScreenInstruction::ResizeLeft => {
+ screen.get_active_tab_mut().unwrap().resize_left();
+ }
+ ScreenInstruction::ResizeRight => {
+ screen.get_active_tab_mut().unwrap().resize_right();
+ }
+ ScreenInstruction::ResizeDown => {
+ screen.get_active_tab_mut().unwrap().resize_down();
+ }
+ ScreenInstruction::ResizeUp => {
+ screen.get_active_tab_mut().unwrap().resize_up();
+ }
+ ScreenInstruction::MoveFocus => {
+ screen.get_active_tab_mut().unwrap().move_focus();
+ }
+ ScreenInstruction::MoveFocusLeft => {
+ screen.get_active_tab_mut().unwrap().move_focus_left();
+ }
+ ScreenInstruction::MoveFocusDown => {
+ screen.get_active_tab_mut().unwrap().move_focus_down();
+ }
+ ScreenInstruction::MoveFocusRight => {
+ screen.get_active_tab_mut().unwrap().move_focus_right();
+ }
+ ScreenInstruction::MoveFocusUp => {
+ screen.get_active_tab_mut().unwrap().move_focus_up();
+ }
+ ScreenInstruction::ScrollUp => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .scroll_active_terminal_up();
+ }
+ ScreenInstruction::ScrollDown => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .scroll_active_terminal_down();
+ }
+ ScreenInstruction::ClearScroll => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .clear_active_terminal_scroll();
+ }
+ ScreenInstruction::CloseFocusedPane => {
+ screen.get_active_tab_mut().unwrap().close_focused_pane();
+ screen.render();
+ }
+ ScreenInstruction::SetSelectable(id, selectable) => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .set_pane_selectable(id, selectable);
+ }
+ ScreenInstruction::SetMaxHeight(id, max_height) => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .set_pane_max_height(id, max_height);
+ }
+ ScreenInstruction::SetInvisibleBorders(id, invisible_borders) => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .set_pane_invisible_borders(id, invisible_borders);
+ screen.render();
+ }
+ ScreenInstruction::ClosePane(id) => {
+ screen.get_active_tab_mut().unwrap().close_pane(id);
+ screen.render();
+ }
+ ScreenInstruction::ToggleActiveTerminalFullscreen => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .toggle_active_pane_fullscreen();
+ }
+ ScreenInstruction::NewTab(pane_id) => {
+ screen.new_tab(pane_id);
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneUpdatingTabs)
+ .unwrap();
+ }
+ ScreenInstruction::SwitchTabNext => {
+ screen.switch_tab_next();
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneUpdatingTabs)
+ .unwrap();
+ }
+ ScreenInstruction::SwitchTabPrev => {
+ screen.switch_tab_prev();
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneUpdatingTabs)
+ .unwrap();
+ }
+ ScreenInstruction::CloseTab => {
+ screen.close_tab();
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneUpdatingTabs)
+ .unwrap();
+ }
+ ScreenInstruction::ApplyLayout(layout, new_pane_pids) => {
+ screen.apply_layout(Layout::new(layout), new_pane_pids);
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneUpdatingTabs)
+ .unwrap();
+ }
+ ScreenInstruction::GoToTab(tab_index) => {
+ screen.go_to_tab(tab_index as usize);
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneUpdatingTabs)
+ .unwrap();
+ }
+ ScreenInstruction::UpdateTabName(c) => {
+ screen.update_active_tab_name(c);
+ screen
+ .send_server_instructions
+ .send(ServerInstruction::DoneUpdatingTabs)
+ .unwrap();
+ }
+ ScreenInstruction::ChangeInputMode(input_mode) => {
+ screen.change_input_mode(input_mode);
+ }
+ ScreenInstruction::Exit => {
+ break;
+ }
+ }
+ }
+ }
+ })
+ .unwrap();
+
+ let wasm_thread = thread::Builder::new()
+ .name("wasm".to_string())
+ .spawn({
+ let mut send_screen_instructions = send_screen_instructions.clone();
+ let mut send_pty_instructions = send_pty_instructions.clone();
+
+ let store = Store::default();
+ let mut plugin_id = 0;
+ let mut plugin_map = HashMap::new();
+ move || loop {
+ let (event, mut err_ctx) = receive_plugin_instructions
+ .recv()
+ .expect("failed to receive event on channel");
+ err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
+ send_screen_instructions.update(err_ctx);
+ send_pty_instructions.update(err_ctx);
+ match event {
+ PluginInstruction::Load(pid_tx, path) => {
+ let project_dirs =
+ ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
+ let plugin_dir = project_dirs.data_dir().join("plugins/");
+ let wasm_bytes = fs::read(&path)
+ .or_else(|_| fs::read(&path.with_extension("wasm")))
+ .or_else(|_| fs::read(&plugin_dir.join(&path).with_extension("wasm")))
+ .unwrap_or_else(|_| panic!("cannot find plugin {}", &path.display()));
+
+ // FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
+ let module = Module::new(&store, &wasm_bytes).unwrap();
+
+ let output = Pipe::new();
+ let input = Pipe::new();
+ let mut wasi_env = WasiState::new("Zellij")
+ .env("CLICOLOR_FORCE", "1")
+ .preopen(|p| {
+ p.directory(".") // FIXME: Change this to a more meaningful dir
+ .alias(".")
+ .read(true)
+ .write(true)
+ .create(true)
+ })
+ .unwrap()
+ .stdin(Box::new(input))
+ .stdout(Box::new(output))
+ .finalize()
+ .unwrap();
+
+ let wasi = wasi_env.import_object(&module).unwrap();
+
+ let plugin_env = PluginEnv {
+ plugin_id,
+ send_screen_instructions: send_screen_instructions.clone(),
+ send_pty_instructions: send_pty_instructions.clone(),
+ wasi_env,
+ subscriptions: Arc::new(Mutex::new(HashSet::new())),
+ };
+
+ let zellij = zellij_imports(&store, &plugin_env);
+ let instance = Instance::new(&module, &zellij.chain_back(wasi)).unwrap();
+
+ let start = instance.exports.get_function("_start").unwrap();
+
+ // This eventually calls the `.init()` method
+ start.call(&[]).unwrap();
+
+ plugin_map.insert(plugin_id, (instance, plugin_env));
+ pid_tx.send(plugin_id).unwrap();
+ plugin_id += 1;
+ }
+ PluginInstruction::Update(pid, event) => {
+ for (&i, (instance, plugin_env)) in &plugin_map {
+ let subs = plugin_env.subscriptions.lock().unwrap();
+ // FIXME: This is very janky... Maybe I should write my own macro for Event -> EventType?
+ let event_type = EventType::from_str(&event.to_string()).unwrap();
+ if (pid.is_none() || pid == Some(i)) && subs.contains(&event_type) {
+ let update = instance.exports.get_function("update").unwrap();
+ wasi_write_string(
+ &plugin_env.wasi_env,
+ &serde_json::to_string(&event).unwrap(),
+ );
+ update.call(&[]).unwrap();
+ }
+ }
+ drop(send_screen_instructions.send(ScreenInstruction::Render));
+ }
+ PluginInstruction::Render(buf_tx, pid, rows, cols) => {
+ let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
+
+ let render = instance.exports.get_function("render").unwrap();
+
+ render
+ .call(&[Value::I32(rows as i32), Value::I32(cols as i32)])
+ .unwrap();
+
+ buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap();
+ }
+ PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
+ PluginInstruction::Exit => break,
+ }
+ }
+ })
+ .unwrap();
+ ClientMetaData {
+ send_plugin_instructions,
+ send_screen_instructions,
+ send_pty_instructions,
+ screen_thread: Some(screen_thread),
+ pty_thread: Some(pty_thread),
+ wasm_thread: Some(wasm_thread),
+ }
+}
diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs
index 7c4175bb..f5b03fde 100644
--- a/src/tests/fakes.rs
+++ b/src/tests/fakes.rs
@@ -190,8 +190,11 @@ impl ClientOsApi for FakeInputOutput {
self.server_sender.update(new_ctx);
self.client_sender.update(new_ctx);
}
- fn connect_to_server(&mut self) {
- ClientOsApi::send_to_server(self, ServerInstruction::NewClient("zellij".into()));
+ fn connect_to_server(&mut self, full_screen_ws: PositionAndSize) {
+ ClientOsApi::send_to_server(
+ self,
+ ServerInstruction::NewClient("zellij".into(), full_screen_ws),
+ );
}
fn client_recv(&self) -> (ClientInstruction, ErrorContext) {
self.client_receiver.lock().unwrap().recv().unwrap()