From a9a35c1804d72ef92e04ee71555bd9e5a08fa17e Mon Sep 17 00:00:00 2001 From: ElKowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 18 Aug 2023 15:51:42 +0200 Subject: [PATCH] Support --duration close timer for windows (#884) --- CHANGELOG.md | 1 + Cargo.lock | 7 +++-- crates/eww/Cargo.toml | 3 +-- crates/eww/src/app.rs | 58 +++++++++++++++++++++++++++++++++++----- crates/eww/src/opts.rs | 11 +++++++- crates/eww/src/server.rs | 1 + 6 files changed, 69 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2edca8..9b8ad17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to eww will be listed here, starting at changes since versio - Add `EWW_TIME` magic variable (By: Erenoit) - Add trigonometric functions (`sin`, `cos`, `tan`, `cot`) and degree/radian conversions (`degtorad`, `radtodeg`) (By: end-4) - Add `substring` function to simplexpr +- Add `--duration` flag to `eww open` ## [0.4.0] (04.09.2022) diff --git a/Cargo.lock b/Cargo.lock index adc5e01..e3e9ca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -683,8 +683,7 @@ dependencies = [ "derive_more", "eww_shared_util", "extend", - "futures-core", - "futures-util", + "futures", "gdk", "gdk-pixbuf", "gdkx11", @@ -786,6 +785,7 @@ checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -854,10 +854,13 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml index c19a862..fe40aec 100644 --- a/crates/eww/Cargo.toml +++ b/crates/eww/Cargo.toml @@ -50,8 +50,7 @@ simple-signal = "1.1" unescape = "0.1" tokio = { version = "1.31.0", features = ["full"] } -futures-core = "0.3.28" -futures-util = "0.3.28" +futures = "0.3.28" tokio-util = "0.7.8" sysinfo = "0.29.8" diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 5159bfe..3b306be 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -55,6 +55,7 @@ pub enum DaemonCommand { anchor: Option, screen: Option, should_toggle: bool, + duration: Option, sender: DaemonResponseSender, }, CloseWindows { @@ -114,6 +115,9 @@ pub struct App { pub app_evt_send: UnboundedSender, pub script_var_handler: ScriptVarHandlerHandle, + /// Senders that will cancel a windows auto-close timer when started with --duration. + pub window_close_timer_abort_senders: HashMap>, + pub paths: EwwPaths, } @@ -181,20 +185,27 @@ impl App { if should_toggle && self.open_windows.contains_key(w) { self.close_window(w) } else { - self.open_window(w, None, None, None, None) + self.open_window(w, None, None, None, None, None) } }) .filter_map(Result::err); sender.respond_with_error_list(errors)?; } - DaemonCommand::OpenWindow { window_name, pos, size, anchor, screen: monitor, should_toggle, sender } => { + DaemonCommand::OpenWindow { + window_name, + pos, + size, + anchor, + screen: monitor, + should_toggle, + duration, + sender, + } => { let is_open = self.open_windows.contains_key(&window_name); - let result = if !is_open { - self.open_window(&window_name, pos, size, monitor, anchor) - } else if should_toggle { + let result = if should_toggle && is_open { self.close_window(&window_name) } else { - Ok(()) + self.open_window(&window_name, pos, size, monitor, anchor, duration) }; sender.respond_with_result(result)?; } @@ -294,6 +305,9 @@ impl App { /// Close a window and do all the required cleanups in the scope_graph and script_var_handler fn close_window(&mut self, window_name: &str) -> Result<()> { + if let Some(old_abort_send) = self.window_close_timer_abort_senders.remove(window_name) { + _ = old_abort_send.send(()); + } let eww_window = self .open_windows .remove(window_name) @@ -320,11 +334,13 @@ impl App { size: Option, monitor: Option, anchor: Option, + duration: Option, ) -> Result<()> { self.failed_windows.remove(window_name); log::info!("Opening window {}", window_name); // if an instance of this is already running, close it + // TODO make reopening optional via a --no-reopen flag? if self.open_windows.contains_key(window_name) { self.close_window(window_name)?; } @@ -380,6 +396,34 @@ impl App { } })); + if let Some(duration) = duration { + let app_evt_sender = self.app_evt_send.clone(); + let window_name = window_name.to_string(); + + let (abort_send, abort_recv) = futures::channel::oneshot::channel(); + + glib::MainContext::default().spawn_local({ + let window_name = window_name.clone(); + async move { + tokio::select! { + _ = glib::timeout_future(duration) => { + let (response_sender, mut response_recv) = daemon_response::create_pair(); + let command = DaemonCommand::CloseWindows { windows: vec![window_name], sender: response_sender }; + if let Err(err) = app_evt_sender.send(command) { + log::error!("Error sending close window command to daemon after gtk window destroy event: {}", err); + } + _ = response_recv.recv().await; + } + _ = abort_recv => {} + } + } + }); + + if let Some(old_abort_send) = self.window_close_timer_abort_senders.insert(window_name, abort_send) { + _ = old_abort_send.send(()); + } + } + self.open_windows.insert(window_name.to_string(), eww_window); }; @@ -407,7 +451,7 @@ impl App { let window_names: Vec = self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect(); for window_name in &window_names { - self.open_window(window_name, None, None, None, None)?; + self.open_window(window_name, None, None, None, None, None)?; } Ok(()) } diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 2880011..75d08c3 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -120,6 +120,10 @@ pub enum ActionWithServer { /// If the window is already open, close it instead #[arg(long = "toggle")] should_toggle: bool, + + /// Automatically close the window after a specified amount of time, i.e.: 1s + #[arg(long, value_parser=parse_duration)] + duration: Option, }, /// Open multiple windows at once. @@ -218,7 +222,7 @@ impl ActionWithServer { ActionWithServer::OpenMany { windows, should_toggle } => { return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, should_toggle, sender }); } - ActionWithServer::OpenWindow { window_name, pos, size, screen, anchor, should_toggle } => { + ActionWithServer::OpenWindow { window_name, pos, size, screen, anchor, should_toggle, duration } => { return with_response_channel(|sender| app::DaemonCommand::OpenWindow { window_name, pos, @@ -226,6 +230,7 @@ impl ActionWithServer { anchor, screen, should_toggle, + duration, sender, }) } @@ -254,3 +259,7 @@ where let (sender, recv) = daemon_response::create_pair(); (f(sender), Some(recv)) } + +fn parse_duration(s: &str) -> Result { + DynVal::from_string(s.to_owned()).as_duration() +} diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 5b557fa..fbe2286 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -84,6 +84,7 @@ pub fn initialize_server( css_provider: gtk::CssProvider::new(), script_var_handler, app_evt_send: ui_send.clone(), + window_close_timer_abort_senders: HashMap::new(), paths, };