summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock71
-rw-r--r--Cargo.toml3
-rw-r--r--src/cli.rs17
-rw-r--r--src/client/mod.rs170
-rw-r--r--src/client/pane_resizer.rs6
-rw-r--r--src/client/panes/terminal_pane.rs5
-rw-r--r--src/client/tab.rs78
-rw-r--r--src/common/command_is_executing.rs50
-rw-r--r--src/common/errors.rs92
-rw-r--r--src/common/input/handler.rs215
-rw-r--r--src/common/ipc.rs74
-rw-r--r--src/common/mod.rs648
-rw-r--r--src/common/os_input_output.rs237
-rw-r--r--src/common/pty_bus.rs48
-rw-r--r--src/common/screen.rs54
-rw-r--r--src/common/setup.rs8
-rw-r--r--src/common/utils/consts.rs37
-rw-r--r--src/common/utils/logging.rs29
-rw-r--r--src/common/wasm_vm.rs7
-rw-r--r--src/main.rs66
-rw-r--r--src/server/mod.rs814
-rw-r--r--src/tests/fakes.rs183
-rw-r--r--src/tests/integration/basic.rs25
-rw-r--r--src/tests/integration/close_pane.rs13
-rw-r--r--src/tests/integration/compatibility.rs21
-rw-r--r--src/tests/integration/layouts.rs7
-rw-r--r--src/tests/integration/move_focus_down.rs2
-rw-r--r--src/tests/integration/move_focus_left.rs2
-rw-r--r--src/tests/integration/move_focus_right.rs2
-rw-r--r--src/tests/integration/move_focus_up.rs2
-rw-r--r--src/tests/integration/resize_down.rs13
-rw-r--r--src/tests/integration/resize_left.rs13
-rw-r--r--src/tests/integration/resize_right.rs13
-rw-r--r--src/tests/integration/resize_up.rs13
-rw-r--r--src/tests/integration/tabs.rs8
-rw-r--r--src/tests/integration/terminal_window_resize.rs28
-rw-r--r--src/tests/integration/toggle_fullscreen.rs2
37 files changed, 1876 insertions, 1200 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3d04bc27f..b1fbaf3e9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -599,6 +599,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
+name = "fuchsia-cprng"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
+
+[[package]]
name = "funty"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1032,6 +1038,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238"
[[package]]
+name = "names"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef320dab323286b50fb5cdda23f61c796a72a89998ab565ca32525c5c556f2da"
+dependencies = [
+ "rand 0.3.23",
+]
+
+[[package]]
name = "nix"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1203,13 +1218,36 @@ checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]]
name = "rand"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
+dependencies = [
+ "libc",
+ "rand 0.4.6",
+]
+
+[[package]]
+name = "rand"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
+dependencies = [
+ "fuchsia-cprng",
+ "libc",
+ "rand_core 0.3.1",
+ "rdrand",
+ "winapi",
+]
+
+[[package]]
+name = "rand"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha",
- "rand_core",
+ "rand_core 0.6.2",
"rand_hc",
]
@@ -1220,11 +1258,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.6.2",
]
[[package]]
name = "rand_core"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
+dependencies = [
+ "rand_core 0.4.2",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
+
+[[package]]
+name = "rand_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
@@ -1238,7 +1291,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
- "rand_core",
+ "rand_core 0.6.2",
]
[[package]]
@@ -1267,6 +1320,15 @@ dependencies = [
]
[[package]]
+name = "rdrand"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
+dependencies = [
+ "rand_core 0.3.1",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1593,7 +1655,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if 1.0.0",
"libc",
- "rand",
+ "rand 0.8.3",
"redox_syscall",
"remove_dir_all",
"winapi",
@@ -2205,6 +2267,7 @@ dependencies = [
"interprocess",
"lazy_static",
"libc",
+ "names",
"nix",
"nom",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
index 705a09066..f59b1e8b3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,7 +35,8 @@ strum = "0.20.0"
lazy_static = "1.4.0"
wasmer = "1.0.0"
wasmer-wasi = "1.0.0"
-interprocess = "1.0.1"
+interprocess = "1.1.1"
+names = "0.11.0"
colors-transform = "0.2.5"
zellij-tile = { path = "zellij-tile/", version = "0.7.0" }
diff --git a/src/cli.rs b/src/cli.rs
index bc4e4a9e6..91b0ed39d 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,22 +1,11 @@
use super::common::utils::consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV};
+use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use structopt::StructOpt;
-#[derive(StructOpt, Default, Debug)]
+#[derive(StructOpt, Default, Debug, Clone, Serialize, Deserialize)]
#[structopt(name = "zellij")]
pub struct CliArgs {
- /// Send "split (direction h == horizontal / v == vertical)" to active zellij session
- #[structopt(short, long)]
- pub split: Option<char>,
-
- /// Send "move focused pane" to active zellij session
- #[structopt(short, long)]
- pub move_focus: bool,
-
- /// Send "open file in new pane" to active zellij session
- #[structopt(short, long)]
- pub open_file: Option<PathBuf>,
-
/// Maximum panes on screen, caution: opening more panes will close old ones
#[structopt(long)]
pub max_panes: Option<usize>,
@@ -44,7 +33,7 @@ pub struct CliArgs {
pub debug: bool,
}
-#[derive(Debug, StructOpt)]
+#[derive(Debug, StructOpt, Clone, Serialize, Deserialize)]
pub enum ConfigCli {
/// Change the behaviour of zellij
#[structopt(name = "option")]
diff --git a/src/client/mod.rs b/src/client/mod.rs
index cba12a46c..8a5834f06 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -4,4 +4,172 @@ pub mod pane_resizer;
pub mod panes;
pub mod tab;
-pub fn _start_client() {}
+use serde::{Deserialize, Serialize};
+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,
+};
+use crate::server::ServerInstruction;
+
+/// Instructions related to the client-side application and sent from server to client
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub enum ClientInstruction {
+ Error(String),
+ Render(Option<String>),
+ UnblockInputThread,
+ Exit,
+}
+
+pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: Config) {
+ let take_snapshot = "\u{1b}[?1049h";
+ os_input.unset_raw_mode(0);
+ let _ = os_input
+ .get_stdout_writer()
+ .write(take_snapshot.as_bytes())
+ .unwrap();
+ std::env::set_var(&"ZELLIJ", "0");
+
+ let mut command_is_executing = CommandIsExecuting::new();
+
+ let full_screen_ws = os_input.get_terminal_size_using_fd(0);
+ os_input.connect_to_server();
+ os_input.send_to_server(ServerInstruction::NewClient(full_screen_ws, opts));
+ os_input.set_raw_mode(0);
+
+ let (send_client_instructions, receive_client_instructions): SyncChannelWithContext<
+ ClientInstruction,
+ > = mpsc::sync_channel(50);
+ let send_client_instructions =
+ SenderWithContext::new(SenderType::SyncSender(send_client_instructions));
+
+ #[cfg(not(test))]
+ std::panic::set_hook({
+ use crate::errors::handle_panic;
+ let send_client_instructions = send_client_instructions.clone();
+ Box::new(move |info| {
+ handle_panic(info, &send_client_instructions);
+ })
+ });
+
+ let _stdin_thread = thread::Builder::new()
+ .name("stdin_handler".to_string())
+ .spawn({
+ 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,
+ config,
+ command_is_executing,
+ send_client_instructions,
+ )
+ }
+ });
+
+ let _signal_thread = thread::Builder::new()
+ .name("signal_listener".to_string())
+ .spawn({
+ let os_input = os_input.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),
+ ));
+ }
+ }));
+ }
+ })
+ .unwrap();
+
+ let router_thread = thread::Builder::new()
+ .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;
+ }
+ send_client_instructions.send(instruction).unwrap();
+ }
+ send_client_instructions
+ .send(ClientInstruction::Exit)
+ .unwrap();
+ }
+ })
+ .unwrap();
+
+ #[warn(clippy::never_loop)]
+ 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,
+ )));
+ 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);
+ }
+ ClientInstruction::Render(output) => {
+ if output.is_none() {
+ break;
+ }
+ let mut stdout = os_input.get_stdout_writer();
+ stdout
+ .write_all(&output.unwrap().as_bytes())
+ .expect("cannot write to stdout");
+ stdout.flush().expect("could not flush");
+ }
+ ClientInstruction::UnblockInputThread => {
+ command_is_executing.unblock_input_thread();
+ }
+ }
+ }
+
+ let _ = os_input.send_to_server(ServerInstruction::ClientExit);
+ router_thread.join().unwrap();
+
+ // cleanup();
+ let reset_style = "\u{1b}[m";
+ let show_cursor = "\u{1b}[?25h";
+ let restore_snapshot = "\u{1b}[?1049l";
+ let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
+ let goodbye_message = format!(
+ "{}\n{}{}{}Bye from Zellij!\n",
+ goto_start_of_last_line, restore_snapshot, reset_style, show_cursor
+ );
+
+ os_input.unset_raw_mode(0);
+ let mut stdout = os_input.get_stdout_writer();
+ let _ = stdout.write(goodbye_message.as_bytes()).unwrap();
+ stdout.flush().unwrap();
+}
diff --git a/src/client/pane_resizer.rs b/src/client/pane_resizer.rs
index f9ec71b82..73678499c 100644
--- a/src/client/pane_resizer.rs
+++ b/src/client/pane_resizer.rs
@@ -1,4 +1,4 @@
-use crate::os_input_output::OsApi;
+use crate::os_input_output::ServerOsApi;
use crate::panes::{PaneId, PositionAndSize};
use crate::tab::Pane;
use std::{
@@ -8,7 +8,7 @@ use std::{
pub struct PaneResizer<'a> {
panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
- os_api: &'a mut Box<dyn OsApi>,
+ os_api: &'a mut Box<dyn ServerOsApi>,
}
// TODO: currently there are some functions here duplicated with Tab
@@ -17,7 +17,7 @@ pub struct PaneResizer<'a> {
impl<'a> PaneResizer<'a> {
pub fn new(
panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
- os_api: &'a mut Box<dyn OsApi>,
+ os_api: &'a mut Box<dyn ServerOsApi>,
) -> Self {
PaneResizer { panes, os_api }
}
diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs
index a5a957e67..f58c3a273 100644
--- a/src/client/panes/terminal_pane.rs
+++ b/src/client/panes/terminal_pane.rs
@@ -1,6 +1,7 @@
use crate::tab::Pane;
use ::nix::pty::Winsize;
use ::std::os::unix::io::RawFd;
+use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::time::Instant;
@@ -10,7 +11,7 @@ use crate::panes::terminal_character::{
};
use crate::pty_bus::VteBytes;
-#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)]
+#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum PaneId {
Terminal(RawFd),
Plugin(u32), // FIXME: Drop the trait object, make this a wrapper for the struct?
@@ -18,7 +19,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 b01e04e57..0098d78b6 100644
--- a/src/client/tab.rs
+++ b/src/client/tab.rs
@@ -2,22 +2,23 @@
//! as well as how they should be resized
use crate::client::pane_resizer::PaneResizer;
-use crate::common::{input::handler::parse_keys, AppInstruction, SenderWithContext};
+use crate::common::{input::handler::parse_keys, SenderWithContext};
use crate::layout::Layout;
-use crate::os_input_output::OsApi;
+use crate::os_input_output::ServerOsApi;
use crate::panes::{PaneId, PositionAndSize, TerminalPane};
use crate::pty_bus::{PtyInstruction, VteBytes};
+use crate::server::ServerInstruction;
use crate::utils::shared::adjust_to_size;
use crate::wasm_vm::PluginInstruction;
use crate::{boundaries::Boundaries, panes::PluginPane};
use serde::{Deserialize, Serialize};
use std::os::unix::io::RawFd;
+use std::sync::mpsc::channel;
use std::time::Instant;
use std::{
cmp::Reverse,
collections::{BTreeMap, HashSet},
};
-use std::{io::Write, sync::mpsc::channel};
use zellij_tile::data::{Event, InputMode, ModeInfo, Palette};
const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
@@ -67,11 +68,11 @@ pub struct Tab {
max_panes: Option<usize>,
full_screen_ws: PositionAndSize,
fullscreen_is_active: bool,
+ os_api: Box<dyn ServerOsApi>,
+ send_plugin_instructions: SenderWithContext<PluginInstruction>,
+ send_pty_instructions: SenderWithContext<PtyInstruction>,
+ send_server_instructions: SenderWithContext<ServerInstruction>,
synchronize_is_active: bool,
- os_api: Box<dyn OsApi>,
- pub send_pty_instructions: SenderWithContext<PtyInstruction>,
- pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
- pub send_app_instructions: SenderWithContext<AppInstruction>,
should_clear_display_before_rendering: bool,
pub mode_info: ModeInfo,
pub input_mode: InputMode,
@@ -225,10 +226,10 @@ impl Tab {
position: usize,
name: String,
full_screen_ws: &PositionAndSize,
- mut os_api: Box<dyn OsApi>,
- send_pty_instructions: SenderWithContext<PtyInstruction>,
+ 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,
@@ -260,9 +261,9 @@ impl Tab {
fullscreen_is_active: false,
synchronize_is_active: false,
os_api,
- send_app_instructions,
- send_pty_instructions,
send_plugin_instructions,
+ send_pty_instructions,
+ send_server_instructions,
should_clear_display_before_rendering: false,
mode_info,
input_mode,
@@ -400,8 +401,8 @@ impl Tab {
);
if terminal_id_to_split.is_none() {
self.send_pty_instructions
- .send(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty
- .unwrap();
+ .send(PtyInstruction::ClosePane(pid))
+ .unwrap(); // we can't open this pane, close the pty
return; // likely no terminal large enough to split
}