Skip to content

helper: windows that land on the Edge come back - #7

Open
mcouzinet wants to merge 1 commit into
fdussert:mainfrom
mcouzinet:window-fence
Open

mcouzinet wants to merge 1 commit into
fdussert:mainfrom
mcouzinet:window-fence

Conversation

@mcouzinet

Copy link
Copy Markdown
Contributor

The mouse fence keeps the pointer off the Edge, but nothing stops an application from placing a window there, which is how it actually happens. It happened to me twice today: a Safari window and a Finder window, both restored to a frame saved before the Edge was plugged in. The kiosk covers that display above the status-bar level, so the window was focused, held the keyboard, and was invisible. Nothing on screen says it exists, the pointer cannot reach it, and Mission Control shows the kiosk. I got the Finder one back with tell application "Finder" to set bounds of window 1 to ..., which is not a thing to ask of anybody.

Same trap AdminPlacement was written for, from the other side, so this reuses it rather than growing new geometry.

How it works. WindowFence sweeps every two seconds: one CGWindowListCopyWindowInfo call, no round trip to any application, no permission. Anything of somebody else's on layer 0 overlapping the Edge goes to AdminPlacement.rescue, and only then is the Accessibility API reached, for that one application, to set kAXPosition (and kAXSize when the window is wider than the display it is moved to). So the steady-state cost is one list read every two seconds, and AX traffic only when there is something to rescue, which is almost never.

Choices worth a look.

  • It rides on the existing fence config flag and toggle rather than adding a second switch: the promise is the same for the pointer and for windows. The menu entry is renamed Mouse fence to Edge fence. Easy to split if you would rather have two.
  • Three moves per window number, then it is left alone: an application that puts its window straight back would otherwise be fought forever, twice a second, with the window flickering between two displays.
  • Any overlap counts, not just a window entirely on the Edge, for the reason AdminPlacement.isUsable already gives: a straddling window has the part you read on the side you cannot see.
  • The window list and AX name the same window in two ways with no shared identifier, so the match is on the frame, falling back to whichever window of that application still overlaps the Edge.

Checked. scripts/test-helper.sh green (85 tests, 9 new on WindowSweep, listed in the shim registry) and scripts/build-helper.sh builds the bundle. The geometry was verified by hand on my Edge (a Finder window at x -1740 came back to the main display keeping its size). What I could not exercise here is the AX move from inside a running helper: this machine has no "Fremkit Helper Dev" identity in the keychain, so a local install would be ad-hoc signed and would drop the Accessibility and Input Monitoring grants of the helper that is running. Worth a try on a machine set up for it before merging.

🤖 Generated with Claude Code

The mouse fence keeps the pointer off the Edge, but nothing stopped an
application from placing a window there: Safari or the Finder restoring
a frame saved before the Edge was plugged in, or a display rearranged
under windows that were where they belonged. The kiosk covers that
display above the status-bar level, so a window that arrives there is
focused, holds the keyboard, and is invisible. Nothing on screen says
it exists, and the pointer cannot reach it, which is the same trap
AdminPlacement was written for, from the other side.

A two-second sweep reads the window list, which costs one call and no
round trip to any application, and hands anything of somebody else's
that overlaps the Edge to AdminPlacement, the geometry the admin window
already uses, then moves it there. The Accessibility API is reached
only for an application that actually has a window on the Edge, which
is almost never. Three moves per window is the limit: an application
that puts its window straight back would otherwise be fought forever,
twice a second, with the window flickering between two displays.

It rides on the existing fence switch rather than adding one, since it
is the same promise for the pointer and for windows, and the menu entry
is renamed from "Mouse fence" to "Edge fence" to say so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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