summaryrefslogtreecommitdiffstats
path: root/zellij-server
diff options
context:
space:
mode:
authora-kenji <aks.kenji@protonmail.com>2021-05-18 10:05:15 +0200
committera-kenji <aks.kenji@protonmail.com>2021-05-18 10:05:15 +0200
commitbcbde9fbb5bffe4e1334acc1270314517938cac5 (patch)
tree8a18820a32392fb4a8a132a27740299ac4849f84 /zellij-server
parentdc067580f3df50bff17aee48fb330c164084c6bd (diff)
parent8c3bf215b7c56fb1254e55ac182233ac488f3113 (diff)
Merge branch 'main' of https://github.com/zellij-org/zellij into layout-path-506
Diffstat (limited to 'zellij-server')
-rw-r--r--zellij-server/Cargo.toml37
-rw-r--r--zellij-server/src/lib.rs318
-rw-r--r--zellij-server/src/os_input_output.rs244
-rw-r--r--zellij-server/src/panes/grid.rs1720
-rw-r--r--zellij-server/src/panes/mod.rs9
-rw-r--r--zellij-server/src/panes/plugin_pane.rs227
-rw-r--r--zellij-server/src/panes/terminal_character.rs781
-rw-r--r--zellij-server/src/panes/terminal_pane.rs343
-rw-r--r--zellij-server/src/panes/unit/grid_tests.rs398
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__csi_b.snap8
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__csi_capital_i.snap8
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__csi_capital_z.snap8
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__terminal_reports.snap6
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_0.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_1.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_2.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_3.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_4.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_5.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_0.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_1.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_10.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_11.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_12.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_13.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_14.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_2.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_3.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_4.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_5.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_6.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_7.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_8.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_9.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest3_0.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_0.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_1.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_2.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_3.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_4.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_5.snap57
-rw-r--r--zellij-server/src/pty.rs311
-rw-r--r--zellij-server/src/route.rs223
-rw-r--r--zellij-server/src/screen.rs651
-rw-r--r--zellij-server/src/tab.rs2273
-rw-r--r--zellij-server/src/thread_bus.rs80
-rw-r--r--zellij-server/src/ui/boundaries.rs630
-rw-r--r--zellij-server/src/ui/layout.rs230
-rw-r--r--zellij-server/src/ui/mod.rs3
-rw-r--r--zellij-server/src/ui/pane_resizer.rs531
-rw-r--r--zellij-server/src/wasm_vm.rs281
51 files changed, 10696 insertions, 0 deletions
diff --git a/zellij-server/Cargo.toml b/zellij-server/Cargo.toml
new file mode 100644
index 000000000..97930c75b
--- /dev/null
+++ b/zellij-server/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "zellij-server"
+version = "0.12.0"
+authors = ["Kunal Mohan <kunalmohan99@gmail.com>"]
+edition = "2018"
+description = "The server-side library for Zellij"
+license = "MIT"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+serde = { version = "1.0", features = ["derive"] }
+wasmer = "1.0.0"
+wasmer-wasi = "1.0.0"
+zellij-tile = { path = "../zellij-tile/", version = "0.12.0" }
+zellij-utils = { path = "../zellij-utils/", version = "0.12.0" }
+vte = "0.10.1"
+unicode-width = "0.1.8"
+ansi_term = "0.12.1"
+serde_yaml = "0.8"
+nix = "0.19.1"
+termion = "1.5.0"
+signal-hook = "0.3"
+libc = "0.2"
+serde_json = "1.0"
+daemonize = "0.4.1"
+interprocess = "1.1.1"
+
+[dependencies.async-std]
+version = "1.3.0"
+features = ["unstable"]
+
+[dev-dependencies]
+insta = "1.6.0"
+
+[features]
+test = ["zellij-utils/test"]
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
new file mode 100644
index 000000000..8d82313db
--- /dev/null
+++ b/zellij-server/src/lib.rs
@@ -0,0 +1,318 @@
+pub mod os_input_output;
+pub mod panes;
+pub mod tab;
+
+mod pty;
+mod route;
+mod screen;
+mod thread_bus;
+mod ui;
+mod wasm_vm;
+
+use std::sync::{Arc, RwLock};
+use std::thread;
+use std::{path::PathBuf, sync::mpsc};
+use wasmer::Store;
+use zellij_tile::data::PluginCapabilities;
+
+use crate::{
+ os_input_output::ServerOsApi,
+ pty::{pty_thread_main, Pty, PtyInstruction},
+ screen::{screen_thread_main, ScreenInstruction},
+ thread_bus::{Bus, ThreadSenders},
+ ui::layout::Layout,
+ wasm_vm::{wasm_thread_main, PluginInstruction},
+};
+use route::route_thread_main;
+use zellij_utils::{
+ channels::{ChannelWithContext, SenderType, SenderWithContext, SyncChannelWithContext},
+ cli::CliArgs,
+ errors::{ContextType, ErrorInstruction, ServerContext},
+ input::options::Options,
+ ipc::{ClientAttributes, ClientToServerMsg, ServerToClientMsg},
+ setup::{get_default_data_dir, install::populate_data_dir},
+};
+
+/// Instructions related to server-side application
+#[derive(Debug, Clone)]
+pub(crate) enum ServerInstruction {
+ NewClient(ClientAttributes, Box<CliArgs>, Box<Options>),
+ Render(Option<String>),
+ UnblockInputThread,
+ ClientExit,
+ Error(String),
+}
+
+impl From<ClientToServerMsg> for ServerInstruction {
+ fn from(instruction: ClientToServerMsg) -> Self {
+ match instruction {
+ ClientToServerMsg::ClientExit => ServerInstruction::ClientExit,
+ ClientToServerMsg::NewClient(pos, opts, options) => {
+ ServerInstruction::NewClient(pos, opts, options)
+ }
+ _ => unreachable!(),
+ }
+ }
+}
+
+impl From<&ServerInstruction> for ServerContext {
+ fn from(server_instruction: &ServerInstruction) -> Self {
+ match *server_instruction {
+ ServerInstruction::NewClient(..) => ServerContext::NewClient,
+ ServerInstruction::Render(_) => ServerContext::Render,
+ ServerInstruction::UnblockInputThread => ServerContext::UnblockInputThread,
+ ServerInstruction::ClientExit => ServerContext::ClientExit,
+ ServerInstruction::Error(_) => ServerContext::Error,
+ }
+ }
+}
+
+impl ErrorInstruction for ServerInstruction {
+ fn error(err: String) -> Self {
+ ServerInstruction::Error(err)
+ }
+}
+
+pub(crate) struct SessionMetaData {
+ pub senders: ThreadSenders,
+ pub capabilities: PluginCapabilities,
+ screen_thread: Option<thread::JoinHandle<()>>,
+ pty_thread: Option<thread::JoinHandle<()>>,
+ wasm_thread: Option<thread::JoinHandle<()>>,
+}
+
+impl Drop for SessionMetaData {
+ fn drop(&mut self) {
+ let _ = self.senders.send_to_pty(PtyInstruction::Exit);
+ let _ = self.senders.send_to_screen(ScreenInstruction::Exit);
+ let _ = self.senders.send_to_plugin(PluginInstruction::Exit);
+ let _ = self.screen_thread.take().unwrap().join();
+ let _ = self.pty_thread.take().unwrap().join();
+ let _ = self.wasm_thread.take().unwrap().join();
+ }
+}
+
+pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
+ #[cfg(not(any(feature = "test", test)))]
+ daemonize::Daemonize::new()
+ .working_directory(std::env::current_dir().unwrap())
+ .umask(0o077)
+ .start()
+ .expect("could not daemonize the server process");
+
+ std::env::set_var(&"ZELLIJ", "0");
+
+ let (to_server, server_receiver): SyncChannelWithContext<ServerInstruction> =
+ mpsc::sync_channel(50);
+ let to_server = SenderWithContext::new(SenderType::SyncSender(to_server));
+ let sessions: Arc<RwLock<Option<SessionMetaData>>> = Arc::new(RwLock::new(None));
+
+ #[cfg(not(any(feature = "test", test)))]
+ std::panic::set_hook({
+ use zellij_utils::errors::handle_panic;
+ let to_server = to_server.clone();
+ Box::new(move |info| {
+ handle_panic(info, &to_server);
+ })
+ });
+
+ #[cfg(any(feature = "test", test))]
+ thread::Builder::new()
+ .name("server_router".to_string())
+ .spawn({
+ let sessions = sessions.clone();
+ let os_input = os_input.clone();
+ let to_server = to_server.clone();
+
+ move || route_thread_main(sessions, os_input, to_server)
+ })
+ .unwrap();
+ #[cfg(not(any(feature = "test", test)))]
+ let _ = thread::Builder::new()
+ .name("server_listener".to_string())
+ .spawn({
+ use interprocess::local_socket::LocalSocketListener;
+ use zellij_utils::shared::set_permissions;
+
+ let os_input = os_input.clone();
+ let sessions = sessions.clone();
+ let to_server = to_server.clone();
+ let socket_path = socket_path.clone();
+ move || {
+ drop(std::fs::remove_file(&socket_path));
+ let listener = LocalSocketListener::bind(&*socket_path).unwrap();
+ set_permissions(&socket_path).unwrap();
+ for stream in listener.incoming() {
+ match stream {
+ Ok(stream) => {
+ let mut os_input = os_input.clone();
+ os_input.update_receiver(stream);
+ let sessions = sessions.clone();
+ let to_server = to_server.clone();
+ thread::Builder::new()
+ .name("server_router".to_string())
+ .spawn({
+ let sessions = sessions.clone();
+ let os_input = os_input.clone();
+ let to_server = to_server.clone();
+
+ move || route_thread_main(sessions, os_input, to_server)
+ })
+ .unwrap();
+ }
+ Err(err) => {
+ panic!("err {:?}", err);
+ }
+ }
+ }
+ }
+ });
+
+ loop {
+ let (instruction, mut err_ctx) = server_receiver.recv().unwrap();
+ err_ctx.add_call(ContextType::IPCServer((&instruction).into()));
+ match instruction {
+ ServerInstruction::NewClient(client_attributes, opts, config_options) => {
+ let session_data = init_session(
+ os_input.clone(),
+ opts,
+ config_options,
+ to_server.clone(),
+ client_attributes,
+ );
+ *sessions.write().unwrap() = Some(session_data);
+ sessions
+ .read()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .senders
+ .send_to_pty(PtyInstruction::NewTab)
+ .unwrap();
+ }
+ ServerInstruction::UnblockInputThread => {
+ os_input.send_to_client(ServerToClientMsg::UnblockInputThread);
+ }
+ ServerInstruction::ClientExit => {
+ *sessions.write().unwrap() = None;
+ os_input.send_to_client(ServerToClientMsg::Exit);
+ break;
+ }
+ ServerInstruction::Render(output) => {
+ os_input.send_to_client(ServerToClientMsg::Render(output))
+ }
+ ServerInstruction::Error(backtrace) => {
+ os_input.send_to_client(ServerToClientMsg::ServerError(backtrace));
+ break;
+ }
+ }
+ }
+ #[cfg(not(any(feature = "test", test)))]
+ drop(std::fs::remove_file(&socket_path));
+}
+
+fn init_session(
+ os_input: Box<dyn ServerOsApi>,
+ opts: Box<CliArgs>,
+ config_options: Box<Options>,
+ to_server: SenderWithContext<ServerInstruction>,
+ client_attributes: ClientAttributes,
+) -> SessionMetaData {
+ let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = mpsc::channel();
+ let to_screen = SenderWithContext::new(SenderType::Sender(to_screen));
+
+ let (to_plugin, plugin_receiver): ChannelWithContext<PluginInstruction> = mpsc::channel();
+ let to_plugin = SenderWithContext::new(SenderType::Sender(to_plugin));
+ let (to_pty, pty_receiver): ChannelWithContext<PtyInstruction> = mpsc::channel();
+ let to_pty = SenderWithContext::new(SenderType::Sender(to_pty));
+
+ // Determine and initialize the data directory
+ let data_dir = opts.data_dir.unwrap_or_else(get_default_data_dir);
+
+ #[cfg(not(disable_automatic_asset_installation))]
+ populate_data_dir(&data_dir);
+
+ let capabilities = PluginCapabilities {
+ arrow_fonts: config_options.simplified_ui,
+ };
+
+ // Don't use default layouts in tests, but do everywhere else
+ #[cfg(not(any(feature = "test", test)))]
+ let default_layout = Some(PathBuf::from("default"));
+ #[cfg(any(feature = "test", test))]
+ let default_layout = None;
+ let layout_path = opts.layout_path;
+ let maybe_layout = opts
+ .layout
+ .as_ref()
+ .map(|p| Layout::from_dir(&p, &data_dir))
+ .or_else(|| layout_path.map(|p| Layout::new(&p)))