diff --git a/Cargo.lock b/Cargo.lock index 72f684cc8..38937d8b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,7 +132,7 @@ dependencies = [ "event-listener", "futures-lite", "once_cell", - "signal-hook 0.3.6", + "signal-hook", "winapi", ] @@ -1561,16 +1561,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "signal-hook" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook" version = "0.3.6" diff --git a/src/client/mod.rs b/src/client/mod.rs index ae298a5eb..b6a0231a1 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -69,6 +69,23 @@ pub fn start_client(mut os_input: Box) { move || input_loop(os_input, 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::terminal_resize( + os_api.get_terminal_size_using_fd(0), + )); + } + })); + } + }) + .unwrap(); + let router_thread = thread::Builder::new() .name("router".to_string()) .spawn({ 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>, - os_api: &'a mut Box, + os_api: &'a mut Box, } // 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>, - os_api: &'a mut Box, + os_api: &'a mut Box, ) -> Self { PaneResizer { panes, os_api } } diff --git a/src/client/tab.rs b/src/client/tab.rs index 4eabab100..3817a3274 100644 --- a/src/client/tab.rs +++ b/src/client/tab.rs @@ -2,22 +2,24 @@ //! as well as how they should be resized use crate::boundaries::colors; +use crate::client::pane_resizer::PaneResizer; 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::server::ServerInstruction; -use crate::utils::shared::pad_to_size; +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::{ cmp::Reverse, collections::{BTreeMap, HashSet}, }; -use zellij_tile::data::{Event, InputMode}; +use zellij_tile::data::{Event, ModeInfo}; const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this @@ -729,9 +731,14 @@ impl Tab { ); let hide_cursor = "\u{1b}[?25l"; output.push_str(hide_cursor); - for (kind, terminal) in self.panes.iter_mut() { - if !self.panes_to_hide.contains(&terminal.pid()) { - match self.active_terminal.unwrap() == terminal.pid() { + if self.should_clear_display_before_rendering { + let clear_display = "\u{1b}[2J"; + output.push_str(clear_display); + self.should_clear_display_before_rendering = false; + } + for (kind, pane) in self.panes.iter_mut() { + if !self.panes_to_hide.contains(&pane.pid()) { + match self.active_terminal.unwrap() == pane.pid() { true => { pane.set_active_at(Instant::now()); boundaries.add_rect(pane.as_ref(), self.mode_info.mode, Some(self.colors)) diff --git a/src/common/errors.rs b/src/common/errors.rs index 720341a45..10b2165a0 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -259,7 +259,7 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::CloseTab => ScreenContext::CloseTab, ScreenInstruction::GoToTab(_) => ScreenContext::GoToTab, ScreenInstruction::UpdateTabName(_) => ScreenContext::UpdateTabName, - ScreenInstruction::TerminalResize => ScreenContext::TerminalResize, + ScreenInstruction::TerminalResize(_) => ScreenContext::TerminalResize, ScreenInstruction::ChangeMode(_) => ScreenContext::ChangeMode, ScreenInstruction::ToggleActiveSyncPanes => ScreenContext::ToggleActiveSyncPanes, } diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index 4015938a1..947a8d02a 100644 --- a/src/common/input/handler.rs +++ b/src/common/input/handler.rs @@ -127,7 +127,7 @@ impl InputHandler { Event::ModeUpdate(get_mode_info(mode)), )); self.os_input - .send_to_server(ServerInstruction::change_input_mode(mode)); + .send_to_server(ServerInstruction::change_mode(get_mode_info(mode))); self.os_input.send_to_server(ServerInstruction::render()); } Action::Resize(direction) => { diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs index 53300424d..66957a4a1 100644 --- a/src/common/os_input_output.rs +++ b/src/common/os_input_output.rs @@ -7,6 +7,7 @@ use nix::sys::wait::waitpid; use nix::unistd; use nix::unistd::{ForkResult, Pid}; use serde::Serialize; +use signal_hook::{consts::signal::*, iterator::Signals}; use std::env; use std::io; use std::io::prelude::*; @@ -334,6 +335,7 @@ pub trait ClientOsApi: Send + Sync { fn client_recv(&self) -> (ClientInstruction, ErrorContext); /// Setup the client IpcChannel and notify server of new client fn connect_to_server(&mut self, full_screen_ws: PositionAndSize); + fn receive_sigwinch(&self, cb: Box); } impl ClientOsApi for ClientOsInputOutput { @@ -389,6 +391,20 @@ impl ClientOsApi for ClientOsInputOutput { .recv() .unwrap() } + fn receive_sigwinch(&self, cb: Box) { + let mut signals = Signals::new(&[SIGWINCH, SIGTERM, SIGINT, SIGQUIT]).unwrap(); + for signal in signals.forever() { + match signal { + SIGWINCH => { + cb(); + } + SIGTERM | SIGINT | SIGQUIT => { + break; + } + _ => unreachable!(), + } + } + } } impl Clone for Box { diff --git a/src/common/screen.rs b/src/common/screen.rs index 8868febb4..92e1075c2 100644 --- a/src/common/screen.rs +++ b/src/common/screen.rs @@ -58,7 +58,7 @@ pub enum ScreenInstruction { CloseTab, GoToTab(u32), UpdateTabName(Vec), - TerminalResize, + TerminalResize(PositionAndSize), ChangeMode(ModeInfo), } @@ -83,7 +83,7 @@ pub struct Screen { active_tab_index: Option, /// The [`ClientOsApi`] this [`Screen`] uses. os_api: Box, - input_mode: InputMode, + mode_info: ModeInfo, } impl Screen { @@ -233,8 +233,7 @@ impl Screen { } } - pub fn resize_to_screen(&mut self) { - let new_screen_size = self.os_api.get_terminal_size_using_fd(0); + pub fn resize_to_screen(&mut self, new_screen_size: PositionAndSize) { self.full_screen_ws = new_screen_size; for (_, tab) in self.tabs.iter_mut() { tab.resize_whole_tab(new_screen_size); diff --git a/src/server/mod.rs b/src/server/mod.rs index 5f1227993..2495c13d6 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -12,7 +12,7 @@ use std::{ }; use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; use wasmer_wasi::{Pipe, WasiState}; -use zellij_tile::data::{Event, EventType, InputMode}; +use zellij_tile::data::{Event, EventType, ModeInfo}; use crate::cli::CliArgs; use crate::client::ClientInstruction; @@ -168,12 +168,15 @@ impl ServerInstruction { pub fn update_tab_name(tab_ids: Vec) -> Self { Self::ToScreen(ScreenInstruction::UpdateTabName(tab_ids)) } - pub fn change_input_mode(input_mode: InputMode) -> Self { - Self::ToScreen(ScreenInstruction::ChangeInputMode(input_mode)) + 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 terminal_resize(new_size: PositionAndSize) -> Self { + Self::ToScreen(ScreenInstruction::TerminalResize(new_size)) + } } struct ClientMetaData { @@ -450,7 +453,7 @@ fn init_client( &full_screen_ws, os_input, max_panes, - InputMode::Normal, + ModeInfo::default(), ); loop { let (event, mut err_ctx) = screen @@ -625,8 +628,11 @@ fn init_client( .send(ServerInstruction::DoneUpdatingTabs) .unwrap(); } - ScreenInstruction::ChangeInputMode(input_mode) => { - screen.change_input_mode(input_mode); + ScreenInstruction::ChangeMode(mode_info) => { + screen.change_mode(mode_info); + } + ScreenInstruction::TerminalResize(new_size) => { + screen.resize_to_screen(new_size); } ScreenInstruction::Exit => { break; diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs index f5b03fde2..8c608de34 100644 --- a/src/tests/fakes.rs +++ b/src/tests/fakes.rs @@ -3,8 +3,7 @@ use std::collections::{HashMap, VecDeque}; use std::io::Write; use std::os::unix::io::RawFd; use std::path::PathBuf; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{mpsc, Arc, Mutex}; +use std::sync::{mpsc, Arc, Condvar, Mutex}; use std::time::{Duration, Instant}; use crate::client::ClientInstruction; @@ -13,8 +12,7 @@ use crate::errors::ErrorContext; use crate::os_input_output::{ClientOsApi, ServerOsApi}; use crate::server::ServerInstruction; use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes}; -use crate::utils::shared::default_palette; -use zellij_tile::data::Palette; +use crate::tests::utils::commands::{QUIT, SLEEP}; const MIN_TIME_BETWEEN_SNAPSHOTS: Duration = Duration::from_millis(150); @@ -76,11 +74,12 @@ pub struct FakeInputOutput { win_sizes: Arc>>, possible_tty_inputs: HashMap, last_snapshot_time: Arc>, - started_reading_from_pty: Arc, client_sender: SenderWithContext, client_receiver: Arc>>, server_sender: SenderWithContext, server_receiver: Arc>>, + should_trigger_sigwinch: Arc<(Mutex, Condvar)>, + sigwinch_event: Option, } impl FakeInputOutput { @@ -108,11 +107,12 @@ impl FakeInputOutput { io_events: Arc::new(Mutex::new(vec![])), win_sizes: Arc::new(Mutex::new(win_sizes)), possible_tty_inputs: get_possible_tty_inputs(), - started_reading_from_pty: Arc::new(AtomicBool::new(false)), server_receiver: Arc::new(Mutex::new(server_receiver)), server_sender, client_receiver: Arc::new(Mutex::new(client_receiver)), client_sender, + should_trigger_sigwinch: Arc::new((Mutex::new(false), Condvar::new())), + sigwinch_event: None, } } pub fn with_tty_inputs(mut self, tty_inputs: HashMap) -> Self { @@ -171,14 +171,24 @@ impl ClientOsApi for FakeInputOutput { ::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS - last_snapshot_time.elapsed()); } } - if self.stdin_commands.lock().unwrap().len() == 1 { - std::thread::sleep(Duration::from_millis(100)); - } - self.stdin_commands + let command = self + .stdin_commands .lock() .unwrap() .pop_front() - .unwrap_or(vec![]) + .unwrap_or(vec![]); + if command == SLEEP { + std::thread::sleep(std::time::Duration::from_millis(200)); + } else if command == QUIT && self.sigwinch_event.is_some() { + let (lock, cvar) = &*self.should_trigger_sigwinch; + let mut should_trigger_sigwinch = lock.lock().unwrap(); + *should_trigger_sigwinch = true; + cvar.notify_one(); + ::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS); // give some time for the app to resize before quitting + } else if command == QUIT { + ::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS); + } + command } fn get_stdout_writer(&self) -> Box { Box::new(self.stdout_writer.clone()) @@ -199,6 +209,16 @@ impl ClientOsApi for FakeInputOutput { fn client_recv(&self) -> (ClientInstruction, ErrorContext) { self.client_receiver.lock().unwrap().recv().unwrap() } + fn receive_sigwinch(&self, cb: Box) { + if self.sigwinch_event.is_some() { + let (lock, cvar) = &*self.should_trigger_sigwinch; + let mut should_trigger_sigwinch = lock.lock().unwrap(); + while !*should_trigger_sigwinch { + should_trigger_sigwinch = cvar.wait(should_trigger_sigwinch).unwrap(); + } + cb(); + } + } } impl ServerOsApi for FakeInputOutput { diff --git a/src/tests/integration/terminal_window_resize.rs b/src/tests/integration/terminal_window_resize.rs index ebea82c0a..5aa13e150 100644 --- a/src/tests/integration/terminal_window_resize.rs +++ b/src/tests/integration/terminal_window_resize.rs @@ -30,7 +30,11 @@ pub fn window_width_decrease_with_one_pane() { ..Default::default() }); let opts = CliArgs::default(); - start(Box::new(fake_input_output.clone()), opts, Config::default()); + start( + Box::new(fake_input_output.clone()), + opts, + Box::new(fake_input_output.clone()), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -61,7 +65,11 @@ pub fn window_width_increase_with_one_pane() { ..Default::default() }); let opts = CliArgs::default(); - start(Box::new(fake_input_output.clone()), opts, Config::default()); + start( + Box::new(fake_input_output.clone()), + opts, + Box::new(fake_input_output.clone()), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -92,7 +100,11 @@ pub fn window_height_increase_with_one_pane() { ..Default::default() }); let opts = CliArgs::default(); - start(Box::new(fake_input_output.clone()), opts, Config::default()); + start( + Box::new(fake_input_output.clone()), + opts, + Box::new(fake_input_output.clone()), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -123,7 +135,11 @@ pub fn window_width_and_height_decrease_with_one_pane() { ..Default::default() }); let opts = CliArgs::default(); - start(Box::new(fake_input_output.clone()), opts, Config::default()); + start( + Box::new(fake_input_output.clone()), + opts, + Box::new(fake_input_output.clone()), + ); let output_frames = fake_input_output .stdout_writer .output_frames