diff --git a/Cargo.lock b/Cargo.lock index 78536f80b..b5da3f431 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,7 +130,7 @@ dependencies = [ "event-listener", "futures-lite", "once_cell", - "signal-hook 0.3.7", + "signal-hook", "winapi", ] @@ -524,9 +524,9 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" [[package]] name = "either" @@ -824,9 +824,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6cf41e31a7e7b78055b548826da45c7dc74e6a13a3fa6b897a17a01322f26" +checksum = "c4a1b21a2971cea49ca4613c0e9fe8225ecaf5de64090fddc6002284726e9244" dependencies = [ "console", "lazy_static", @@ -899,9 +899,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -942,9 +942,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714" [[package]] name = "libloading" @@ -964,9 +964,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "lock_api" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" dependencies = [ "scopeguard", ] @@ -998,18 +998,18 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memmap2" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e3e85b970d650e2ae6d70592474087051c11c54da7f7b4949725c5735fbcc6" +checksum = "397d1a6d6d0563c0f5462bbdae662cf6c784edf5e828e40c7257f85d82bf56dd" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc14fc54a812b4472b4113facc3e44d099fbc0ea2ce0551fa5c703f8edfbfd38" +checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" dependencies = [ "autocfg", ] @@ -1194,9 +1194,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -1428,19 +1428,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.1.17" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aa894ef3fade0ee7243422f4fbbd6c2b48e6de767e621d37ef65f2310f53cea" +checksum = "ef33d6d0cd06e0840fba9985aab098c147e67e05cee14d412d3345ed14ff30ac" dependencies = [ "libc", "signal-hook-registry", @@ -1587,9 +1577,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.65" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" +checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" dependencies = [ "proc-macro2", "quote", @@ -1894,9 +1884,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -1904,9 +1894,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", @@ -1919,9 +1909,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73157efb9af26fb564bb59a009afd1c7c334a44db171d280690d0c3faaec3468" +checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -1931,9 +1921,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1941,9 +1931,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", @@ -1954,9 +1944,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "wasmer" @@ -2169,18 +2159,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0fa059022c5dabe129f02b429d67086400deb8277f89c975555dacc1dadbcc" +checksum = "8ec280a739b69173e0ffd12c1658507996836ba4e992ed9bc1e5385a0bd72a02" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310" +checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" dependencies = [ "js-sys", "wasm-bindgen", @@ -2197,12 +2187,12 @@ dependencies = [ [[package]] name = "which" -version = "4.0.2" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef" +checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe" dependencies = [ + "either", "libc", - "thiserror", ] [[package]] @@ -2292,7 +2282,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "signal-hook 0.1.17", + "signal-hook", "strip-ansi-escapes", "structopt", "strum", diff --git a/Cargo.toml b/Cargo.toml index 94ebc4726..746db0281 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ nom = "6.0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.8" -signal-hook = "0.1.10" +signal-hook = "0.3" strip-ansi-escapes = "0.1.0" structopt = "0.3" termion = "1.5.0" diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 2928cd601..b405d29db 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -26,3 +26,6 @@ Once the organization reaches 10 members, a reasonable and achievable process mu * Denis Maximov * Kunal Mohan * Henil Dedania +* Roee Shapira +* Alex Kenji Berthold +* Kyle Sutherland-Cash diff --git a/default-tiles/status-bar/src/first_line.rs b/default-tiles/status-bar/src/first_line.rs index febb719a3..c30cd5f2d 100644 --- a/default-tiles/status-bar/src/first_line.rs +++ b/default-tiles/status-bar/src/first_line.rs @@ -105,7 +105,7 @@ fn unselected_mode_shortcut(letter: char, text: &str, palette: Palette) -> LineP suffix_separator, ]) .to_string(), - len: text.chars().count() + 6, // 2 for the arrows, 3 for the char separators, 1 for the character + len: text.chars().count() + 7, // 2 for the arrows, 3 for the char separators, 1 for the character, 1 for the text padding } } @@ -151,7 +151,7 @@ fn selected_mode_shortcut(letter: char, text: &str, palette: Palette) -> LinePar suffix_separator, ]) .to_string(), - len: text.chars().count() + 6, // 2 for the arrows, 3 for the char separators, 1 for the character + len: text.chars().count() + 7, // 2 for the arrows, 3 for the char separators, 1 for the character, 1 for the text padding } } diff --git a/default-tiles/status-bar/src/main.rs b/default-tiles/status-bar/src/main.rs index f3cd4dc4e..7e1cf2fbe 100644 --- a/default-tiles/status-bar/src/main.rs +++ b/default-tiles/status-bar/src/main.rs @@ -2,7 +2,7 @@ mod first_line; mod second_line; use std::fmt::{Display, Error, Formatter}; -use zellij_tile::prelude::*; +use zellij_tile::{prelude::*, data::Theme}; use first_line::{ctrl_keys, superkey}; use second_line::keybinds; @@ -62,21 +62,30 @@ impl ZellijTile for State { let first_line = format!("{}{}", superkey, ctrl_keys); let second_line = keybinds(&self.mode_info, cols); + let first_line_color = match self.mode_info.palette.theme { + Theme::Light => self.mode_info.palette.black, + Theme::Dark => self.mode_info.palette.white, + }; + let second_line_color = match self.mode_info.palette.theme { + Theme::Light => self.mode_info.palette.bg, + Theme::Dark => self.mode_info.palette.bg, + }; + // [48;5;238m is gray background, [0K is so that it fills the rest of the line // [48;5;16m is black background, [0K is so that it fills the rest of the line println!( - "{}\u{1b}[{};{};{}m\u{1b}[0K", + "{}\x1B[38;2;{};{};{}m\u{1b}[0K", first_line, - self.mode_info.palette.black.0, - self.mode_info.palette.black.1, - self.mode_info.palette.black.2 + first_line_color.0, + first_line_color.1, + first_line_color.2 ); println!( "{}\u{1b}[{};{};{}m\u{1b}[0K", second_line, - self.mode_info.palette.fg.0, - self.mode_info.palette.fg.1, - self.mode_info.palette.fg.2 + second_line_color.0, + second_line_color.1, + second_line_color.2 ); } } diff --git a/src/client/mod.rs b/src/client/mod.rs index 93f35e066..cba12a46c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,5 +1,6 @@ pub mod boundaries; pub mod layout; +pub mod pane_resizer; pub mod panes; pub mod tab; diff --git a/src/client/pane_resizer.rs b/src/client/pane_resizer.rs new file mode 100644 index 000000000..007dc33f8 --- /dev/null +++ b/src/client/pane_resizer.rs @@ -0,0 +1,508 @@ +use crate::os_input_output::OsApi; +use crate::panes::{PaneId, PositionAndSize}; +use crate::tab::Pane; +use std::collections::{BTreeMap, HashSet}; + +pub struct PaneResizer<'a> { + panes: &'a mut BTreeMap>, + os_api: &'a mut Box, +} + +// TODO: currently there are some functions here duplicated with Tab +// the reason for this is that we need to get rid of the expansion_boundary +// otherwise we'll have a big separation of concerns issue +// once that is done, all resizing functions should move here + +impl<'a> PaneResizer<'a> { + pub fn new( + panes: &'a mut BTreeMap>, + os_api: &'a mut Box, + ) -> Self { + PaneResizer { panes, os_api } + } + pub fn resize( + &mut self, + mut current_size: PositionAndSize, + new_size: PositionAndSize, + ) -> Option<(isize, isize)> { + // (column_difference, row_difference) + let mut successfully_resized = false; + let mut column_difference: isize = 0; + let mut row_difference: isize = 0; + if new_size.columns < current_size.columns { + let reduce_by = current_size.columns - new_size.columns; + find_reducible_vertical_chain( + &self.panes, + reduce_by, + current_size.columns, + current_size.rows, + ) + .map(|panes_to_resize| { + self.reduce_panes_left_and_pull_adjacents_left(panes_to_resize, reduce_by); + column_difference = new_size.columns as isize - current_size.columns as isize; + current_size.columns = (current_size.columns as isize + column_difference) as usize; + successfully_resized = true; + }); + } else if new_size.columns > current_size.columns { + let increase_by = new_size.columns - current_size.columns; + find_increasable_vertical_chain( + &self.panes, + increase_by, + current_size.columns, + current_size.rows, + ) + .map(|panes_to_resize| { + self.increase_panes_right_and_push_adjacents_right(panes_to_resize, increase_by); + column_difference = new_size.columns as isize - current_size.columns as isize; + current_size.columns = (current_size.columns as isize + column_difference) as usize; + successfully_resized = true; + }); + } + if new_size.rows < current_size.rows { + let reduce_by = current_size.rows - new_size.rows; + find_reducible_horizontal_chain( + &self.panes, + reduce_by, + current_size.columns, + current_size.rows, + ) + .map(|panes_to_resize| { + self.reduce_panes_up_and_pull_adjacents_up(panes_to_resize, reduce_by); + row_difference = new_size.rows as isize - current_size.rows as isize; + current_size.rows = (current_size.rows as isize + row_difference) as usize; + successfully_resized = true; + }); + } else if new_size.rows > current_size.rows { + let increase_by = new_size.rows - current_size.rows; + find_increasable_horizontal_chain( + &self.panes, + increase_by, + current_size.columns, + current_size.rows, + ) + .map(|panes_to_resize| { + self.increase_panes_down_and_push_down_adjacents(panes_to_resize, increase_by); + row_difference = new_size.rows as isize - current_size.rows as isize; + current_size.rows = (current_size.rows as isize + row_difference) as usize; + successfully_resized = true; + }); + } + if successfully_resized { + Some((column_difference, row_difference)) + } else { + None + } + } + fn reduce_panes_left_and_pull_adjacents_left( + &mut self, + panes_to_reduce: Vec, + reduce_by: usize, + ) { + let mut pulled_panes: HashSet = HashSet::new(); + for pane_id in panes_to_reduce { + let (pane_x, pane_y, pane_columns, pane_rows) = { + let pane = self.panes.get(&pane_id).unwrap(); + (pane.x(), pane.y(), pane.columns(), pane.rows()) + }; + let panes_to_pull = self.panes.values_mut().filter(|p| { + p.x() > pane_x + pane_columns + && (p.y() <= pane_y && p.y() + p.rows() >= pane_y + || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows) + }); + for pane in panes_to_pull { + if !pulled_panes.contains(&pane.pid()) { + pane.pull_left(reduce_by); + pulled_panes.insert(pane.pid()); + } + } + self.reduce_pane_width_left(&pane_id, reduce_by); + } + } + fn reduce_panes_up_and_pull_adjacents_up( + &mut self, + panes_to_reduce: Vec, + reduce_by: usize, + ) { + let mut pulled_panes: HashSet = HashSet::new(); + for pane_id in panes_to_reduce { + let (pane_x, pane_y, pane_columns, pane_rows) = { + let pane = self.panes.get(&pane_id).unwrap(); + (pane.x(), pane.y(), pane.columns(), pane.rows()) + }; + let panes_to_pull = self.panes.values_mut().filter(|p| { + p.y() > pane_y + pane_rows + && (p.x() <= pane_x && p.x() + p.columns() >= pane_x + || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns) + }); + for pane in panes_to_pull { + if !pulled_panes.contains(&pane.pid()) { + pane.pull_up(reduce_by); + pulled_panes.insert(pane.pid()); + } + } + self.reduce_pane_height_up(&pane_id, reduce_by); + } + } + fn increase_panes_down_and_push_down_adjacents( + &mut self, + panes_to_increase: Vec, + increase_by: usize, + ) { + let mut pushed_panes: HashSet = HashSet::new(); + for pane_id in panes_to_increase { + let (pane_x, pane_y, pane_columns, pane_rows) = { + let pane = self.panes.get(&pane_id).unwrap(); + (pane.x(), pane.y(), pane.columns(), pane.rows()) + }; + let panes_to_push = self.panes.values_mut().filter(|p| { + p.y() > pane_y + pane_rows + && (p.x() <= pane_x && p.x() + p.columns() >= pane_x + || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns) + }); + for pane in panes_to_push { + if !pushed_panes.contains(&pane.pid()) { + pane.push_down(increase_by); + pushed_panes.insert(pane.pid()); + } + } + self.increase_pane_height_down(&pane_id, increase_by); + } + } + fn increase_panes_right_and_push_adjacents_right( + &mut self, + panes_to_increase: Vec, + increase_by: usize, + ) { + let mut pushed_panes: HashSet = HashSet::new(); + for pane_id in panes_to_increase { + let (pane_x, pane_y, pane_columns, pane_rows) = { + let pane = self.panes.get(&pane_id).unwrap(); + (pane.x(), pane.y(), pane.columns(), pane.rows()) + }; + let panes_to_push = self.panes.values_mut().filter(|p| { + p.x() > pane_x + pane_columns + && (p.y() <= pane_y && p.y() + p.rows() >= pane_y + || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows) + }); + for pane in panes_to_push { + if !pushed_panes.contains(&pane.pid()) { + pane.push_right(increase_by); + pushed_panes.insert(pane.pid()); + } + } + self.increase_pane_width_right(&pane_id, increase_by); + } + } + fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) { + let pane = self.panes.get_mut(id).unwrap(); + pane.reduce_height_up(count); + if let PaneId::Terminal(pid) = id { + self.os_api + .set_terminal_size_using_fd(*pid, pane.columns() as u16, pane.rows() as u16); + } + } + fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) { + let pane = self.panes.get_mut(id).unwrap(); + pane.increase_height_down(count); + if let PaneId::Terminal(pid) = pane.pid() { + self.os_api + .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16); + } + } + fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) { + let pane = self.panes.get_mut(id).unwrap(); + pane.increase_width_right(count); + if let PaneId::Terminal(pid) = pane.pid() { + self.os_api + .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16); + } + } + fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) { + let pane = self.panes.get_mut(id).unwrap(); + pane.reduce_width_left(count); + if let PaneId::Terminal(pid) = pane.pid() { + self.os_api + .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16); + } + } +} + +fn find_next_increasable_horizontal_pane( + panes: &BTreeMap>, + right_of: &Box, + increase_by: usize, +) -> Option { + let next_pane_candidates = panes.values().filter( + |p| { + p.x() == right_of.x() + right_of.columns() + 1 + && p.horizontally_overlaps_with(right_of.as_ref()) + }, // TODO: the name here is wrong, it should be vertically_overlaps_with + ); + let resizable_candidates = + next_pane_candidates.filter(|p| p.can_increase_height_by(increase_by)); + resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { + Some(next_pane) => { + let next_pane = panes.get(&next_pane).unwrap(); + if next_pane.y() < p.y() { + next_pane_id + } else { + Some(p.pid()) + } + } + None => Some(p.pid()), + }) +} + +fn find_next_increasable_vertical_pane( + panes: &BTreeMap>, + below: &Box, + increase_by: usize, +) -> Option { + let next_pane_candidates = panes.values().filter( + |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below.as_ref()), // TODO: the name here is wrong, it should be horizontally_overlaps_with + ); + let resizable_candidates = + next_pane_candidates.filter(|p| p.can_increase_width_by(increase_by)); + resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { + Some(next_pane) => { + let next_pane = panes.get(&next_pane).unwrap(); + if next_pane.x() < p.x() { + next_pane_id + } else { + Some(p.pid()) + } + } + None => Some(p.pid()), + }) +} + +fn find_next_reducible_vertical_pane( + panes: &BTreeMap>, + below: &Box, + reduce_by: usize, +) -> Option { + let next_pane_candidates = panes.values().filter( + |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below.as_ref()), // TODO: the name here is wrong, it should be horizontally_overlaps_with + ); + let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_width_by(reduce_by)); + resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { + Some(next_pane) => { + let next_pane = panes.get(&next_pane).unwrap(); + if next_pane.x() < p.x() { + next_pane_id + } else { + Some(p.pid()) + } + } + None => Some(p.pid()), + }) +} + +fn find_next_reducible_horizontal_pane( + panes: &BTreeMap>, + right_of: &Box, + reduce_by: usize, +) -> Option { + let next_pane_candidates = panes.values().filter( + |p| { + p.x() == right_of.x() + right_of.columns() + 1 + && p.horizontally_overlaps_with(right_of.as_ref()) + }, // TODO: the name here is wrong, it should be vertically_overlaps_with + ); + let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_height_by(reduce_by)); + resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { + Some(next_pane) => { + let next_pane = panes.get(&next_pane).unwrap(); + if next_pane.y() < p.y() { + next_pane_id + } else { + Some(p.pid()) + } + } + None => Some(p.pid()), + }) +} + +fn find_increasable_horizontal_chain( + panes: &BTreeMap>, + increase_by: usize, + screen_width: usize, + screen_height: usize, // TODO: this is the previous size (make this clearer) +) -> Option> { + let mut horizontal_coordinate = 0; + loop { + if horizontal_coordinate == screen_height { + return None; + } + + match panes + .values() + .find(|p| p.x() == 0 && p.y() == horizontal_coordinate) + { + Some(leftmost_pane) => { + if !leftmost_pane.can_increase_height_by(increase_by) { + horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; + continue; + } + let mut panes_to_resize = vec![]; + let mut current_pane = leftmost_pane; + loop { + panes_to_resize.push(current_pane.pid()); + if current_pane.x() + current_pane.columns() == screen_width { + return Some(panes_to_resize); + } + match find_next_increasable_horizontal_pane(panes, ¤t_pane, increase_by) { + Some(next_pane_id) => { + current_pane = panes.get(&next_pane_id).unwrap(); + } + None => { + horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; + break; + } + }; + } + } + None => { + return None; + } + } + } +} + +fn find_increasable_vertical_chain( + panes: &BTreeMap>, + increase_by: usize, + screen_width: usize, + screen_height: usize, // TODO: this is the previous size (make this clearer) +) -> Option> { + let mut vertical_coordinate = 0; + loop { + if vertical_coordinate == screen_width { + return None; + } + + match panes + .values() + .find(|p| p.y() == 0 && p.x() == vertical_coordinate) + { + Some(topmost_pane) => { + if !topmost_pane.can_increase_width_by(increase_by) { + vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; + continue; + } + let mut panes_to_resize = vec![]; + let mut current_pane = topmost_pane; + loop { + panes_to_resize.push(current_pane.pid()); + if current_pane.y() + current_pane.rows() == screen_height { + return Some(panes_to_resize); + } + match find_next_increasable_vertical_pane(panes, ¤t_pane, increase_by) { + Some(next_pane_id) => { + current_pane = panes.get(&next_pane_id).unwrap(); + } + None => { + vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; + break; + } + }; + } + } + None => { + return None; + } + } + } +} + +fn find_reducible_horizontal_chain( + panes: &BTreeMap>, + reduce_by: usize, + screen_width: usize, + screen_height: usize, // TODO: this is the previous size (make this clearer) +) -> Option> { + let mut horizontal_coordinate = 0; + loop { + if horizontal_coordinate == screen_height { + return None; + } + + match panes + .values() + .find(|p| p.x() == 0 && p.y() == horizontal_coordinate) + { + Some(leftmost_pane) => { + if !leftmost_pane.can_reduce_height_by(reduce_by) { + horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; + continue; + } + let mut panes_to_resize = vec![]; + let mut current_pane = leftmost_pane; + loop { + panes_to_resize.push(current_pane.pid()); + if current_pane.x() + current_pane.columns() == screen_width { + return Some(panes_to_resize); + } + match find_next_reducible_horizontal_pane(panes, ¤t_pane, reduce_by) { + Some(next_pane_id) => { + current_pane = panes.get(&next_pane_id).unwrap(); + } + None => { + horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; + break; + } + }; + } + } + None => { + return None; + } + } + } +} + +fn find_reducible_vertical_chain( + panes: &BTreeMap>, + increase_by: usize, + screen_width: usize, + screen_height: usize, // TODO: this is the previous size (make this clearer) +) -> Option> { + let mut vertical_coordinate = 0; + loop { + if vertical_coordinate == screen_width { + return None; + } + + match panes + .values() + .find(|p| p.y() == 0 && p.x() == vertical_coordinate) + { + Some(topmost_pane) => { + if !topmost_pane.can_reduce_width_by(increase_by) { + vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; + continue; + } + let mut panes_to_resize = vec![]; + let mut current_pane = topmost_pane; + loop { + panes_to_resize.push(current_pane.pid()); + if current_pane.y() + current_pane.rows() == screen_height { + return Some(panes_to_resize); + } + match find_next_reducible_vertical_pane(panes, ¤t_pane, increase_by) { + Some(next_pane_id) => { + current_pane = panes.get(&next_pane_id).unwrap(); + } + None => { + vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; + break; + } + }; + } + } + None => { + return None; + } + } + } +} diff --git a/src/client/panes/plugin_pane.rs b/src/client/panes/plugin_pane.rs index 4cffff5f5..caf49acc8 100644 --- a/src/client/panes/plugin_pane.rs +++ b/src/client/panes/plugin_pane.rs @@ -173,6 +173,18 @@ impl Pane for PluginPane { self.position_and_size.columns += count; self.should_render = true; } + fn push_down(&mut self, count: usize) { + self.position_and_size.y += count; + } + fn push_right(&mut self, count: usize) { + self.position_and_size.x += count; + } + fn pull_left(&mut self, count: usize) { + self.position_and_size.x -= count; + } + fn pull_up(&mut self, count: usize) { + self.position_and_size.y -= count; + } fn scroll_up(&mut self, _count: usize) { unimplemented!() } diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs index f0b15e504..463a33e3c 100644 --- a/src/client/panes/terminal_pane.rs +++ b/src/client/panes/terminal_pane.rs @@ -282,6 +282,18 @@ impl Pane for TerminalPane { self.position_and_size.columns += count; self.reflow_lines(); } + fn push_down(&mut self, count: usize) { + self.position_and_size.y += count; + } + fn push_right(&mut self, count: usize) { + self.position_and_size.x += count; + } + fn pull_left(&mut self, count: usize) { + self.position_and_size.x -= count; + } + fn pull_up(&mut self, count: usize) { + self.position_and_size.y -= count; + } fn scroll_up(&mut self, count: usize) { self.grid.move_viewport_up(count); self.mark_for_rerender(); diff --git a/src/client/tab.rs b/src/client/tab.rs index 4121edbaa..9f7692520 100644 --- a/src/client/tab.rs +++ b/src/client/tab.rs @@ -1,20 +1,23 @@ //! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size, //! as well as how they should be resized -use crate::common::{colors, input::handler::parse_keys, AppInstruction, SenderWithContext}; +use crate::client::pane_resizer::PaneResizer; +use crate::common::{input::handler::parse_keys, AppInstruction, SenderWithContext}; use crate::layout::Layout; +use crate::os_input_output::OsApi; use crate::panes::{PaneId, PositionAndSize, TerminalPane}; use crate::pty_bus::{PtyInstruction, VteEvent}; +use crate::utils::shared::adjust_to_size; use crate::wasm_vm::PluginInstruction; use crate::{boundaries::Boundaries, panes::PluginPane}; -use crate::{os_input_output::OsApi, utils::shared::pad_to_size}; +use serde::{Deserialize, Serialize}; use std::os::unix::io::RawFd; use std::{ cmp::Reverse, collections::{BTreeMap, HashSet}, }; use std::{io::Write, sync::mpsc::channel}; -use zellij_tile::data::{Event, InputMode, ModeInfo, Palette}; +use zellij_tile::data::{Event, InputMode, ModeInfo, Palette, colors}; const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this const MIN_TERMINAL_HEIGHT: usize = 2; @@ -65,6 +68,18 @@ pub struct Tab { pub send_plugin_instructions: SenderWithContext, pub send_app_instructions: SenderWithContext, expansion_boundary: Option, + should_clear_display_before_rendering: bool, + pub mode_info: ModeInfo, + pub input_mode: InputMode, + pub colors: Palette +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct TabData { + /* subset of fields to publish to plugins */ + pub position: usize, + pub name: String, + pub active: bool, pub mode_info: ModeInfo, pub input_mode: InputMode, pub colors: Palette, @@ -100,6 +115,10 @@ pub trait Pane { fn reduce_width_right(&mut self, count: usize); fn reduce_width_left(&mut self, count: usize); fn increase_width_left(&mut self, count: usize); + fn push_down(&mut self, count: usize); + fn push_right(&mut self, count: usize); + fn pull_left(&mut self, count: usize); + fn pull_up(&mut self, count: usize); fn scroll_up(&mut self, count: usize); fn scroll_down(&mut self, count: usize); fn clear_scroll(&mut self); @@ -154,6 +173,22 @@ pub trait Pane { rows: self.rows(), } } + fn can_increase_height_by(&self, increase_by: usize) -> bool { + self.max_height() + .map(|max_height| self.rows() + increase_by <= max_height) + .unwrap_or(true) + } + fn can_increase_width_by(&self, increase_by: usize) -> bool { + self.max_width() + .map(|max_width| self.columns() + increase_by <= max_width) + .unwrap_or(true) + } + fn can_reduce_height_by(&self, reduce_by: usize) -> bool { + self.rows() > reduce_by && self.rows() - reduce_by >= self.min_height() + } + fn can_reduce_width_by(&self, reduce_by: usize) -> bool { + self.columns() > reduce_by && self.columns() - reduce_by >= self.min_width() + } fn min_width(&self) -> usize { MIN_TERMINAL_WIDTH } @@ -216,6 +251,7 @@ impl Tab { send_pty_instructions, send_plugin_instructions, expansion_boundary: None, + should_clear_display_before_rendering: false, mode_info, input_mode, colors, @@ -635,28 +671,33 @@ impl Tab { stdout .write_all(&hide_cursor.as_bytes()) .expect("cannot write to stdout"); - for (kind, terminal) in self.panes.iter_mut() { - if !self.panes_to_hide.contains(&terminal.pid()) { - match self.active_terminal.unwrap() == terminal.pid() { - true => boundaries.add_rect( - terminal.as_ref(), - self.mode_info.mode, - Some(self.colors), - ), - false => boundaries.add_rect(terminal.as_ref(), self.mode_info.mode, None), + if self.should_clear_display_before_rendering { + let clear_display = "\u{1b}[2J"; + stdout + .write_all(&clear_display.as_bytes()) + .expect("cannot write to stdout"); + 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 => { + boundaries.add_rect(pane.as_ref(), self.mode_info.mode, Some(self.colors)) + } + false => boundaries.add_rect(pane.as_ref(), self.mode_info.mode, None), } - if let Some(vte_output) = terminal.render() { + if let Some(vte_output) = pane.render() { let vte_output = if let PaneId::Terminal(_) = kind { vte_output } else { - pad_to_size(&vte_output, terminal.rows(), terminal.columns()) + adjust_to_size(&vte_output, pane.rows(), pane.columns()) }; // FIXME: Use Termion for cursor and style clearing? write!( stdout, "\u{1b}[{};{}H\u{1b}[m{}", - terminal.y() + 1, - terminal.x() + 1, + pane.y() + 1, + pane.x() + 1, vte_output ) .expect("cannot write to stdout"); @@ -1669,17 +1710,30 @@ impl Tab { false } } - pub fn resize_right(&mut self) { - // TODO: find out by how much we actually reduced and only reduce by that much - let count = 10; - if let Some(active_pane_id) = self.get_active_pane_id() { - if self.can_increase_pane_and_surroundings_right(&active_pane_id, count) { - self.increase_pane_and_surroundings_right(&active_pane_id, count); - } else if self.can_reduce_pane_and_surroundings_right(&active_pane_id, count) { - self.reduce_pane_and_surroundings_right(&active_pane_id, count); - } + pub fn resize_whole_tab(&mut self, new_screen_size: PositionAndSize) { + if self.fullscreen_is_active { + // this is not ideal but until we get rid of expansion_boundary, it's a necessity + self.toggle_active_pane_fullscreen(); } - self.render(); + match PaneResizer::new(&mut self.panes, &mut self.os_api) + .resize(self.full_screen_ws, new_screen_size) + { + Some((column_difference, row_difference)) => { + self.should_clear_display_before_rendering = true; + self.expansion_boundary.as_mut().map(|expansion_boundary| { + // TODO: this is not always accurate + expansion_boundary.columns = + (expansion_boundary.columns as isize + column_difference) as usize; + expansion_boundary.rows = + (expansion_boundary.rows as isize + row_difference) as usize; + }); + self.full_screen_ws.columns = + (self.full_screen_ws.columns as isize + column_difference) as usize; + self.full_screen_ws.rows = + (self.full_screen_ws.rows as isize + row_difference) as usize; + } + None => {} + }; } pub fn resize_left(&mut self) { // TODO: find out by how much we actually reduced and only reduce by that much @@ -1693,6 +1747,18 @@ impl Tab { } self.render(); } + pub fn resize_right(&mut self) { + // TODO: find out by how much we actually reduced and only reduce by that much + let count = 10; + if let Some(active_pane_id) = self.get_active_pane_id() { + if self.can_increase_pane_and_surroundings_right(&active_pane_id, count) { + self.increase_pane_and_surroundings_right(&active_pane_id, count); + } else if self.can_reduce_pane_and_surroundings_right(&active_pane_id, count) { + self.reduce_pane_and_surroundings_right(&active_pane_id, count); + } + } + self.render(); + } pub fn resize_down(&mut self) { // TODO: find out by how much we actually reduced and only reduce by that much let count = 2; diff --git a/src/common/errors.rs b/src/common/errors.rs index 56bf27f4f..00284cd49 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -42,7 +42,7 @@ pub fn handle_panic( msg, location.file(), location.line(), - backtrace + backtrace, ), (Some(location), None) => format!( "{}\n\u{1b}[0;0mError: \u{1b}[0;31mthread '{}' panicked: {}:{}\n\u{1b}[0;0m{:?}", @@ -200,6 +200,7 @@ pub enum ScreenContext { CloseTab, GoToTab, UpdateTabName, + TerminalResize, ChangeMode, } @@ -241,6 +242,7 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::CloseTab => ScreenContext::CloseTab, ScreenInstruction::GoToTab(_) => ScreenContext::GoToTab, ScreenInstruction::UpdateTabName(_) => ScreenContext::UpdateTabName, + ScreenInstruction::TerminalResize => ScreenContext::TerminalResize, ScreenInstruction::ChangeMode(_) => ScreenContext::ChangeMode, } } diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index c2f9ce934..1ed9169ee 100644 --- a/src/common/input/handler.rs +++ b/src/common/input/handler.rs @@ -81,7 +81,8 @@ impl InputHandler { } termion::event::Event::Mouse(_) | termion::event::Event::Unsupported(_) => { - unimplemented!("Mouse and unsupported events aren't supported!"); + // Mouse and unsupported events aren't implemented yet, + // use a NoOp untill then. } }, Err(err) => panic!("Encountered read error: {:?}", err), diff --git a/src/common/mod.rs b/src/common/mod.rs index bbb2242c7..afd1f78ff 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -39,7 +39,9 @@ use wasm_vm::{wasi_stdout, wasi_write_string, zellij_imports, PluginInstruction} use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; use wasmer_wasi::{Pipe, WasiState}; use xrdb::Colors; -use zellij_tile::data::{EventType, InputMode, ModeInfo, Palette}; +use zellij_tile::data::{EventType, InputMode, ModeInfo, Palette, Theme}; + +use self::utils::logging::debug_log_to_file; #[derive(Serialize, Deserialize, Debug)] pub enum ApiCommand { @@ -126,6 +128,19 @@ pub mod colors { pub const BLACK: (u8, u8, u8) = (0, 0, 0); } +pub fn detect_theme(bg: (u8, u8, u8)) -> Theme { + let (r, g, b) = bg; + // HSP, P stands for perceived brightness + let hsp: f64 = (0.299 * (r as f64 * r as f64) + + 0.587 * (g as f64 * g as f64) + + 0.114 * (b as f64 * b as f64)) + .sqrt(); + match hsp > 127.5 { + true => Theme::Light, + false => Theme::Dark, + } +} + pub fn load_palette() -> Palette { let palette = match Colors::new("xresources") { Some(colors) => { @@ -150,7 +165,13 @@ pub fn load_palette() -> Palette { (rgb.0 as u8, rgb.1 as u8, rgb.2 as u8) }) .collect(); + let theme = detect_theme(bg); + debug_log_to_file(format!( + "{:?} {:?}, white: {:?}, black: {:?}, fg: {:?}", + theme, bg, colors[7], colors[0], fg + )); Palette { + theme, fg, bg, black: colors[0], @@ -164,6 +185,7 @@ pub fn load_palette() -> Palette { } } None => Palette { + theme: Theme::Dark, fg: colors::BRIGHT_GRAY, bg: colors::BLACK, black: colors::BLACK, @@ -459,6 +481,9 @@ pub fn start(mut os_input: Box, opts: CliArgs) { ScreenInstruction::UpdateTabName(c) => { screen.update_active_tab_name(c); } + ScreenInstruction::TerminalResize => { + screen.resize_to_screen(); + } ScreenInstruction::ChangeMode(mode_info) => { screen.change_mode(mode_info); } @@ -576,6 +601,19 @@ pub fn start(mut os_input: Box, opts: CliArgs) { }) .unwrap(); + let _signal_thread = thread::Builder::new() + .name("signal_listener".to_string()) + .spawn({ + let os_input = os_input.clone(); + let send_screen_instructions = send_screen_instructions.clone(); + move || { + os_input.receive_sigwinch(Box::new(move || { + let _ = send_screen_instructions.send(ScreenInstruction::TerminalResize); + })); + } + }) + .unwrap(); + // TODO: currently we don't wait for this to quit // because otherwise the app will hang. Need to fix this so it both // listens to the ipc-bus and is able to quit cleanly diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs index 043a9aea6..de0c6a3bb 100644 --- a/src/common/os_input_output.rs +++ b/src/common/os_input_output.rs @@ -13,6 +13,8 @@ use std::path::PathBuf; use std::process::{Child, Command}; use std::sync::{Arc, Mutex}; +use signal_hook::{consts::signal::*, iterator::Signals}; + use std::env; fn into_raw_mode(pid: RawFd) { @@ -65,7 +67,7 @@ pub fn set_terminal_size_using_fd(fd: RawFd, columns: u16, rows: u16) { /// process exits. fn handle_command_exit(mut child: Child) { // register the SIGINT signal (TODO handle more signals) - let signals = ::signal_hook::iterator::Signals::new(&[::signal_hook::SIGINT]).unwrap(); + let mut signals = ::signal_hook::iterator::Signals::new(&[SIGINT]).unwrap(); 'handle_exit: loop { // test whether the child process has exited match child.try_wait() { @@ -82,10 +84,15 @@ fn handle_command_exit(mut child: Child) { } for signal in signals.pending() { - if signal == signal_hook::SIGINT { - child.kill().unwrap(); - child.wait().unwrap(); - break 'handle_exit; + // FIXME: We need to handle more signals here! + #[allow(clippy::single_match)] + match signal { + SIGINT => { + child.kill().unwrap(); + child.wait().unwrap(); + break 'handle_exit; + } + _ => {} } } } @@ -188,6 +195,7 @@ pub trait OsApi: Send + Sync { fn get_stdout_writer(&self) -> Box; /// Returns a [`Box`] pointer to this [`OsApi`] struct. fn box_clone(&self) -> Box; + fn receive_sigwinch(&self, cb: Box); } impl OsApi for OsInputOutput { @@ -238,6 +246,20 @@ impl OsApi for OsInputOutput { waitpid(Pid::from_raw(pid), None).unwrap(); Ok(()) } + 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 bd8a72c97..7546becea 100644 --- a/src/common/screen.rs +++ b/src/common/screen.rs @@ -50,6 +50,7 @@ pub enum ScreenInstruction { CloseTab, GoToTab(u32), UpdateTabName(Vec), + TerminalResize, ChangeMode(ModeInfo), } @@ -221,6 +222,15 @@ impl Screen { } } + pub fn resize_to_screen(&mut self) { + let new_screen_size = self.os_api.get_terminal_size_using_fd(0); + self.full_screen_ws = new_screen_size; + for (_, tab) in self.tabs.iter_mut() { + tab.resize_whole_tab(new_screen_size); + } + self.render(); + } + /// Renders this [`Screen`], which amounts to rendering its active [`Tab`]. pub fn render(&mut self) { if let Some(active_tab) = self.get_active_tab_mut() { diff --git a/src/common/utils/shared.rs b/src/common/utils/shared.rs index 9717837a0..93892d4f3 100644 --- a/src/common/utils/shared.rs +++ b/src/common/utils/shared.rs @@ -11,9 +11,18 @@ fn ansi_len(s: &str) -> usize { .count() } -pub fn pad_to_size(s: &str, rows: usize, columns: usize) -> String { +pub fn adjust_to_size(s: &str, rows: usize, columns: usize) -> String { s.lines() - .map(|l| [l, &str::repeat(" ", columns - ansi_len(l))].concat()) + .map(|l| { + let actual_len = ansi_len(l); + if actual_len > columns { + let mut line = String::from(l); + line.truncate(columns); + return line; + } else { + return [l, &str::repeat(" ", columns - ansi_len(l))].concat(); + } + }) .chain(iter::repeat(str::repeat(" ", columns))) .take(rows) .collect::>() diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs index c800de97a..c9504a984 100644 --- a/src/tests/fakes.rs +++ b/src/tests/fakes.rs @@ -3,14 +3,13 @@ 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::{Arc, Mutex}; +use std::sync::{Arc, Condvar, Mutex}; use std::time::{Duration, Instant}; use crate::os_input_output::OsApi; use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes}; -use crate::tests::utils::commands::SLEEP; +use crate::tests::utils::commands::{QUIT, SLEEP}; const MIN_TIME_BETWEEN_SNAPSHOTS: Duration = Duration::from_millis(50); @@ -72,7 +71,8 @@ pub struct FakeInputOutput { win_sizes: Arc>>, possible_tty_inputs: HashMap, last_snapshot_time: Arc>, - started_reading_from_pty: Arc, + should_trigger_sigwinch: Arc<(Mutex, Condvar)>, + sigwinch_event: Option, } impl FakeInputOutput { @@ -91,7 +91,8 @@ 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)), + should_trigger_sigwinch: Arc::new((Mutex::new(false), Condvar::new())), + sigwinch_event: None, } } pub fn with_tty_inputs(mut self, tty_inputs: HashMap) -> Self { @@ -108,10 +109,20 @@ impl FakeInputOutput { pub fn add_terminal(&mut self, fd: RawFd) { self.stdin_writes.lock().unwrap().insert(fd, vec![]); } + pub fn add_sigwinch_event(&mut self, new_position_and_size: PositionAndSize) { + self.sigwinch_event = Some(new_position_and_size); + } } impl OsApi for FakeInputOutput { fn get_terminal_size_using_fd(&self, pid: RawFd) -> PositionAndSize { + if let Some(new_position_and_size) = self.sigwinch_event { + let (lock, _cvar) = &*self.should_trigger_sigwinch; + let should_trigger_sigwinch = lock.lock().unwrap(); + if *should_trigger_sigwinch && pid == 0 { + return new_position_and_size; + } + } let win_sizes = self.win_sizes.lock().unwrap(); let winsize = win_sizes.get(&pid).unwrap(); *winsize @@ -159,7 +170,6 @@ impl OsApi for FakeInputOutput { if bytes_read > bytes.read_position { bytes.set_read_position(bytes_read); } - self.started_reading_from_pty.store(true, Ordering::Release); return Ok(bytes_read); } None => Err(nix::Error::Sys(nix::errno::Errno::EAGAIN)), @@ -199,6 +209,12 @@ impl OsApi for FakeInputOutput { .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 } command } @@ -209,4 +225,14 @@ impl OsApi for FakeInputOutput { self.io_events.lock().unwrap().push(IoEvent::Kill(fd)); Ok(()) } + 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(); + } + } } diff --git a/src/tests/integration/close_pane.rs b/src/tests/integration/close_pane.rs index 0a7aea48c..7421ba371 100644 --- a/src/tests/integration/close_pane.rs +++ b/src/tests/integration/close_pane.rs @@ -6,7 +6,7 @@ use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots} use crate::{start, CliArgs}; use crate::tests::utils::commands::{ - CLOSE_PANE_IN_PANE_MODE, COMMAND_TOGGLE, ESC, MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, + CLOSE_PANE_IN_PANE_MODE, ESC, MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_DOWN_IN_RESIZE_MODE, RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE, RESIZE_UP_IN_RESIZE_MODE, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE, }; diff --git a/src/tests/integration/mod.rs b/src/tests/integration/mod.rs index 9036ad6d9..83cb7d222 100644 --- a/src/tests/integration/mod.rs +++ b/src/tests/integration/mod.rs @@ -12,4 +12,5 @@ pub mod resize_left; pub mod resize_right; pub mod resize_up; pub mod tabs; +pub mod terminal_window_resize; pub mod toggle_fullscreen; diff --git a/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_height_increase_with_one_pane.snap b/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_height_increase_with_one_pane.snap new file mode 100644 index 000000000..2b8efe26f --- /dev/null +++ b/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_height_increase_with_one_pane.snap @@ -0,0 +1,25 @@ +--- +source: src/tests/integration/terminal_window_resize.rs +expression: snapshot_before_quit + +--- +line1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + █ diff --git a/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_width_and_height_decrease_with_one_pane.snap b/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_width_and_height_decrease_with_one_pane.snap new file mode 100644 index 000000000..55f32b5d4 --- /dev/null +++ b/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_width_and_height_decrease_with_one_pane.snap @@ -0,0 +1,25 @@ +--- +source: src/tests/integration/terminal_window_resize.rs +expression: snapshot_before_quit + +--- +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +prompt $ █ + + + + + + + + + + diff --git a/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_width_decrease_with_one_pane.snap b/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_width_decrease_with_one_pane.snap new file mode 100644 index 000000000..2f72d22ab --- /dev/null +++ b/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_width_decrease_with_one_pane.snap @@ -0,0 +1,25 @@ +--- +source: src/tests/integration/terminal_window_resize.rs +expression: snapshot_before_quit + +--- +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +prompt $ █ diff --git a/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_width_increase_with_one_pane.snap b/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_width_increase_with_one_pane.snap new file mode 100644 index 000000000..95cc6c369 --- /dev/null +++ b/src/tests/integration/snapshots/zellij__tests__integration__terminal_window_resize__window_width_increase_with_one_pane.snap @@ -0,0 +1,25 @@ +--- +source: src/tests/integration/terminal_window_resize.rs +expression: snapshot_before_quit + +--- +line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +prompt $ + █ diff --git a/src/tests/integration/terminal_window_resize.rs b/src/tests/integration/terminal_window_resize.rs new file mode 100644 index 000000000..ab73d24e5 --- /dev/null +++ b/src/tests/integration/terminal_window_resize.rs @@ -0,0 +1,127 @@ +use crate::panes::PositionAndSize; +use ::insta::assert_snapshot; + +use crate::tests::fakes::FakeInputOutput; +use crate::tests::utils::commands::QUIT; +use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots}; +use crate::{start, CliArgs}; + +fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { + FakeInputOutput::new(fake_win_size.clone()) +} + +#[test] +pub fn window_width_decrease_with_one_pane() { + let fake_win_size = PositionAndSize { + columns: 121, + rows: 20, + x: 0, + y: 0, + }; + let mut fake_input_output = get_fake_os_input(&fake_win_size); + fake_input_output.add_terminal_input(&[&QUIT]); + fake_input_output.add_sigwinch_event(PositionAndSize { + columns: 90, + rows: 20, + x: 0, + y: 0, + }); + let opts = CliArgs::default(); + start(Box::new(fake_input_output.clone()), opts); + let output_frames = fake_input_output + .stdout_writer + .output_frames + .lock() + .unwrap(); + let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size); + let snapshot_before_quit = + get_next_to_last_snapshot(snapshots).expect("could not find snapshot"); + assert_snapshot!(snapshot_before_quit); +} + +#[test] +pub fn window_width_increase_with_one_pane() { + let fake_win_size = PositionAndSize { + columns: 121, + rows: 20, + x: 0, + y: 0, + }; + let mut fake_input_output = get_fake_os_input(&fake_win_size); + fake_input_output.add_terminal_input(&[&QUIT]); + fake_input_output.add_sigwinch_event(PositionAndSize { + columns: 141, + rows: 20, + x: 0, + y: 0, + }); + let opts = CliArgs::default(); + start(Box::new(fake_input_output.clone()), opts); + let output_frames = fake_input_output + .stdout_writer + .output_frames + .lock() + .unwrap(); + let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size); + let snapshot_before_quit = + get_next_to_last_snapshot(snapshots).expect("could not find snapshot"); + assert_snapshot!(snapshot_before_quit); +} + +#[test] +pub fn window_height_increase_with_one_pane() { + let fake_win_size = PositionAndSize { + columns: 121, + rows: 20, + x: 0, + y: 0, + }; + let mut fake_input_output = get_fake_os_input(&fake_win_size); + fake_input_output.add_terminal_input(&[&QUIT]); + fake_input_output.add_sigwinch_event(PositionAndSize { + columns: 121, + rows: 30, + x: 0, + y: 0, + }); + let opts = CliArgs::default(); + start(Box::new(fake_input_output.clone()), opts); + let output_frames = fake_input_output + .stdout_writer + .output_frames + .lock() + .unwrap(); + let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size); + let snapshot_before_quit = + get_next_to_last_snapshot(snapshots).expect("could not find snapshot"); + assert_snapshot!(snapshot_before_quit); +} + +#[test] +pub fn window_width_and_height_decrease_with_one_pane() { + let fake_win_size = PositionAndSize { + columns: 121, + rows: 20, + x: 0, + y: 0, + }; + let mut fake_input_output = get_fake_os_input(&fake_win_size); + fake_input_output.add_terminal_input(&[&QUIT]); + fake_input_output.add_sigwinch_event(PositionAndSize { + columns: 90, + rows: 10, + x: 0, + y: 0, + }); + let opts = CliArgs::default(); + start(Box::new(fake_input_output.clone()), opts); + let output_frames = fake_input_output + .stdout_writer + .output_frames + .lock() + .unwrap(); + let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size); + let snapshot_before_quit = + get_next_to_last_snapshot(snapshots).expect("could not find snapshot"); + assert_snapshot!(snapshot_before_quit); +} diff --git a/src/tests/possible_tty_inputs.rs b/src/tests/possible_tty_inputs.rs index fa918be04..306683607 100644 --- a/src/tests/possible_tty_inputs.rs +++ b/src/tests/possible_tty_inputs.rs @@ -1,6 +1,7 @@ use crate::tests::tty_inputs::{ - COL_10, COL_121, COL_14, COL_15, COL_19, COL_20, COL_24, COL_25, COL_29, COL_30, COL_34, - COL_39, COL_4, COL_40, COL_47, COL_50, COL_60, COL_70, COL_8, COL_9, COL_90, COL_96, + COL_10, COL_121, COL_14, COL_141, COL_15, COL_19, COL_20, COL_24, COL_25, COL_29, COL_30, + COL_34, COL_39, COL_4, COL_40, COL_47, COL_50, COL_60, COL_70, COL_8, COL_80, COL_9, COL_90, + COL_96, }; use std::collections::HashMap; use std::fs; @@ -69,9 +70,11 @@ pub fn get_possible_tty_inputs() -> HashMap { let col_50_bytes = Bytes::new().content_from_str(&COL_50); let col_60_bytes = Bytes::new().content_from_str(&COL_60); let col_70_bytes = Bytes::new().content_from_str(&COL_70); + let col_80_bytes = Bytes::new().content_from_str(&COL_80); let col_90_bytes = Bytes::new().content_from_str(&COL_90); let col_96_bytes = Bytes::new().content_from_str(&COL_96); let col_121_bytes = Bytes::new().content_from_str(&COL_121); + let col_141_bytes = Bytes::new().content_from_str(&COL_141); possible_inputs.insert(4, col_4_bytes); possible_inputs.insert(8, col_8_bytes); possible_inputs.insert(9, col_9_bytes); @@ -91,8 +94,10 @@ pub fn get_possible_tty_inputs() -> HashMap { possible_inputs.insert(50, col_50_bytes); possible_inputs.insert(60, col_60_bytes); possible_inputs.insert(70, col_70_bytes); + possible_inputs.insert(80, col_80_bytes); possible_inputs.insert(90, col_90_bytes); possible_inputs.insert(96, col_96_bytes); possible_inputs.insert(121, col_121_bytes); + possible_inputs.insert(141, col_141_bytes); possible_inputs } diff --git a/src/tests/tty_inputs.rs b/src/tests/tty_inputs.rs index cf49290ee..43ac8abc3 100644 --- a/src/tests/tty_inputs.rs +++ b/src/tests/tty_inputs.rs @@ -1,3 +1,26 @@ +pub const COL_141: [&str; 20] = [ + "line1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line17-baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line18-baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "line19-baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", + "prompt $ ", +]; + pub const COL_121: [&str; 20] = [ "line1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", "line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n", @@ -457,6 +480,29 @@ pub const COL_70: [&str; 20] = [ "prompt $ ", ]; +pub const COL_80: [&str; 20] = [ + "line1-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line2-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line3-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line4-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line5-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line6-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line7-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line8-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line9-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line10-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", + "prompt $ ", +]; + pub const COL_90: [&str; 20] = [ "line1-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", "line2-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n", diff --git a/src/tests/utils.rs b/src/tests/utils.rs index 14f4cfa0f..915adfb09 100644 --- a/src/tests/utils.rs +++ b/src/tests/utils.rs @@ -45,7 +45,6 @@ pub fn get_next_to_last_snapshot(mut snapshots: Vec) -> Option { } pub mod commands { - pub const COMMAND_TOGGLE: [u8; 1] = [7]; // ctrl-g pub const QUIT: [u8; 1] = [17]; // ctrl-q pub const ESC: [u8; 1] = [27]; diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs index c41a40eef..8a44dcdf6 100644 --- a/zellij-tile/src/data.rs +++ b/zellij-tile/src/data.rs @@ -58,6 +58,11 @@ impl Default for InputMode { } } +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum Theme { + Light, + Dark +} pub mod colors { pub const WHITE: (u8, u8, u8) = (238, 238, 238); pub const GREEN: (u8, u8, u8) = (175, 255, 0); @@ -69,6 +74,7 @@ pub mod colors { #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct Palette { + pub theme: Theme, pub fg: (u8, u8, u8), pub bg: (u8, u8, u8), pub black: (u8, u8, u8), @@ -84,6 +90,7 @@ pub struct Palette { impl Default for Palette { fn default() -> Palette { Palette { + theme: Theme::Dark, fg: colors::BRIGHT_GRAY, bg: colors::BLACK, black: colors::BLACK,