diff options
author | a-kenji <aks.kenji@protonmail.com> | 2021-05-16 12:44:56 +0200 |
---|---|---|
committer | a-kenji <aks.kenji@protonmail.com> | 2021-05-16 12:44:56 +0200 |
commit | 197f31875646a5b78abd23edecee072931b2b14b (patch) | |
tree | a8fb17d057d77c0a5090c5e1878ecd5618ec592d /src | |
parent | abf0a2d0c624046d4660000b8b16acabe7cb5b6e (diff) | |
parent | 8d742ccc53770032d8d5891a6ff43875f4987ede (diff) |
Merge branch 'main' of https://github.com/zellij-org/zellij into layout-path-506
Diffstat (limited to 'src')
30 files changed, 411 insertions, 414 deletions
diff --git a/src/cli.rs b/src/cli.rs index 206ebe9d3..b8a613443 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -13,11 +13,15 @@ pub struct CliArgs { pub max_panes: Option<usize>, /// Change where zellij looks for layouts and plugins - #[structopt(long)] + #[structopt(long, parse(from_os_str))] pub data_dir: Option<PathBuf>, - /// Name of a layout yaml file inside the plugin directory - #[structopt(short, long)] + /// Run server listening at the specified socket path + #[structopt(long, parse(from_os_str))] + pub server: Option<PathBuf>, + + /// Path to a layout yaml file + #[structopt(short, long, parse(from_os_str))] pub layout: Option<PathBuf>, /// Path to a layout yaml file @@ -25,11 +29,11 @@ pub struct CliArgs { pub layout_path: Option<PathBuf>, /// Change where zellij looks for the configuration - #[structopt(short, long, env=ZELLIJ_CONFIG_FILE_ENV)] + #[structopt(short, long, env=ZELLIJ_CONFIG_FILE_ENV, parse(from_os_str))] pub config: Option<PathBuf>, /// Change where zellij looks for the configuration - #[structopt(long, env=ZELLIJ_CONFIG_DIR_ENV)] + #[structopt(long, env=ZELLIJ_CONFIG_DIR_ENV, parse(from_os_str))] pub config_dir: Option<PathBuf>, #[structopt(subcommand)] diff --git a/src/client/mod.rs b/src/client/mod.rs index 6f298615f..862c13ccf 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -4,30 +4,62 @@ pub mod pane_resizer; pub mod panes; pub mod tab; -use serde::{Deserialize, Serialize}; -use std::io::Write; +use std::env::current_exe; +use std::io::{self, Write}; +use std::path::Path; +use std::process::Command; use std::sync::mpsc; use std::thread; use crate::cli::CliArgs; use crate::common::{ command_is_executing::CommandIsExecuting, - errors::{ClientContext, ContextType}, + errors::ContextType, input::config::Config, input::handler::input_loop, input::options::Options, + ipc::{ClientToServerMsg, ServerToClientMsg}, os_input_output::ClientOsApi, thread_bus::{SenderType, SenderWithContext, SyncChannelWithContext}, + utils::consts::ZELLIJ_IPC_PIPE, }; -use crate::server::ServerInstruction; /// Instructions related to the client-side application and sent from server to client -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Debug, Clone)] pub enum ClientInstruction { Error(String), Render(Option<String>), UnblockInputThread, Exit, + ServerError(String), +} + +impl From<ServerToClientMsg> for ClientInstruction { + fn from(instruction: ServerToClientMsg) -> Self { + match instruction { + ServerToClientMsg::Exit => ClientInstruction::Exit, + ServerToClientMsg::Render(buffer) => ClientInstruction::Render(buffer), + ServerToClientMsg::UnblockInputThread => ClientInstruction::UnblockInputThread, + ServerToClientMsg::ServerError(backtrace) => ClientInstruction::ServerError(backtrace), + } + } +} + +fn spawn_server(socket_path: &Path) -> io::Result<()> { + let status = Command::new(current_exe()?) + .arg("--server") + .arg(socket_path) + .status()?; + if status.success() { + Ok(()) + } else { + let msg = "Process returned non-zero exit code"; + let err_msg = match status.code() { + Some(c) => format!("{}: {}", msg, c), + None => msg.to_string(), + }; + Err(io::Error::new(io::ErrorKind::Other, err_msg)) + } } pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: Config) { @@ -45,13 +77,16 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C .unwrap(); std::env::set_var(&"ZELLIJ", "0"); + #[cfg(not(test))] + spawn_server(&*ZELLIJ_IPC_PIPE).unwrap(); + let mut command_is_executing = CommandIsExecuting::new(); let config_options = Options::from_cli(&config.options, opts.option.clone()); let full_screen_ws = os_input.get_terminal_size_using_fd(0); - os_input.connect_to_server(); - os_input.send_to_server(ServerInstruction::NewClient( + os_input.connect_to_server(&*ZELLIJ_IPC_PIPE); + os_input.send_to_server(ClientToServerMsg::NewClient( full_screen_ws, opts, config_options, @@ -97,15 +132,26 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C .name("signal_listener".to_string()) .spawn({ let os_input = os_input.clone(); + let send_client_instructions = send_client_instructions.clone(); move || { - os_input.receive_sigwinch(Box::new({ - let os_api = os_input.clone(); - move || { - os_api.send_to_server(ServerInstruction::TerminalResize( - os_api.get_terminal_size_using_fd(0), - )); - } - })); + os_input.handle_signals( + Box::new({ + let os_api = os_input.clone(); + move || { + os_api.send_to_server(ClientToServerMsg::TerminalResize( + os_api.get_terminal_size_using_fd(0), + )); + } + }), + Box::new({ + let send_client_instructions = send_client_instructions.clone(); + move || { + send_client_instructions + .send(ClientInstruction::Exit) + .unwrap() + } + }), + ); } }) .unwrap(); @@ -114,47 +160,53 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C .name("router".to_string()) .spawn({ let os_input = os_input.clone(); - move || { - loop { - let (instruction, mut err_ctx) = os_input.recv_from_server(); - err_ctx.add_call(ContextType::Client(ClientContext::from(&instruction))); - if let ClientInstruction::Exit = instruction { - break; + let mut should_break = false; + move || loop { + let (instruction, err_ctx) = os_input.recv_from_server(); + err_ctx.update_thread_ctx(); + match instruction { + ServerToClientMsg::Exit | ServerToClientMsg::ServerError(_) => { + should_break = true; } - send_client_instructions.send(instruction).unwrap(); + _ => {} + } + send_client_instructions.send(instruction.into()).unwrap(); + if should_break { + break; } - send_client_instructions - .send(ClientInstruction::Exit) - .unwrap(); } }) .unwrap(); - #[warn(clippy::never_loop)] + let handle_error = |backtrace: String| { + os_input.unset_raw_mode(0); + let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1); + 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()) + .unwrap(); + std::process::exit(1); + }; + loop { let (client_instruction, mut err_ctx) = receive_client_instructions .recv() .expect("failed to receive app instruction on channel"); - err_ctx.add_call(ContextType::Client(ClientContext::from( - &client_instruction, - ))); + err_ctx.add_call(ContextType::Client((&client_instruction).into())); match client_instruction { ClientInstruction::Exit => break, ClientInstruction::Error(backtrace) => { - 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 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()) - .unwrap(); - std::process::exit(1); + let _ = os_input.send_to_server(ClientToServerMsg::ClientExit); + handle_error(backtrace); + } + ClientInstruction::ServerError(backtrace) => { + handle_error(backtrace); } ClientInstruction::Render(output) => { if output.is_none() { @@ -172,7 +224,7 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C } } - let _ = os_input.send_to_server(ServerInstruction::ClientExit); + let _ = os_input.send_to_server(ClientToServerMsg::ClientExit); router_thread.join().unwrap(); // cleanup(); diff --git a/src/common/errors.rs b/src/common/errors.rs index c2caf19d6..262dfd6bc 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -19,12 +19,29 @@ const MAX_THREAD_CALL_STACK: usize = 6; use super::thread_bus::SenderWithContext; #[cfg(not(test))] use std::panic::PanicInfo; + +pub trait ErrorInstruction { + fn error(err: String) -> Self; +} + +impl ErrorInstruction for ClientInstruction { + fn error(err: String) -> Self { + ClientInstruction::Error(err) + } +} + +impl ErrorInstruction for ServerInstruction { + fn error(err: String) -> Self { + ServerInstruction::Error(err) + } +} + /// Custom panic handler/hook. Prints the [`ErrorContext`]. #[cfg(not(test))] -pub fn handle_panic( - info: &PanicInfo<'_>, - send_app_instructions: &SenderWithContext<ClientInstruction>, -) { +pub fn handle_panic<T>(info: &PanicInfo<'_>, sender: &SenderWithContext<T>) +where + T: ErrorInstruction + Clone, +{ use backtrace::Backtrace; use std::{process, thread}; let backtrace = Backtrace::new(); @@ -70,7 +87,7 @@ pub fn handle_panic( println!("{}", backtrace); process::exit(1); } else { - let _ = send_app_instructions.send(ClientInstruction::Error(backtrace)); + let _ = sender.send(T::error(backtrace)); } } @@ -103,6 +120,11 @@ impl ErrorContext { break; } } + self.update_thread_ctx() + } + + /// Updates the thread local [`ErrorContext`]. + pub fn update_thread_ctx(&self) { ASYNCOPENCALLS .try_with(|ctx| *ctx.borrow_mut() = *self) .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self)); @@ -333,6 +355,7 @@ pub enum ClientContext { Error, UnblockInputThread, Render, + ServerError, } impl From<&ClientInstruction> for ClientContext { @@ -340,6 +363,7 @@ impl From<&ClientInstruction> for ClientContext { match *client_instruction { ClientInstruction::Exit => ClientContext::Exit, ClientInstruction::Error(_) => ClientContext::Error, + ClientInstruction::ServerError(_) => ClientContext::ServerError, ClientInstruction::Render(_) => ClientContext::Render, ClientInstruction::UnblockInputThread => ClientContext::UnblockInputThread, } @@ -350,22 +374,20 @@ impl From<&ClientInstruction> for ClientContext { #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub enum ServerContext { NewClient, - Action, Render, - TerminalResize, UnblockInputThread, ClientExit, + Error, } impl From<&ServerInstruction> for ServerContext { fn from(server_instruction: &ServerInstruction) -> Self { match *server_instruction { ServerInstruction::NewClient(..) => ServerContext::NewClient, - ServerInstruction::Action(_) => ServerContext::Action, - ServerInstruction::TerminalResize(_) => ServerContext::TerminalResize, ServerInstruction::Render(_) => ServerContext::Render, ServerInstruction::UnblockInputThread => ServerContext::UnblockInputThread, ServerInstruction::ClientExit => ServerContext::ClientExit, + ServerInstruction::Error(_) => ServerContext::Error, } } } diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index b2f4d0a32..4ac2cf839 100644 --- a/src/common/input/handler.rs +++ b/src/common/input/handler.rs @@ -4,10 +4,10 @@ use super::actions::Action; use super::keybinds::Keybinds; use crate::client::ClientInstruction; use crate::common::input::config::Config; +use crate::common::ipc::ClientToServerMsg; use crate::common::thread_bus::{SenderWithContext, OPENCALLS}; use crate::errors::ContextType; use crate::os_input_output::ClientOsApi; -use crate::server::ServerInstruction; use crate::CommandIsExecuting; use termion::input::{TermRead, TermReadEventsAndRaw}; @@ -139,7 +139,7 @@ impl InputHandler { Action::SwitchToMode(mode) => { self.mode = mode; self.os_input - .send_to_server(ServerInstruction::Action(action)); + .send_to_server(ClientToServerMsg::Action(action)); } Action::CloseFocus | Action::NewPane(_) @@ -151,13 +151,13 @@ impl InputHandler { | Action::MoveFocusOrTab(_) => { self.command_is_executing.blocking_input_thread(); self.os_input - .send_to_server(ServerInstruction::Action(action)); + .send_to_server(ClientToServerMsg::Action(action)); self.command_is_executing .wait_until_input_thread_is_unblocked(); } _ => self .os_input - .send_to_server(ServerInstruction::Action(action)), + .send_to_server(ClientToServerMsg::Action(action)), } should_break diff --git a/src/common/ipc.rs b/src/common/ipc.rs index 190680dce..129727a1a 100644 --- a/src/common/ipc.rs +++ b/src/common/ipc.rs @@ -1,10 +1,14 @@ //! IPC stuff for starting to split things into a client and server model. -use crate::common::errors::{get_current_ctx, ErrorContext}; +use crate::cli::CliArgs; +use crate::common::{ + errors::{get_current_ctx, ErrorContext}, + input::{actions::Action, options::Options}, +}; +use crate::panes::PositionAndSize; use interprocess::local_socket::LocalSocketStream; use nix::unistd::dup; use serde::{Deserialize, Serialize}; -use std::collections::HashSet; use std::io::{self, Write}; use std::marker::PhantomData; use std::os::unix::io::{AsRawFd, FromRawFd}; @@ -29,9 +33,9 @@ pub enum ClientType { } // Types of messages sent from the client to the server -#[derive(Serialize, Deserialize)] -pub enum _ClientToServerMsg { - // List which sessions are available +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum ClientToServerMsg { + /*// List which sessions are available ListSessions, // Create a new session CreateSession, @@ -40,16 +44,24 @@ pub enum _ClientToServerMsg { // Force detach DetachSession(SessionId), // Disconnect from the session we're connected to - DisconnectFromSession, + DisconnectFromSession,*/ + ClientExit, + TerminalResize(PositionAndSize), + NewClient(PositionAndSize, CliArgs, Options), + Action(Action), } // Types of messages sent from the server to the client -// @@@ Implement Serialize and Deserialize for this... -pub enum _ServerToClientMsg { - // Info about a particular session +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum ServerToClientMsg { + /*// Info about a particular session SessionInfo(Session), // A list of sessions - SessionList(HashSet<Session>), + SessionList(HashSet<Session>),*/ + Render(Option<String>), + UnblockInputThread, + Exit, + ServerError(String), } /// Sends messages on a stream socket, along with an [`ErrorContext`]. diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs index 6f891e0d5..adde6fdff 100644 --- a/src/common/os_input_output.rs +++ b/src/common/os_input_output.rs @@ -1,11 +1,8 @@ -use crate::client::ClientInstruction; -use crate::common::{ - ipc::{IpcReceiverWithContext, IpcSenderWithContext}, - utils::consts::ZELLIJ_IPC_PIPE, +use crate::common::ipc::{ + ClientToServerMsg, IpcReceiverWithContext, IpcSenderWithContext, ServerToClientMsg, }; use crate::errors::ErrorContext; use crate::panes::PositionAndSize; -use crate::server::ServerInstruction; use crate::utils::shared::default_palette; use interprocess::local_socket::LocalSocketStream; use nix::fcntl::{fcntl, FcntlArg, OFlag}; @@ -170,8 +167,8 @@ fn spawn_terminal(file_to_open: Option<PathBuf>, orig_termios: termios::Termios) #[derive(Clone)] pub struct ServerOsInputOutput { orig_termios: Arc<Mutex<termios::Termios>>, - receive_instructions_from_client: Option<Arc<Mutex<IpcReceiverWithContext<ServerInstruction>>>>, - send_instructions_to_client: Arc<Mutex<Option<IpcSenderWithContext<ClientInstruction>>>>, + receive_instructions_from_client: Option<Arc<Mutex<IpcReceiverWithContext<ClientToServerMsg>>>>, + send_instructions_to_client: Arc<Mutex<Option<IpcSenderWithContext<ServerToClientMsg>>>>, } /// The `ServerOsApi` trait represents an abstract interface to the features of an operating system that @@ -195,9 +192,9 @@ pub trait ServerOsApi: Send + Sync { /// Returns a [`Box`] pointer to this [`ServerOsApi`] struct. fn box_clone(&self) -> Box<dyn ServerOsApi>; /// Receives a message on server-side IPC channel - fn recv_from_client(&self) -> (ServerInstruction, ErrorContext); + fn recv_from_client(&self) -> (ClientToServerMsg, ErrorContext); /// Sends a message to client - fn send_to_client(&self, msg: ClientInstruction); + fn send_to_client(&self, msg: ServerToClientMsg); /// Adds a sender to client fn add_client_sender(&mut self); |