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
|
||||
/// window has been dropped to the tray (no egui frame is running then).
|
||||
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 {
|
||||
@@ -131,6 +135,25 @@ impl ksni::Tray for PixelPassTray {
|
||||
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>> {
|
||||
use ksni::menu::{MenuItem, StandardItem};
|
||||
vec![
|
||||
@@ -202,6 +225,7 @@ pub fn start(proxy: EventLoopProxy<UserEvent>) -> Option<TrayHandle> {
|
||||
status: TrayStatus::Idle,
|
||||
icon,
|
||||
proxy,
|
||||
registered: registered_thread.clone(),
|
||||
};
|
||||
let handle = match tray.spawn().await {
|
||||
Ok(handle) => handle,
|
||||
|
||||
Reference in New Issue
Block a user