Display pinning: park on sleep, retry-on-wake with backoff#3
Open
imaznation wants to merge 1 commit into
Open
Conversation
Symptom on a multi-monitor kiosk setup: after sleep/wake or
screen-lock/unlock, the kiosk window could end up on the main display
instead of the configured secondary target. macOS surfaces the wake
notifications *before* NSScreen.screens reflects the secondary display
being re-enumerated (USB-C / DisplayPort handshakes take a few seconds),
so a single repin call right at wake-time finds no target and falls
back to whatever screen exists, typically main.
This change makes window pinning robust across the wake transition:
Wake side:
- Observe NSApplication.didChangeScreenParameters plus
NSWorkspace.didWake, screensDidWake, sessionDidBecomeActive. Any
of those triggers `repinDashboard`.
- `repinDashboard` schedules `retryRepin` with a backoff
[0, 0.5, 1.5, 3, 6, 12, 30, 60, 60, 60s] — try right away, then
progressively wait for the target to re-enumerate.
- Each attempt: if the configured target screen exists (or no
specific target is configured), `show()` the window; otherwise
`hide()` (orderOut) so it stays parked off-screen and retry.
Sleep / lock side:
- Observe NSWorkspace.willSleep, screensDidSleep,
sessionDidResignActive — any of those calls `parkDashboard` which
orderOuts the window. That way the wake transition starts with the
window already off-screen instead of macOS relocating it to main
before our wake handler fires.
DashboardWindowController gains a `hide()` method to support both
sides of this cycle without releasing the window.
The backoff hard ceiling (10 attempts, ~ 4 minutes) means a target
that doesn't come back leaves the window parked rather than the user
finding it on the wrong screen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Symptom
On a multi-monitor kiosk setup, after sleep/wake or screen-lock/unlock, the kiosk window can end up on the main display instead of the configured secondary target. macOS surfaces the wake notifications before
NSScreen.screensreflects the secondary display being re-enumerated (USB-C / DisplayPort handshakes take a few seconds), so a single repin call right at wake-time finds no target and falls back to whatever screen exists — typically main. Once it's there, macOS often keeps it there across the next wake.Changes
Makes window pinning robust across the wake transition.
Wake side
NSApplication.didChangeScreenParametersplusNSWorkspace.didWake/screensDidWake/sessionDidBecomeActive. Any of these triggersrepinDashboard.repinDashboardschedulesretryRepinwith a backoff[0, 0.5, 1.5, 3, 6, 12, 30, 60, 60, 60s]— try right away, then progressively wait for the target to re-enumerate.show()the window; otherwisehide()(orderOut) so it stays parked off-screen and retry.Sleep / lock side
NSWorkspace.willSleep/screensDidSleep/sessionDidResignActive— any of those callsparkDashboardwhichorderOuts the window. That way the wake transition starts with the window already off-screen instead of macOS relocating it to main before our wake handler fires.Window controller
DashboardWindowControllergains ahide()method to support both sides of this cycle without releasing the window.Notes
The backoff hard ceiling (10 attempts, ~4 minutes) means a target that doesn't come back leaves the window parked rather than the user finding it on the wrong screen. No behavior change in the steady state — only changes the transient handling around sleep/wake.