summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authora-kenji <aks.kenji@protonmail.com>2021-05-16 12:44:56 +0200
committera-kenji <aks.kenji@protonmail.com>2021-05-16 12:44:56 +0200
commit197f31875646a5b78abd23edecee072931b2b14b (patch)
treea8fb17d057d77c0a5090c5e1878ecd5618ec592d /src
parentabf0a2d0c624046d4660000b8b16acabe7cb5b6e (diff)
parent8d742ccc53770032d8d5891a6ff43875f4987ede (diff)
Merge branch 'main' of https://github.com/zellij-org/zellij into layout-path-506
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs14
-rw-r--r--src/client/mod.rs138
-rw-r--r--src/common/errors.rs40
-rw-r--r--src/common/input/handler.rs8
-rw-r--r--src/common/ipc.rs32
-rw-r--r--src/common/os_input_output.rs67
-rw-r--r--src/common/pty.rs4
-rw-r--r--src/common/screen.rs4
-rw-r--r--src/common/utils/consts.rs4
-rw-r--r--src/common/wasm_vm.rs4
-rw-r--r--src/main.rs32
-rw-r--r--src/server/mod.rs183
-rw-r--r--src/server/route.rs42
-rw-r--r--src/tests/fakes.rs31
-rw-r--r--src/tests/integration/basic.rs19
-rw-r--r--src/tests/integration/close_pane.rs18
-rw-r--r--src/tests/integration/compatibility.rs25
-rw-r--r--src/tests/integration/layouts.rs6
-rw-r--r--src/tests/integration/move_focus_down.rs7
-rw-r--r--src/tests/integration/move_focus_left.rs8
-rw-r--r--src/tests/integration/move_focus_right.rs8
-rw-r--r--src/tests/integration/move_focus_up.rs7
-rw-r--r--src/tests/integration/resize_down.rs18
-rw-r--r--src/tests/integration/resize_left.rs18
-rw-r--r--src/tests/integration/resize_right.rs18
-rw-r--r--src/tests/integration/resize_up.rs18
-rw-r--r--src/tests/integration/tabs.rs13
-rw-r--r--src/tests/integration/terminal_window_resize.rs9
-rw-r--r--src/tests/integration/toggle_fullscreen.rs7
-rw-r--r--src/tests/mod.rs23
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);</