Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Magic Switch/Manager/DisplayMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ import SwiftUI
/// and works for any display, not just Apple's.
///
/// Only a genuine absent → present *edge* fires the trigger:
/// - The set of displays online at launch is the baseline. A display that's
/// already attached when the app starts never fires — an app (re)start must
/// not yank peripherals from a Mac the user is actively working on.
/// - Launch counts as a dock: a trigger display that's already attached when
/// the app starts fires once, after the same debounce as a live change.
/// - Reconfiguration callbacks are debounced and then *reconciled* against
/// `CGGetOnlineDisplayList`, so the remove/add churn of a resolution change
/// or a wake-time renegotiation nets out to no edge.
Expand Down Expand Up @@ -109,17 +108,18 @@ final class DisplayMonitor: ObservableObject {
loadTriggerDisplays()
}

/// Begin monitoring. Snapshots the currently-online displays as the
/// no-edge baseline, then registers for reconfiguration callbacks and the
/// Begin monitoring. Populates the Settings rows right away, schedules a
/// reconcile against an empty present set (so trigger displays already
/// attached fire), then registers for reconfiguration callbacks and the
/// sleep/wake notifications the settle logic needs.
func start() {
guard !started else { return }
started = true

let online = Self.onlineExternalDisplays()
presentUUIDs = Set(online.map { $0.id })
connectedDisplays = online
refreshTriggerNames(from: online)
scheduleReconcile()

let refCon = Unmanaged.passUnretained(self).toOpaque()
let error = CGDisplayRegisterReconfigurationCallback(Self.reconfigurationCallback, refCon)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ Magic Switch tells you when there's a new version — it never updates itself. A
- **Closing or sleeping one Mac hands its peripherals to the other.** When this Mac sleeps (or you close its lid), it hands the peripherals it holds to your other Mac — or, if that Mac isn't reachable yet, frees them so it can pick them up the moment it wakes. That's why you can close one Mac and find the keyboard and mouse already on the other. This is on by default; you can turn it off under **Settings → Other → "Release peripherals when this Mac sleeps."**
- **A peripheral didn't come back after sleep or a lid-close.** Apple's Magic devices sometimes get stuck once the Bluetooth radio sleeps and won't reconnect — even a manual reconnect fails until you switch the peripheral **off and on** with its power switch. Magic Switch keeps watching for anything that was on this Mac before it slept: the moment the device reappears (which a power-cycle triggers), it reconnects automatically — as long as your other Mac isn't actively using it. This is on by default; you can turn it off under **Settings → Other → "Reconnect peripherals if they drop."**
- **Both Macs slept, and now a peripheral connects to neither.** When this Mac sleeps, it frees its peripherals for your other Mac to take (see above). If the other Mac never takes them — it was asleep too, or off the network the whole time — they end up attached to no Mac at all, and a Magic device in that state stops listening for connections after a short while; no Mac can reach it until it's woken up. When this Mac wakes it immediately tries to grab everything it freed, so a round trip usually recovers on its own — clicking or pressing a key on the peripheral within the first minute after opening the lid helps it get caught. If a peripheral still won't respond, this is the one situation where you have to help: flick its power switch **off and on**, and Magic Switch reconnects it the moment it comes back up. If that trade-off doesn't suit how you use your Macs, turn off **Settings → Other → "Release peripherals when this Mac sleeps"** — peripherals then stay bonded to this Mac across sleep (and reconnect instantly on wake), at the cost of the other Mac needing the same power-cycle help if you switch desks while this Mac sleeps.
- **Docking to a marked display switches peripherals automatically.** Under **Settings → Other → "Take peripherals when a display connects,"** every external display connected to this Mac gets a toggle. Mark your desk's display and, whenever it connects to this Mac — plugging in the cable, or opening the lid of a MacBook that was docked while it slept — Magic Switch switches your peripherals to this Mac, taking them from your other Mac if needed. Displays are recognized individually, so two identical monitors aren't confused; waking while already docked doesn't count as docking; and a display that's already connected when Magic Switch starts doesn't trigger a switch. This is off until you mark a display.
- **Docking to a marked display switches peripherals automatically.** Under **Settings → Other → "Take peripherals when a display connects,"** every external display connected to this Mac gets a toggle. Mark your desk's display and, whenever it connects to this Mac — plugging in the cable, or opening the lid of a MacBook that was docked while it slept — Magic Switch switches your peripherals to this Mac, taking them from your other Mac if needed. Displays are recognized individually, so two identical monitors aren't confused; waking while already docked doesn't count as docking; and if a marked display is already connected when Magic Switch starts, it switches your peripherals over just as if you had docked. This is off until you mark a display.

## Developer notes

Expand Down