fix: prevent window oscillation when moving windows between displays#395
Merged
acsandmann merged 1 commit intoJun 18, 2026
Conversation
…displays PR acsandmann#392 (space actor) changed handle_window_server_appeared to reassign a known window to the WindowServer-reported space on every SpaceWindowCreated event. That event also fires as a side effect of Rift's own SetWindowFrame dragging a window across a display seam, so Rift chased the echo of its own move and re-tiled endlessly. Apps that resist AX frame changes (e.g. Zen, Outlook) never settle, producing a ~170 ms flap between displays. Unlike the WindowFrameChanged path, this appeared->reassign path had no self-move guard. Skip the reassignment when a Rift frame transaction is still in flight for the window (transaction_manager.get_target_frame is Some); the authoritative space record is still updated. Genuine external moves (manual drag, no pending transaction) continue to reassign. Adds appeared_does_not_reassign_window_while_rift_move_is_in_flight (the regression guard) and appeared_reassigns_window_without_pending_rift_move (ensures genuine external moves are still followed).
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.
Summary
Fixes a regression introduced by #392 (
feat: space actor, this PR's base) where moving a window between displays makes some apps oscillate rapidly between the two displays instead of settling. It reproduces with both themove_window_to_displayshortcut and a manual mouse drag, and is most visible with frame-resisting apps like Zen and Outlook.Root cause: #392 changed
handle_window_server_appearedto callreassign_window_to_authoritative_spacefor any known window on everyWindowServerAppearedevent, re-tiling it onto the WindowServer-reported space. Before #392, this event was ignored for known windows.WindowServerAppearedis the CGS/SkyLightSpaceWindowCreatedevent (actor/window_notify.rs), which also fires as a side effect of Rift's ownSetWindowFramedragging a window across a display seam — and fires for both spaces while the window straddles the boundary. So Rift treated the echo of its own move as authoritative, reassigned the window, re-tiled it (moving it again), and chased it indefinitely.Unlike the parallel
WindowFrameChangedpath (actor/reactor/events/window.rs), which suppresses self-induced moves via the pending-transaction (txid) check, the newappeared → reassignpath had no self-move guard. Apps that accept the frame change settle after one event; frame-resisting apps never settle, so they oscillate.This is not a removed guard — the existing oscillation protections (#342 two-tree desync, floating ping-pong, churn/sleep quarantine) are all intact; they simply don't cover this newly-added path.
Diagnosis from live logs: a 12 s capture of one repro showed the Zen window (
WindowServerId(54951)) generating 121WindowServerAppearedevents split almost evenly across both displays (60 on one space, 61 on the other), driving ~52 reassign re-tiles — the window's frame alternating between two origins roughly every 170 ms with noMoveWindowToDisplaycommand in between.Fix: in
handle_window_server_appeared, skip thereassign_window_to_authoritative_spacefallback when a Rift frame transaction is still in flight for the window (transaction_manager.get_target_frame(wsid).is_some()) — i.e. when the appearance is an echo of Rift's own move. The authoritative-space record is still updated, so #392's tested bookkeeping contract is unchanged. Genuine external moves (manual drag with no pending transaction) still reassign and follow the window.Tests:
appeared_does_not_reassign_window_while_rift_move_is_in_flight— the regression guard; fails before the fix (the window is reassigned to the echo space), passes after.appeared_reassigns_window_without_pending_rift_move— ensures genuine external space changes are still followed (guards against over-suppression).Test plan
cargo testpasses (284 tests, including 2 new tests)