Skip to content

feat: every window goes back after a display change - #115

Merged
ostapondo merged 2 commits into
mainfrom
feat/desk-snapshot
Aug 26, 2026
Merged

ostapondo merged 2 commits into
mainfrom
feat/desk-snapshot

Conversation

@ostapondo

@ostapondo ostapondo commented Aug 25, 2026

Copy link
Copy Markdown
Owner

What changed, and why

Plonk put its own placements back after a display change and left every other window scrambled. Now DeskWatcher keeps a note of where every window on screen sits, per set of attached displays. A window Plonk places (hotkey, drop, grab, API, workspace) is noted the moment it is placed. The rest comes from one AX walk on its own queue, and only when a one-call look at the window server says something has moved since the last note, so an idle desk costs nothing. Each walk is folded into what was known, so a window on another Space keeps its place. A walk that a display change or a switch-off overtakes lands on nothing.

When that set of displays comes back the desk is put back first, on its own queue so it never waits behind a stale walk, skipping windows already where the note says, with one change-bus announcement for all of it. Then the zone placements cover only what the desk had no note of, so a window is never moved twice and a hand move after a snap is what wins. A second display change mid-restore stops the first restore where it is.

New switch under "When the desktop changes", on by default; it widens the existing display-change switch and is put away when that one is off. Config has the one predicate all three readers use. Off, or with the Zones module off, the timer stops and the desks are forgotten. Apps on the exceptions list are filtered on the main queue before a walk, so the walk never reads the config. Memory only: an AX element does not survive a relaunch and neither does the scramble.

What you ran

(cd App && swift build)
./scripts/test.sh                 494 pass, 13 new
./scripts/lint.sh
node scripts/check-strings.mjs
./scripts/check-security-claims.sh

Checks

  • swift build passes on every commit in the branch
  • New logic is covered: DeskMemoryTests (per-desk keys, replace, note, clear, merging a walk, what a walk notes and skips, the fraction round trip, the switches)
  • Conventional commits
  • Nothing touches the network, permissions, entitlements or the update path
  • No screenshots; one new toggle row

If this moves windows

Setup: macOS 26, one built-in display at 2x.

  • One screen. Built and ran it: placed a bench window over the API, waited past the placement note and a timer tick, app stayed up, nothing else moved.
  • Put it back: not touched.
  • Second monitor: no hardware.
  • Mixed scale factors: no hardware.
  • Unplug and plug back in: no hardware. This is the case the change is for, so a report from a desk with two monitors would be worth more than the tests here.
  • Excluded app: restoreDesk goes through the same mayTouch check as the zone placements.

DeskWatcher notes where every window on screen sits, per set of attached
displays: each window Plonk places the moment it is placed, and the whole
desk by one AX walk on its own queue whenever the window server says
something has moved, folded into what was known so a window on another
Space keeps its place. When that set of displays comes back the desk is put
back first, off the main queue with one announcement, then the zone
placements cover what the desk had no note of. Off, and forgotten, when
the display-change switch or the Zones module is off. A second switch
under When the desktop changes, on by default.
# Conflicts:
#	CHANGELOG.md
#	docs/zones.md
@ostapondo
ostapondo merged commit 4df2a5c into main Aug 26, 2026
6 of 7 checks passed
@ostapondo
ostapondo deleted the feat/desk-snapshot branch August 26, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant