Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix: status feed reports Focus::None honestly
focused_window() falls back to the most recent real window so the bar
doesn't flash while overlay UI briefly holds focus — but that fallback
also swallowed explicit Focus::None (a desktop click), leaving the bar
showing the last window as if it still had focus while keystrokes went
nowhere. build_status_update now bypasses the fallback when the seat
focus is literally None; the overlay case keeps it.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/server/status_server.rs | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/server/status_server.rs b/src/server/status_server.rs
index 5df62b4..6ceab64 100644
--- a/src/server/status_server.rs
+++ b/src/server/status_server.rs
@@ -296,7 +296,21 @@ fn format_for_subscription(sub: Subscription, update: &StatusUpdate) -> String {
}
pub unsafe fn build_status_update(wm: &crate::window_manager::WindowManager) -> StatusUpdate {
- let focused_window = wm.focused_window();
+ // `focused_window()` falls back to the most recent real window so the
+ // bar doesn't flash while overlay UI (the launcher) briefly holds
+ // focus. But an explicit Focus::None (desktop click) is a real,
+ // user-visible state — keystrokes go nowhere — and the status feed
+ // must report it honestly instead of showing the last window as if it
+ // still had focus.
+ let seat_focus_is_none = wm
+ .first_seat()
+ .map(|s| matches!((*s).focused, crate::seat::Focus::None))
+ .unwrap_or(false);
+ let focused_window = if seat_focus_is_none {
+ std::ptr::null_mut()
+ } else {
+ wm.focused_window()
+ };
let text = format!("Mode: {:?} | Zoom: {:.2} | Pan: ({:.0}, {:.0})", wm.mode, wm.desk_zoom, wm.desk_pan_x, wm.desk_pan_y);
let escaped = text.replace('\\', "\\\\").replace('"', "\\\"");