fix(gui/tray): track watcher loss and never strand the window
The `registered` flag was one-way: set true once the tray registered, but never cleared if the StatusNotifierWatcher later disappeared (panel restart, tray plugin disabled, tray app killed). After that, a Wayland close-to-tray would destroy the window into a tray that no longer exists, with no recovery short of SIGTERM. Implement ksni's `watcher_online`/`watcher_offline` callbacks to keep the shared flag in sync. On offline, also force the window back (idempotent `show()`) so a window that was already hidden when the watcher died isn't stranded, and return true to keep the service alive for re-registration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -87,6 +87,10 @@ struct PixelPassTray {
|
|||||||
/// Wakes the winit loop and delivers the action — works even when the
|
/// Wakes the winit loop and delivers the action — works even when the
|
||||||
/// window has been dropped to the tray (no egui frame is running then).
|
/// window has been dropped to the tray (no egui frame is running then).
|
||||||
proxy: EventLoopProxy<UserEvent>,
|
proxy: EventLoopProxy<UserEvent>,
|
||||||
|
/// Shared with [`TrayHandle`]; kept in sync with the watcher's presence via
|
||||||
|
/// the `watcher_online`/`watcher_offline` callbacks so the app never diverts
|
||||||
|
/// a close to a tray that has since disappeared.
|
||||||
|
registered: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PixelPassTray {
|
impl PixelPassTray {
|
||||||
@@ -131,6 +135,25 @@ impl ksni::Tray for PixelPassTray {
|
|||||||
self.notify(TrayAction::Show);
|
self.notify(TrayAction::Show);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The StatusNotifierWatcher came back (e.g. the panel restarted). Mark the
|
||||||
|
/// tray live again so close-to-tray can resume hiding the window.
|
||||||
|
fn watcher_online(&self) {
|
||||||
|
self.registered.store(true, Ordering::Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The watcher went away (panel restart, tray plugin disabled, …). Clear the
|
||||||
|
/// flag so a subsequent close quits normally instead of destroying the window
|
||||||
|
/// into a tray that no longer exists, and force the window back now in case
|
||||||
|
/// it was already hidden (otherwise it'd be stranded with no way to restore).
|
||||||
|
/// Returning `true` keeps the service alive so it re-registers if the watcher
|
||||||
|
/// returns.
|
||||||
|
fn watcher_offline(&self, reason: ksni::OfflineReason) -> bool {
|
||||||
|
tracing::warn!("tray: StatusNotifierWatcher offline ({reason:?}); restoring window");
|
||||||
|
self.registered.store(false, Ordering::Release);
|
||||||
|
self.notify(TrayAction::Show);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
|
fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
|
||||||
use ksni::menu::{MenuItem, StandardItem};
|
use ksni::menu::{MenuItem, StandardItem};
|
||||||
vec![
|
vec![
|
||||||
@@ -202,6 +225,7 @@ pub fn start(proxy: EventLoopProxy<UserEvent>) -> Option<TrayHandle> {
|
|||||||
status: TrayStatus::Idle,
|
status: TrayStatus::Idle,
|
||||||
icon,
|
icon,
|
||||||
proxy,
|
proxy,
|
||||||
|
registered: registered_thread.clone(),
|
||||||
};
|
};
|
||||||
let handle = match tray.spawn().await {
|
let handle = match tray.spawn().await {
|
||||||
Ok(handle) => handle,
|
Ok(handle) => handle,
|
||||||
|
|||||||
Reference in New Issue
Block a user