diff --git a/Cargo.lock b/Cargo.lock index 38937d8b..832ac842 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,9 +209,9 @@ dependencies = [ [[package]] name = "bincode" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d175dfa69e619905c4c3cdb7c3c203fa3bdd5d51184e3afdb2742c0280493772" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ "serde", ] @@ -265,9 +265,9 @@ checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cache-padded" @@ -491,9 +491,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06d4a9551359071d1890820e3571252b91229e0712e7c36b08940e603c5a8fc" +checksum = "e9d6ddad5866bb2170686ed03f6839d31a76e5407d80b1c334a2c24618543ffa" dependencies = [ "darling_core", "darling_macro", @@ -501,9 +501,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b443e5fb0ddd56e0c9bfa47dc060c5306ee500cb731f2b91432dd65589a77684" +checksum = "a9ced1fd13dc386d5a8315899de465708cf34ee2a6d9394654515214e67bb846" dependencies = [ "fnv", "ident_case", @@ -515,9 +515,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0220073ce504f12a70efc4e7cdaea9e9b1b324872e7ad96a208056d7a638b81" +checksum = "0a7a1445d54b2f9792e3b31a3e715feabbace393f38dc4ffd49d94ee9bc487d5" dependencies = [ "darling_core", "quote", @@ -637,9 +637,9 @@ checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" [[package]] name = "futures" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" +checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253" dependencies = [ "futures-channel", "futures-core", @@ -652,9 +652,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" +checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" dependencies = [ "futures-core", "futures-sink", @@ -662,15 +662,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" +checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" [[package]] name = "futures-executor" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1" +checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d" dependencies = [ "futures-core", "futures-task", @@ -679,9 +679,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" +checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" [[package]] name = "futures-lite" @@ -700,9 +700,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" +checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -712,21 +712,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" +checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" [[package]] name = "futures-task" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" +checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" [[package]] name = "futures-util" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" +checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" dependencies = [ "futures-channel", "futures-core", @@ -872,9 +872,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6cf41e31a7e7b78055b548826da45c7dc74e6a13a3fa6b897a17a01322f26" +checksum = "c4a1b21a2971cea49ca4613c0e9fe8225ecaf5de64090fddc6002284726e9244" dependencies = [ "console", "lazy_static", @@ -1030,9 +1030,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "lock_api" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" dependencies = [ "scopeguard", ] @@ -1108,9 +1108,9 @@ checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" [[package]] name = "nb-connect" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670361df1bc2399ee1ff50406a0d422587dd3bb0da596e1978fe8e05dabddf4f" +checksum = "a19900e7eee95eb2b3c2e26d12a874cc80aaf750e31be6fcbe743ead369fa45d" dependencies = [ "libc", "socket2", @@ -1563,9 +1563,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7f3f92a1da3d6b1d32245d0cbcbbab0cfc45996d8df619c42bccfa6d2bbb5f" +checksum = "ef33d6d0cd06e0840fba9985aab098c147e67e05cee14d412d3345ed14ff30ac" dependencies = [ "libc", "signal-hook-registry", @@ -1600,11 +1600,10 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "socket2" -version = "0.3.19" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" dependencies = [ - "cfg-if 1.0.0", "libc", "winapi", ] @@ -1844,9 +1843,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07" +checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" dependencies = [ "proc-macro2", "quote", @@ -2291,18 +2290,18 @@ checksum = "87cc2fe6350834b4e528ba0901e7aa405d78b89dc1fa3145359eb4de0e323fcf" [[package]] name = "wast" -version = "35.0.0" +version = "35.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db5ae96da18bb5926341516fd409b5a8ce4e4714da7f0a1063d3b20ac9f9a1e1" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" dependencies = [ "leb128", ] [[package]] name = "wat" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0fa059022c5dabe129f02b429d67086400deb8277f89c975555dacc1dadbcc" +checksum = "8ec280a739b69173e0ffd12c1658507996836ba4e992ed9bc1e5385a0bd72a02" dependencies = [ "wast", ] diff --git a/src/cli.rs b/src/cli.rs index d4c1c704..c3f76967 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,7 +2,7 @@ use super::common::utils::consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV use std::path::PathBuf; use structopt::StructOpt; -#[derive(StructOpt, Debug, Default, Clone)] +#[derive(StructOpt, Default, Debug, Clone)] #[structopt(name = "zellij")] pub struct CliArgs { /// Send "split (direction h == horizontal / v == vertical)" to active zellij session @@ -44,7 +44,7 @@ pub struct CliArgs { pub debug: bool, } -#[derive(Debug, StructOpt)] +#[derive(Debug, StructOpt, Clone)] pub enum ConfigCli { /// Change the behaviour of zellij #[structopt(name = "option")] diff --git a/src/client/mod.rs b/src/client/mod.rs index b6a0231a..6ba6df73 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -9,12 +9,14 @@ use std::io::Write; use std::sync::mpsc; use std::thread; +use crate::cli::CliArgs; use crate::common::{ command_is_executing::CommandIsExecuting, errors::{ClientContext, ContextType}, + input::config::Config, input::handler::input_loop, os_input_output::ClientOsApi, - SenderType, SenderWithContext, SyncChannelWithContext, OPENCALLS, + SenderType, SenderWithContext, SyncChannelWithContext, }; use crate::server::ServerInstruction; @@ -29,7 +31,7 @@ pub enum ClientInstruction { Exit, } -pub fn start_client(mut os_input: Box) { +pub fn start_client(mut os_input: Box, opts: CliArgs) { let take_snapshot = "\u{1b}[?1049h"; os_input.unset_raw_mode(0); let _ = os_input @@ -37,17 +39,23 @@ pub fn start_client(mut os_input: Box) { .write(take_snapshot.as_bytes()) .unwrap(); + let config = Config::from_cli_config(opts.config) + .map_err(|e| { + eprintln!("There was an error in the config file:\n{}", e); + std::process::exit(1); + }) + .unwrap(); + let mut command_is_executing = CommandIsExecuting::new(); let full_screen_ws = os_input.get_terminal_size_using_fd(0); os_input.set_raw_mode(0); - let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); 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)); + let send_client_instructions = + SenderWithContext::new(SenderType::SyncSender(send_client_instructions)); os_input.connect_to_server(full_screen_ws); @@ -66,7 +74,14 @@ pub fn start_client(mut os_input: Box) { 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_client_instructions) + move || { + input_loop( + os_input, + config, + command_is_executing, + send_client_instructions, + ) + } }); let _signal_thread = thread::Builder::new() @@ -92,8 +107,7 @@ pub fn start_client(mut os_input: Box) { let os_input = os_input.clone(); move || { loop { - let (instruction, err_ctx) = os_input.client_recv(); - send_client_instructions.update(err_ctx); + let (instruction, _err_ctx) = os_input.client_recv(); if let ClientInstruction::Exit = instruction { break; } @@ -122,7 +136,11 @@ pub fn start_client(mut os_input: Box) { let _ = os_input.send_to_server(ServerInstruction::ClientExit); 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); + let restore_snapshot = "\u{1b}[?1049l"; + let error = format!( + "{}\n{}{}", + goto_start_of_last_line, restore_snapshot, backtrace + ); let _ = os_input .get_stdout_writer() .write(error.as_bytes()) diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs index 9fcddc82..f58c3a27 100644 --- a/src/client/panes/terminal_pane.rs +++ b/src/client/panes/terminal_pane.rs @@ -1,7 +1,6 @@ use crate::tab::Pane; use ::nix::pty::Winsize; use ::std::os::unix::io::RawFd; -use ::vte::Perform; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::time::Instant; diff --git a/src/client/tab.rs b/src/client/tab.rs index 3817a327..42e19388 100644 --- a/src/client/tab.rs +++ b/src/client/tab.rs @@ -7,7 +7,7 @@ use crate::common::{input::handler::parse_keys, SenderWithContext}; use crate::layout::Layout; use crate::os_input_output::ServerOsApi; use crate::panes::{PaneId, PositionAndSize, TerminalPane}; -use crate::pty_bus::{PtyInstruction, VteEvent}; +use crate::pty_bus::{PtyInstruction, VteBytes}; use crate::server::ServerInstruction; use crate::utils::shared::adjust_to_size; use crate::wasm_vm::PluginInstruction; diff --git a/src/common/errors.rs b/src/common/errors.rs index 10b2165a..5685f885 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -1,7 +1,7 @@ //! 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::{ServerInstruction, OPENCALLS}; +use super::{ServerInstruction, ASYNCOPENCALLS, OPENCALLS}; use crate::client::ClientInstruction; use crate::pty_bus::PtyInstruction; use crate::screen::ScreenInstruction; diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index 947a8d02..2fba8537 100644 --- a/src/common/input/handler.rs +++ b/src/common/input/handler.rs @@ -1,12 +1,14 @@ //! Main input logic. use super::actions::Action; -use super::keybinds::get_default_keybinds; +use super::keybinds::Keybinds; use crate::client::ClientInstruction; +use crate::common::input::config::Config; 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::CommandIsExecuting; @@ -19,8 +21,10 @@ struct InputHandler { /// The current input mode mode: InputMode, os_input: Box, + config: Config, command_is_executing: CommandIsExecuting, send_client_instructions: SenderWithContext, + should_exit: bool, } impl InputHandler { @@ -28,6 +32,7 @@ impl InputHandler { fn new( os_input: Box, command_is_executing: CommandIsExecuting, + config: Config, send_client_instructions: SenderWithContext, ) -> Self { InputHandler { @@ -36,6 +41,7 @@ impl InputHandler { config, command_is_executing, send_client_instructions, + should_exit: false, } } @@ -44,34 +50,26 @@ impl InputHandler { fn handle_input(&mut self) { let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); err_ctx.add_call(ContextType::StdinHandler); - self.send_client_instructions.update(err_ctx); - self.os_input.update_senders(err_ctx); - if let Ok(keybinds) = get_default_keybinds() { - 'input_loop: loop { - //@@@ I think this should actually just iterate over stdin directly - let stdin_buffer = self.os_input.read_from_stdin(); - for key_result in stdin_buffer.events_and_raw() { - match key_result { - Ok((event, raw_bytes)) => match event { - termion::event::Event::Key(key) => { - let key = cast_termion_key(key); - // FIXME this explicit break is needed because the current test - // framework relies on it to not create dead threads that loop - // and eat up CPUs. Do not remove until the test framework has - // been revised. Sorry about this (@categorille) - let mut should_break = false; - for action in key_to_actions(&key, raw_bytes, &self.mode, &keybinds) - { - should_break |= self.dispatch_action(action); - } - if should_break { - break 'input_loop; - } - } - termion::event::Event::Mouse(_) - | termion::event::Event::Unsupported(_) => { - // Mouse and unsupported events aren't implemented yet, - // use a NoOp untill then. + let keybinds = self.config.keybinds.clone(); + let alt_left_bracket = vec![27, 91]; + loop { + if self.should_exit { + break; + } + let stdin_buffer = self.os_input.read_from_stdin(); + for key_result in stdin_buffer.events_and_raw() { + match key_result { + Ok((event, raw_bytes)) => match event { + termion::event::Event::Key(key) => { + let key = cast_termion_key(key); + self.handle_key(&key, raw_bytes, &keybinds); + } + termion::event::Event::Unsupported(unsupported_key) => { + // we have to do this because of a bug in termion + // this should be a key event and not an unsupported event + if unsupported_key == alt_left_bracket { + let key = Key::Alt('['); + self.handle_key(&key, raw_bytes, &keybinds); } } termion::event::Event::Mouse(_) => { @@ -139,8 +137,19 @@ impl InputHandler { }; self.os_input.send_to_server(screen_instr); } - Action::SwitchFocus(_) => { - self.os_input.send_to_server(ServerInstruction::MoveFocus); + Action::SwitchFocus => { + self.os_input + .send_to_server(ServerInstruction::ToScreen(ScreenInstruction::SwitchFocus)); + } + Action::FocusNextPane => { + self.os_input.send_to_server(ServerInstruction::ToScreen( + ScreenInstruction::FocusNextPane, + )); + } + Action::FocusPreviousPane => { + self.os_input.send_to_server(ServerInstruction::ToScreen( + ScreenInstruction::FocusPreviousPane, + )); } Action::MoveFocus(direction) => { let screen_instr = match direction { @@ -299,11 +308,17 @@ pub fn get_mode_info(mode: InputMode, palette: Palette) -> ModeInfo { /// its [`InputHandler::handle_input()`] loop. pub fn input_loop( os_input: Box, + config: Config, command_is_executing: CommandIsExecuting, send_client_instructions: SenderWithContext, ) { - let _handler = - InputHandler::new(os_input, command_is_executing, send_client_instructions).handle_input(); + let _handler = InputHandler::new( + os_input, + command_is_executing, + config, + send_client_instructions, + ) + .handle_input(); } pub fn parse_keys(input_bytes: &[u8]) -> Vec { diff --git a/src/common/mod.rs b/src/common/mod.rs index d9b5e6cf..6061f1a6 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -11,7 +11,8 @@ pub mod wasm_vm; use crate::panes::PaneId; use crate::server::ServerInstruction; -use errors::ErrorContext; +use async_std::task_local; +use errors::{get_current_ctx, ErrorContext}; use std::cell::RefCell; use std::sync::mpsc; @@ -43,8 +44,8 @@ pub struct SenderWithContext { } impl SenderWithContext { - pub fn new(err_ctx: ErrorContext, sender: SenderType) -> Self { - Self { err_ctx, sender } + pub fn new(sender: SenderType) -> Self { + Self { sender } } /// Sends an event, along with the current [`ErrorContext`], on this @@ -66,3 +67,9 @@ thread_local!( /// stack in the form of an [`ErrorContext`]. pub static OPENCALLS: RefCell = RefCell::default() ); + +task_local! { + /// A key to some task local storage that holds a representation of the task's call + /// stack in the form of an [`ErrorContext`]. + static ASYNCOPENCALLS: RefCell = RefCell::default() +} diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs index 66957a4a..3d7b8918 100644 --- a/src/common/os_input_output.rs +++ b/src/common/os_input_output.rs @@ -256,7 +256,13 @@ impl ServerOsApi for ServerOsInputOutput { Box::new((*self).clone()) } fn kill(&mut self, pid: RawFd) -> Result<(), nix::Error> { - kill(Pid::from_raw(pid), Some(Signal::SIGINT)).unwrap(); + // TODO: + // Ideally, we should be using SIGINT rather than SIGKILL here, but there are cases in which + // the terminal we're trying to kill hangs on SIGINT and so all the app gets stuck + // that's why we're sending SIGKILL here + // A better solution would be to send SIGINT here and not wait for it, and then have + // a background thread do the waitpid stuff and send SIGKILL if the process is stuck + kill(Pid::from_raw(pid), Some(Signal::SIGKILL)).unwrap(); waitpid(Pid::from_raw(pid), None).unwrap(); Ok(()) } diff --git a/src/common/pty_bus.rs b/src/common/pty_bus.rs index b9bb422b..b9f61d09 100644 --- a/src/common/pty_bus.rs +++ b/src/common/pty_bus.rs @@ -6,16 +6,15 @@ use ::std::os::unix::io::RawFd; use ::std::pin::*; use ::std::sync::mpsc::Receiver; use ::std::time::{Duration, Instant}; -use ::vte; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -use super::{SenderWithContext, OPENCALLS}; +use super::SenderWithContext; use crate::layout::Layout; use crate::os_input_output::ServerOsApi; use crate::utils::logging::debug_to_file; use crate::{ - errors::{ContextType, ErrorContext}, + errors::{get_current_ctx, ContextType, ErrorContext}, panes::PaneId, screen::ScreenInstruction, wasm_vm::PluginInstruction, @@ -67,99 +66,7 @@ impl Stream for ReadFromPid { } } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum VteEvent { - // TODO: try not to allocate Vecs - Print(char), - Execute(u8), // byte - Hook(Vec, Vec, bool, char), // params, intermediates, ignore, char - Put(u8), // byte - Unhook, - OscDispatch(Vec>, bool), // params, bell_terminated - CsiDispatch(Vec, Vec, bool, char), // params, intermediates, ignore, char - EscDispatch(Vec, bool, u8), // intermediates, ignore, byte -} - -struct VteEventSender { - id: RawFd, - send_screen_instructions: SenderWithContext, -} - -impl VteEventSender { - pub fn new(id: RawFd, send_screen_instructions: SenderWithContext) -> Self { - VteEventSender { - id, - send_screen_instructions, - } - } -} - -impl vte::Perform for VteEventSender { - fn print(&mut self, c: char) { - self.send_screen_instructions - .send(ScreenInstruction::Pty(self.id, VteEvent::Print(c))) - .unwrap(); - } - fn execute(&mut self, byte: u8) { - 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_screen_instructions - .send(ScreenInstruction::Pty( - self.id, - VteEvent::Hook(params, intermediates, ignore, c), - )) - .unwrap(); - } - - fn put(&mut self, byte: u8) { - self.send_screen_instructions - .send(ScreenInstruction::Pty(self.id, VteEvent::Put(byte))) - .unwrap(); - } - - fn unhook(&mut self) { - 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_screen_instructions - .send(ScreenInstruction::Pty( - self.id, - VteEvent::OscDispatch(params, bell_terminated), - )) - .unwrap(); - } - - 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_screen_instructions - .send(ScreenInstruction::Pty( - self.id, - VteEvent::CsiDispatch(params, intermediates, ignore, c), - )) - .unwrap(); - } - - fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) { - let intermediates = intermediates.iter().copied().collect(); - self.send_screen_instructions - .send(ScreenInstruction::Pty( - self.id, - VteEvent::EscDispatch(intermediates, ignore, byte), - )) - .unwrap(); - } -} +pub type VteBytes = Vec; /// Instructions related to PTYs (pseudoterminals). #[derive(Clone, Debug, Serialize, Deserialize)] @@ -185,18 +92,15 @@ pub struct PtyBus { fn stream_terminal_bytes( pid: RawFd, + send_screen_instructions: SenderWithContext, os_input: Box, - mut send_screen_instructions: SenderWithContext, debug: bool, ) -> JoinHandle<()> { - let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); + let mut err_ctx = get_current_ctx(); task::spawn({ async move { err_ctx.add_call(ContextType::AsyncTask); - send_screen_instructions.update(err_ctx); - let mut vte_parser = vte::Parser::new(); - let mut vte_event_sender = VteEventSender::new(pid, send_screen_instructions.clone()); - let mut terminal_bytes = ReadFromPid::new(&pid, os_input.clone()); + let mut terminal_bytes = ReadFromPid::new(&pid, os_input); let mut last_byte_receive_time: Option = None; let mut pending_render = false; @@ -282,8 +186,8 @@ impl PtyBus { self.os_input.spawn_terminal(file_to_open); let task_handle = stream_terminal_bytes( pid_primary, - self.os_input.clone(), self.send_screen_instructions.clone(), + self.os_input.clone(), self.debug_to_file, ); self.task_handles.insert(pid_primary, task_handle); @@ -308,8 +212,8 @@ impl PtyBus { for id in new_pane_pids { let task_handle = stream_terminal_bytes( id, - self.os_input.clone(), self.send_screen_instructions.clone(), + self.os_input.clone(), self.debug_to_file, ); self.task_handles.insert(id, task_handle); diff --git a/src/common/screen.rs b/src/common/screen.rs index 92e1075c..8c83f59e 100644 --- a/src/common/screen.rs +++ b/src/common/screen.rs @@ -10,7 +10,7 @@ use std::sync::mpsc::Receiver; use crate::common::SenderWithContext; use crate::os_input_output::ServerOsApi; use crate::panes::PositionAndSize; -use crate::pty_bus::{PtyInstruction, VteEvent}; +use crate::pty_bus::{PtyInstruction, VteBytes}; use crate::server::ServerInstruction; use crate::tab::Tab; use crate::{errors::ErrorContext, wasm_vm::PluginInstruction}; diff --git a/src/main.rs b/src/main.rs index d89ff577..b9a4575e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,6 @@ use structopt::StructOpt; use crate::cli::CliArgs; use crate::command_is_executing::CommandIsExecuting; use crate::os_input_output::{get_client_os_input, get_server_os_input, ClientOsApi, ServerOsApi}; -use crate::pty_bus::VteEvent; use crate::utils::{ consts::{ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR}, logging::*, @@ -89,7 +88,7 @@ pub fn start( opts: CliArgs, server_os_input: Box, ) { - let ipc_thread = start_server(server_os_input, opts); - start_client(client_os_input); + let ipc_thread = start_server(server_os_input, opts.clone()); + start_client(client_os_input, opts); drop(ipc_thread.join()); } diff --git a/src/server/mod.rs b/src/server/mod.rs index 2495c13d..d8640836 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -16,14 +16,13 @@ use zellij_tile::data::{Event, EventType, ModeInfo}; use crate::cli::CliArgs; use crate::client::ClientInstruction; -use crate::common::pty_bus::VteEvent; use crate::common::{ - errors::{ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext, ServerContext}, + errors::{ContextType, PluginContext, PtyContext, ScreenContext, ServerContext}, os_input_output::ServerOsApi, - pty_bus::{PtyBus, PtyInstruction}, + pty_bus::{PtyBus, PtyInstruction, VteBytes}, screen::{Screen, ScreenInstruction}, wasm_vm::{wasi_stdout, wasi_write_string, zellij_imports, PluginEnv, PluginInstruction}, - ChannelWithContext, SenderType, SenderWithContext, OPENCALLS, + ChannelWithContext, SenderType, SenderWithContext, }; use crate::layout::Layout; use crate::panes::PaneId; @@ -102,9 +101,6 @@ impl ServerInstruction { pub fn resize_up() -> Self { Self::ToScreen(ScreenInstruction::ResizeUp) } - pub fn move_focus() -> Self { - Self::ToScreen(ScreenInstruction::MoveFocus) - } pub fn move_focus_left() -> Self { Self::ToScreen(ScreenInstruction::MoveFocusLeft) } @@ -171,8 +167,8 @@ impl ServerInstruction { pub fn change_mode(mode_info: ModeInfo) -> Self { Self::ToScreen(ScreenInstruction::ChangeMode(mode_info)) } - pub fn pty(fd: RawFd, event: VteEvent) -> Self { - Self::ToScreen(ScreenInstruction::Pty(fd, event)) + pub fn pty(fd: RawFd, bytes: VteBytes) -> Self { + Self::ToScreen(ScreenInstruction::PtyBytes(fd, bytes)) } pub fn terminal_resize(new_size: PositionAndSize) -> Self { Self::ToScreen(ScreenInstruction::TerminalResize(new_size)) @@ -188,14 +184,6 @@ struct ClientMetaData { wasm_thread: Option>, } -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); - } -} - impl Drop for ClientMetaData { fn drop(&mut self) { let _ = self.send_pty_instructions.send(PtyInstruction::Exit); @@ -211,18 +199,15 @@ pub fn start_server(mut os_input: Box, opts: CliArgs) -> thread let (send_server_instructions, receive_server_instructions): ChannelWithContext< ServerInstruction, > = channel(); - let send_server_instructions = SenderWithContext::new( - ErrorContext::new(), - SenderType::Sender(send_server_instructions), - ); + let send_server_instructions = + SenderWithContext::new(SenderType::Sender(send_server_instructions)); let router_thread = thread::Builder::new() .name("server_router".to_string()) .spawn({ let os_input = os_input.clone(); - let mut send_server_instructions = send_server_instructions.clone(); + let send_server_instructions = send_server_instructions.clone(); move || loop { - let (instruction, err_ctx) = os_input.server_recv(); - send_server_instructions.update(err_ctx); + let (instruction, _err_ctx) = os_input.server_recv(); match instruction { ServerInstruction::Exit => break, _ => { @@ -243,9 +228,6 @@ pub fn start_server(mut os_input: Box, opts: CliArgs) -> thread let (instruction, mut err_ctx) = receive_server_instructions.recv().unwrap(); err_ctx.add_call(ContextType::IPCServer(ServerContext::from(&instruction))); 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); @@ -269,7 +251,7 @@ pub fn start_server(mut os_input: Box, opts: CliArgs) -> thread ServerInstruction::MoveFocus => { clients[client.as_ref().unwrap()] .send_screen_instructions - .send(ScreenInstruction::MoveFocus) + .send(ScreenInstruction::FocusNextPane) .unwrap(); } ServerInstruction::NewClient(buffer_path, full_screen_ws) => { @@ -340,22 +322,20 @@ fn init_client( send_server_instructions: SenderWithContext, 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)); + SenderWithContext::new(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)); + SenderWithContext::new(SenderType::Sender(send_plugin_instructions)); let (send_pty_instructions, receive_pty_instructions): ChannelWithContext = channel(); - let send_pty_instructions = - SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions)); + let send_pty_instructions = SenderWithContext::new(SenderType::Sender(send_pty_instructions)); // Don't use default layouts in tests, but do everywhere else #[cfg(not(test))] @@ -461,15 +441,24 @@ fn init_client( .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::PtyBytes(pid, vte_bytes) => { + let active_tab = screen.get_active_tab_mut().unwrap(); + if active_tab.has_terminal_pid(pid) { + // it's most likely that this event is directed at the active tab + // look there first + active_tab.handle_pty_bytes(pid, vte_bytes); + } else { + // if this event wasn't directed at the active tab, start looking + // in other tabs + let all_tabs = screen.get_tabs_mut(); + for tab in all_tabs.values_mut() { + if tab.has_terminal_pid(pid) { + tab.handle_pty_bytes(pid, vte_bytes); + break; + } + } + } } ScreenInstruction::Render => { screen.render(); @@ -513,9 +502,15 @@ fn init_client( ScreenInstruction::ResizeUp => { screen.get_active_tab_mut().unwrap().resize_up(); } - ScreenInstruction::MoveFocus => { + ScreenInstruction::SwitchFocus => { screen.get_active_tab_mut().unwrap().move_focus(); } + ScreenInstruction::FocusNextPane => { + screen.get_active_tab_mut().unwrap().focus_next_pane(); + } + ScreenInstruction::FocusPreviousPane => { + screen.get_active_tab_mut().unwrap().focus_previous_pane(); + } ScreenInstruction::MoveFocusLeft => { screen.get_active_tab_mut().unwrap().move_focus_left(); } @@ -646,8 +641,8 @@ fn init_client( 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 send_screen_instructions = send_screen_instructions.clone(); + let send_pty_instructions = send_pty_instructions.clone(); let store = Store::default(); let mut plugin_id = 0; @@ -657,8 +652,6 @@ fn init_client( .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 = diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs index 79511743..271edde0 100644 --- a/src/tests/fakes.rs +++ b/src/tests/fakes.rs @@ -89,12 +89,10 @@ impl FakeInputOutput { let stdout_writer = FakeStdoutWriter::new(last_snapshot_time.clone()); let (client_sender, client_receiver): ChannelWithContext = mpsc::channel(); - let client_sender = - SenderWithContext::new(ErrorContext::new(), SenderType::Sender(client_sender)); + let client_sender = SenderWithContext::new(SenderType::Sender(client_sender)); let (server_sender, server_receiver): ChannelWithContext = mpsc::channel(); - let server_sender = - SenderWithContext::new(ErrorContext::new(), SenderType::Sender(server_sender)); + let server_sender = SenderWithContext::new(SenderType::Sender(server_sender)); win_sizes.insert(0, winsize); // 0 is the current terminal FakeInputOutput { @@ -198,10 +196,7 @@ impl ClientOsApi for FakeInputOutput { fn send_to_server(&self, msg: ServerInstruction) { self.server_sender.send(msg).unwrap(); } - fn update_senders(&mut self, new_ctx: ErrorContext) { - self.server_sender.update(new_ctx); - self.client_sender.update(new_ctx); - } + fn update_senders(&mut self, new_ctx: ErrorContext) {} fn connect_to_server(&mut self, full_screen_ws: PositionAndSize) { ClientOsApi::send_to_server( self, @@ -293,11 +288,5 @@ impl ServerOsApi for FakeInputOutput { self.client_sender.send(msg).unwrap(); } fn add_client_sender(&mut self, _buffer_path: String) {} - fn update_senders(&mut self, new_ctx: ErrorContext) { - self.server_sender.update(new_ctx); - self.client_sender.update(new_ctx); - } - fn load_palette(&self) -> Palette { - default_palette() - } + fn update_senders(&mut self, new_ctx: ErrorContext) {} }