From b6b7bc8453a5deecae0f0f4cc0c5f8639df97964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?bbb651=20=F0=9F=87=AE=F0=9F=87=B1?= <53972231+bbb651@users.noreply.github.com> Date: Wed, 5 Feb 2025 21:17:57 +0200 Subject: [PATCH] Avoid recreating windows when only duration changes (#1236) This avoids possible flicker with windows such as OSDs. The logic to do this was added in #263 but broke when durations were added. Fixes #260. --- crates/eww/src/app.rs | 83 ++++++++++++++++++------------ crates/eww/src/window_arguments.rs | 11 ++++ 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 38a3f846..9494a9d3 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -390,14 +390,24 @@ impl App { self.failed_windows.remove(instance_id); log::info!("Opening window {} as '{}'", window_args.window_name, instance_id); - // if an instance of this is already running, close it - if self.open_windows.contains_key(instance_id) { - self.close_window(instance_id)?; - } - + // if an instance of this is already running and arguments haven't change, only update duration + let reuse_window = if self.open_windows.contains_key(instance_id) { + if self.instance_id_to_args.get(instance_id).is_some_and(|args| window_args.can_reuse_window_with_args(args)) { + true + } else { + self.close_window(instance_id)?; + false + } + } else { + false + }; self.instance_id_to_args.insert(instance_id.to_string(), window_args.clone()); let open_result: Result<_> = (|| { + if reuse_window { + return Ok(()); + } + let window_name: &str = &window_args.window_name; let window_def = self.eww_config.get_window(window_name)?.clone(); @@ -454,38 +464,12 @@ impl App { } })); - let duration = window_args.duration; - if let Some(duration) = duration { - let app_evt_sender = self.app_evt_send.clone(); - - let (abort_send, abort_recv) = futures::channel::oneshot::channel(); - - glib::MainContext::default().spawn_local({ - let instance_id = instance_id.to_string(); - async move { - tokio::select! { - _ = glib::timeout_future(duration) => { - let (response_sender, mut response_recv) = daemon_response::create_pair(); - let command = DaemonCommand::CloseWindows { windows: vec![instance_id.clone()], 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(instance_id.to_string(), abort_send) { - _ = old_abort_send.send(()); - } - } - self.open_windows.insert(instance_id.to_string(), eww_window); Ok(()) })(); + self.update_window_duration(instance_id, window_args.duration); + if let Err(err) = open_result { self.failed_windows.insert(instance_id.to_string()); Err(err).with_context(|| format!("failed to open window `{}`", instance_id)) @@ -494,6 +478,39 @@ impl App { } } + fn update_window_duration(&mut self, instance_id: &str, duration: Option) { + if let Some(duration) = duration { + let app_evt_sender = self.app_evt_send.clone(); + + let (abort_send, abort_recv) = futures::channel::oneshot::channel(); + + glib::MainContext::default().spawn_local({ + let instance_id = instance_id.to_string(); + async move { + tokio::select! { + _ = glib::timeout_future(duration) => { + let (response_sender, mut response_recv) = daemon_response::create_pair(); + let command = DaemonCommand::CloseWindows { windows: vec![instance_id.clone()], 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(instance_id.to_string(), abort_send) { + _ = old_abort_send.send(()); + } + } else { + if let Some(old_abort_send) = self.window_close_timer_abort_senders.remove(instance_id) { + _ = old_abort_send.send(()); + } + } + } + /// Load the given configuration, reloading all script-vars and attempting to reopen all windows that where opened. pub fn load_config(&mut self, config: config::EwwConfig) -> Result<()> { log::info!("Reloading windows"); diff --git a/crates/eww/src/window_arguments.rs b/crates/eww/src/window_arguments.rs index 5a079795..b6e64756 100644 --- a/crates/eww/src/window_arguments.rs +++ b/crates/eww/src/window_arguments.rs @@ -87,4 +87,15 @@ impl WindowArguments { Ok(local_variables) } + + // Compares all values except `duration`, since it's not used by the window itself + pub fn can_reuse_window_with_args(&self, other: &Self) -> bool { + self.window_name == other.window_name + && self.instance_id == other.instance_id + && self.anchor == other.anchor + && self.args == other.args + && self.monitor == other.monitor + && self.pos == other.pos + && self.size == other.size + } }