import AppKit import CoreGraphics struct LockScreenDisplayContext { let screen: NSScreen let frame: NSRect let identifier: String } @MainActor final class LockScreenDisplayContextProvider { static let shared = LockScreenDisplayContextProvider() private(set) var context: LockScreenDisplayContext? private var screenChangeObserver: NSObjectProtocol? private var workspaceObservers: [NSObjectProtocol] = [] private init() { refresh(reason: "init") registerObservers() } deinit { if let screenChangeObserver { NotificationCenter.default.removeObserver(screenChangeObserver) } let workspaceCenter = NSWorkspace.shared.notificationCenter workspaceObservers.forEach { workspaceCenter.removeObserver($0) } } @discardableResult func refresh(reason: String) -> LockScreenDisplayContext? { guard let screen = preferredLockScreen() else { context = nil return nil } let snapshot = LockScreenDisplayContext( screen: screen, frame: screen.frame, identifier: screen.localizedName ) context = snapshot return snapshot } func contextSnapshot() -> LockScreenDisplayContext? { if let context { return context } return refresh(reason: "snapshot-miss") } private func preferredLockScreen() -> NSScreen? { if let builtin = NSScreen.screens.first(where: { screen in guard let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else { return false } return CGDisplayIsBuiltin(CGDirectDisplayID(number.uint32Value)) != 0 }) { return builtin } let mainDisplayID = CGMainDisplayID() if let mainScreen = NSScreen.screens.first(where: { screen in guard let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else { return false } return CGDirectDisplayID(number.uint32Value) == mainDisplayID }) { return mainScreen } return NSScreen.main ?? NSScreen.screens.first } private func registerObservers() { screenChangeObserver = NotificationCenter.default.addObserver( forName: NSApplication.didChangeScreenParametersNotification, object: nil, queue: .main ) { [weak self] _ in self?.refresh(reason: "screen-parameters") } let workspaceCenter = NSWorkspace.shared.notificationCenter let wakeObserver = workspaceCenter.addObserver( forName: NSWorkspace.screensDidWakeNotification, object: nil, queue: .main ) { [weak self] _ in self?.refresh(reason: "screens-did-wake") } let spaceObserver = workspaceCenter.addObserver( forName: NSWorkspace.activeSpaceDidChangeNotification, object: nil, queue: .main ) { [weak self] _ in self?.refresh(reason: "space-changed") } workspaceObservers = [wakeObserver, spaceObserver] } }