fix(macos): refresh the known windows after a workspace rule moved one

enforce_workspace_rules moves a window between workspaces at the start of
an event but known_window_ids is only rebuilt at the end of process_event,
so it still said the window was on the focused workspace. A Show for the
application in the same event then passed the 'already associated with
another workspace' check, found the window in no container of the focused
workspace, and created a container for it there again: the window ended up
in the containers of both workspaces and in view on the one the rule sent
it from. Windows guards the same place by looking for the window in every
workspace before adding it; here the index is brought up to date when the
enforcement moved something.
This commit is contained in:
LGUG2Z committed 2026-11-06 14:40:50 -08:00
1 parent cf845a1fba
commit 7d584a809a
1 file changed
+31
@@ -3211,6 +3211,15 @@ impl WindowManager {
self.update_focused_workspace(false, false)?;
}
// The windows that moved are on their new workspaces now, and `known_window_ids` is what
// a `Show` event is checked against to tell a window komorebi has put away from a new
// one. Left as it was before the move it says the window is still on the focused
// workspace, so the next `Show` for the application brings it back there as well, and the
// window is on two workspaces and in view on the one it was sent from.
if !to_move.is_empty() {
self.update_known_window_ids();
}
Ok(())
}
@@ -6025,6 +6034,28 @@ mod tests {
assert_eq!(op.floating, floating); // false
}
#[test]
fn known_window_ids_follow_a_window_that_a_rule_moved() {
// what `enforce_workspace_rules` does to a window, short of reading it from the
// application: out of the container on the focused workspace, into one on another
let (mut wm, _context) = tiled(DefaultLayout::BSP, &[7]);
wm.update_known_window_ids();
assert_eq!(wm.known_window_ids.get(&7), Some(&(0, 0)));
let monitor = wm.monitors_mut().front_mut().unwrap();
monitor.ensure_workspace_count(2);
let window = monitor.workspaces_mut()[0].remove_window(7).unwrap();
monitor.workspaces_mut()[1]
.new_container_for_window(window)
.unwrap();
// nothing has told the window manager yet, so a `Show` would still be read against 0
assert_eq!(wm.known_window_ids.get(&7), Some(&(0, 0)));
wm.update_known_window_ids();
assert_eq!(wm.known_window_ids.get(&7), Some(&(0, 1)));
}
// Resizing a tile by dragging an edge. A Rect is `left, top, width, height` (`right` and
// `bottom` are the size), and the work area of the monitor is 1920x1050, which makes the
// right-hand stack of an UltrawideVerticalStack layout narrow enough to expose the old guard.