fix(focus): restore focused window after workspace switch - #4
Merged
Conversation
Root cause: in the X11 backend, WindowManager::focus issued set_input_focus(w) and then called reconcile_focus() to verify the server accepted it, but only committed the logical focus (mon.focused / focus_stack) AFTER that reconcile. reconcile_focus compares the real X focus against mon.focused, so when the focus request's caller had not pre-written mon.focused (e.g. ViewWorkspace, which emits FocusWindow(best_focus(mi)) without updating mon.focused itself — unlike FocusDirection), mon.focused still named the window focused on the just-left workspace. reconcile_focus then saw logical != real and re-asserted input focus onto that previous, now hidden-but-viewable window, leaving real X focus parked on a hidden window while Maverick believed the visible one was focused. The only recovery was a later h/l press, which pre-sets mon.focused and so makes the reconcile a no-op. Fix: commit mon.focused / focus_stack BEFORE reconcile_focus() in focus(), so the logical focus already matches the window we just focused and the reconcile is a no-op. This matches the ordering already used by the focus(None) branch and fixes every FocusWindow emitter uniformly without touching core state or X calls in core. Verified: full cargo test -p maverick (343 tests, including the property/chaos invariant harness) passes; cargo check --workspace passes. The X-level reconciliation is exercised under Xephyr via the input-trace diagnostics. Added a core state/effect regression test (view_workspace_round_trip_keeps_focused_window) guarding the ViewWorkspace focus contract.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause: in the X11 backend, WindowManager::focus issued set_input_focus(w) and then called reconcile_focus() to verify the server accepted it, but only committed the logical focus (mon.focused / focus_stack) AFTER that reconcile. reconcile_focus compares the real X focus against mon.focused, so when the focus request's caller had not pre-written mon.focused (e.g. ViewWorkspace, which emits FocusWindow(best_focus(mi)) without updating mon.focused itself — unlike FocusDirection), mon.focused still named the window focused on the just-left workspace. reconcile_focus then saw logical != real and re-asserted input focus onto that previous, now hidden-but-viewable window, leaving real X focus parked on a hidden window while Maverick believed the visible one was focused. The only recovery was a later h/l press, which pre-sets mon.focused and so makes the reconcile a no-op.
Fix: commit mon.focused / focus_stack BEFORE reconcile_focus() in focus(), so the logical focus already matches the window we just focused and the reconcile is a no-op. This matches the ordering already used by the focus(None) branch and fixes every FocusWindow emitter uniformly without touching core state or X calls in core.
Verified: full cargo test -p maverick (343 tests, including the property/chaos invariant harness) passes; cargo check --workspace passes. The X-level reconciliation is exercised under Xephyr via the input-trace diagnostics. Added a core state/effect regression test (view_workspace_round_trip_keeps_focused_window) guarding the ViewWorkspace focus contract.