fix(wm): verify foreground state before mutating focus state

As a defense-in-depth fix independent of the event-conversion layer, the
WindowManagerEvent::FocusChange handler should not update the focused
monitor/workspace/container/window state when the incoming window is not
the actual OS foreground window.

The issue notes that after a monitor switch komorebi focuses "the last
window focused from monitor 1" while the popout window on the other
monitor still receives input.

This is exactly the symptom of komorebi processing a stale/background
focus event and re-pointing its selection at monitor 1 even though real
focus is elsewhere. Verifying foreground state before mutating focus
state prevents the desync regardless of which win-event produced the
FocusChange.

fix #1679

Signed-off-by: ChinhLee <76194645+chinhkrb113@users.noreply.github.com>
This commit is contained in:
ChinhLee
2026-05-31 02:17:16 +07:00
committed by LGUG2Z
parent a3b2e28dd5
commit 57b93a7404
+31
View File
@@ -41,6 +41,30 @@ use crate::windows_api::WindowsApi;
use crate::winevent::WinEvent;
use crate::workspace::WorkspaceLayer;
fn should_skip_focus_change(foreground_hwnd: Option<isize>, window_hwnd: isize) -> bool {
matches!(foreground_hwnd, Some(hwnd) if hwnd != window_hwnd)
}
#[cfg(test)]
mod focus_change_tests {
use super::should_skip_focus_change;
#[test]
fn skips_focus_change_from_background_window() {
assert!(should_skip_focus_change(Some(1), 2));
}
#[test]
fn allows_focus_change_for_foreground_window() {
assert!(!should_skip_focus_change(Some(1), 1));
}
#[test]
fn allows_focus_change_when_foreground_unknown() {
assert!(!should_skip_focus_change(None, 1));
}
}
#[tracing::instrument]
pub fn listen_for_events(wm: Arc<Mutex<WindowManager>>) {
let receiver = wm.lock().incoming_events.clone();
@@ -341,6 +365,13 @@ impl WindowManager {
already_moved_window_handles.remove(&window.hwnd);
}
WindowManagerEvent::FocusChange(_, window) => {
if should_skip_focus_change(WindowsApi::foreground_window().ok(), window.hwnd) {
tracing::debug!(
"ignoring stale focus change for hwnd {} as it is not the foreground window",
window.hwnd
);
return Ok(());
}
// don't want to trigger the full workspace updates when there are no managed
// containers - this makes floating windows on empty workspaces go into very
// annoying focus change loops which prevents users from interacting with them