breadbar: reconnect Hyprland event stream, fix notification spec violations, point lock button at breadlock
- src/bar/workspaces.rs: the Hyprland EventStream loop exited permanently on the first Err/end-of-stream (Hyprland restart/reload, IPC hiccup), freezing every workspace button for the bar's remaining life. Now wrapped in a reconnect loop with capped exponential backoff, re-syncing workspace state on every reconnect - src/notifications/mod.rs + popup.rs: three spec deviations fixed — expire_timeout=0 now means never-expire instead of being coerced to 5s (and a critical-urgency notification with no explicit timeout also persists by default); NotificationClosed is now emitted with the correct reason code whenever a notification actually goes away (expiry or an explicit CloseNotification call); replaces_id no longer races its auto-dismiss timer against the replacement's, via a per-id generation counter checked before a stale timer is allowed to dismiss anything - src/main.rs + README.md + assets/icons-needed.txt: lock button (and its docs) now invoke breadlock instead of hyprlock, the thing breadlock was built to replace - src/notifications/mod.rs: added 4 unit tests for the new expire_timeout/ urgency mapping (pulled into a pure compute_expire() for testability) — this crate had zero test coverage before
This commit is contained in:
parent
e8d5fd5a52
commit
1f2d58d97f
6 changed files with 229 additions and 46 deletions
|
|
@ -10,28 +10,57 @@ use relm4::ComponentSender;
|
|||
|
||||
use crate::AppInput;
|
||||
|
||||
/// Fetches the current workspace list + active workspace and pushes both to
|
||||
/// the app — used both for the initial state and to re-sync after the event
|
||||
/// stream reconnects (state may have changed while we were disconnected).
|
||||
async fn sync_state(sender: &ComponentSender<crate::App>) {
|
||||
if let Ok(ws) = Workspaces::get_async().await {
|
||||
sender.input(AppInput::WorkspaceList(ws.to_vec()));
|
||||
}
|
||||
if let Ok(active) = Workspace::get_active_async().await {
|
||||
sender.input(AppInput::ActiveWorkspace(active.id));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_watcher(sender: ComponentSender<crate::App>) {
|
||||
relm4::spawn(async move {
|
||||
if let Ok(ws) = Workspaces::get_async().await {
|
||||
sender.input(AppInput::WorkspaceList(ws.to_vec()));
|
||||
}
|
||||
if let Ok(active) = Workspace::get_active_async().await {
|
||||
sender.input(AppInput::ActiveWorkspace(active.id));
|
||||
}
|
||||
sync_state(&sender).await;
|
||||
|
||||
let mut stream = EventStream::new();
|
||||
while let Some(Ok(event)) = stream.next().await {
|
||||
match event {
|
||||
Event::WorkspaceChanged(data) => {
|
||||
sender.input(AppInput::ActiveWorkspace(data.id));
|
||||
}
|
||||
Event::WorkspaceAdded(_) | Event::WorkspaceDeleted(_) => {
|
||||
if let Ok(ws) = Workspaces::get_async().await {
|
||||
sender.input(AppInput::WorkspaceList(ws.to_vec()));
|
||||
// Hyprland's IPC event socket can drop out from under us — a
|
||||
// Hyprland restart/reload, or just a transient hiccup — at which
|
||||
// point `stream.next()` yields `None` (or an `Err`, also excluded
|
||||
// by this `while let Some(Ok(..))` pattern). That used to just fall
|
||||
// through and end this whole task permanently, freezing every
|
||||
// workspace button for the rest of the bar's life. Reconnect with a
|
||||
// capped exponential backoff instead of giving up.
|
||||
let mut backoff = std::time::Duration::from_millis(500);
|
||||
const MAX_BACKOFF: std::time::Duration = std::time::Duration::from_secs(30);
|
||||
|
||||
loop {
|
||||
let mut stream = EventStream::new();
|
||||
while let Some(Ok(event)) = stream.next().await {
|
||||
backoff = std::time::Duration::from_millis(500);
|
||||
match event {
|
||||
Event::WorkspaceChanged(data) => {
|
||||
sender.input(AppInput::ActiveWorkspace(data.id));
|
||||
}
|
||||
Event::WorkspaceAdded(_) | Event::WorkspaceDeleted(_) => {
|
||||
if let Ok(ws) = Workspaces::get_async().await {
|
||||
sender.input(AppInput::WorkspaceList(ws.to_vec()));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
eprintln!(
|
||||
"breadbar: Hyprland event stream ended (restart/reload/IPC hiccup); \
|
||||
reconnecting in {:?}",
|
||||
backoff
|
||||
);
|
||||
tokio::time::sleep(backoff).await;
|
||||
backoff = (backoff * 2).min(MAX_BACKOFF);
|
||||
sync_state(&sender).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue