diff options
| author | Kunal Mohan <kunalmohan99@gmail.com> | 2021-04-01 22:40:40 +0530 |
|---|---|---|
| committer | Kunal Mohan <kunalmohan99@gmail.com> | 2021-05-04 20:48:15 +0530 |
| commit | 223ee743e1b503f7d81c06343c23b2c786b2dbee (patch) | |
| tree | 0b7a6a996c93c80328e327197816a28447f7b65a /src/client | |
| parent | wip: helper functions for ServerInstruction (diff) | |
| download | sl0ppij-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/client')
| -rw-r--r-- | src/client/mod.rs | 440 | ||||
| -rw-r--r-- | src/client/panes/terminal_pane.rs | 2 | ||||
| -rw-r--r-- | src/client/tab.rs | 402 |
3 files changed, 219 insertions, 625 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) { |
