Skip to content

Commit d1954ca

Browse files
committed
fix: prevent floating windows from getting stuck in corner after workspace switch
When AeroSpace hides windows from inactive workspaces, it moves them to a screen corner and saves their previous position so they can be restored later (unhideFromCorner). A race condition with AX notifications could cause the following broken cycle: 1. hideInCorner() saves the normal position, moves window to corner 2. unhideFromCorner() restores it, clears prevUnhiddenProportionalPosition 3. A spurious AX event triggers unhideFromCorner() again — no-op since prevUnhiddenProportionalPosition is already nil, window stays in place 4. Next workspace switch calls hideInCorner() again — isHiddenInCorner is false (nil check) — so it saves the *current* position, which is already the corner position 5. unhideFromCorner() then restores the window to the corner → stuck forever Fix: in hideInCorner(), only record the window's position when its top-left corner is actually inside the monitor's visible rect. If it's already outside (stuck in a corner from a previous broken cycle), skip saving to avoid permanently anchoring it to the corner. Fixes: #1875
1 parent 8134ad0 commit d1954ca

File tree

1 file changed

+10
-3
lines changed

1 file changed

+10
-3
lines changed

Sources/AppBundle/tree/MacWindow.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,16 @@ final class MacWindow: Window {
131131
guard let windowRect = try await getAxRect() else { return }
132132
let topLeftCorner = windowRect.topLeftCorner
133133
let monitorRect = windowRect.center.monitorApproximation.rect // Similar to layoutFloatingWindow. Non idempotent
134-
let absolutePoint = topLeftCorner - monitorRect.topLeftCorner
135-
prevUnhiddenProportionalPositionInsideWorkspaceRect =
136-
CGPoint(x: absolutePoint.x / monitorRect.width, y: absolutePoint.y / monitorRect.height)
134+
// Only record the window's position if it is actually inside the visible monitor
135+
// area. If the window is already outside (i.e. stuck at a corner from a previous
136+
// broken hide/unhide cycle), skipping the save prevents permanently anchoring the
137+
// window to the corner on the next unhideFromCorner() call.
138+
// See: https://github.com/nikitabobko/AeroSpace/discussions/1875
139+
if nodeMonitor.visibleRect.contains(topLeftCorner) {
140+
let absolutePoint = topLeftCorner - monitorRect.topLeftCorner
141+
prevUnhiddenProportionalPositionInsideWorkspaceRect =
142+
CGPoint(x: absolutePoint.x / monitorRect.width, y: absolutePoint.y / monitorRect.height)
143+
}
137144
}
138145
let p: CGPoint
139146
switch corner {

0 commit comments

Comments
 (0)