BugFix: Display Lock and Unlock Lag

This commit is contained in:
Hariharan Mudaliar 2026-01-09 12:32:00 +05:30
commit bb183a701d
No known key found for this signature in database
GPG key ID: 025583EB0BE5640E
8 changed files with 132 additions and 18 deletions

View file

@ -0,0 +1,83 @@
import AppKit
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 = NSScreen.main ?? NSScreen.screens.first 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 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]
}
}

View file

@ -83,7 +83,7 @@ class LockScreenLiveActivityWindowManager {
} }
private func lockContext() -> (notchSize: CGSize, screen: NSScreen)? { private func lockContext() -> (notchSize: CGSize, screen: NSScreen)? {
guard let screen = NSScreen.main else { guard let screen = LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main else {
print("[\(timestamp())] LockScreenLiveActivityWindowManager: no main screen available") print("[\(timestamp())] LockScreenLiveActivityWindowManager: no main screen available")
return nil return nil
} }

View file

@ -87,6 +87,7 @@ class LockScreenManager: ObservableObject {
print("[\(timestamp())] LockScreenManager: 🔒 Screen LOCKED event received") print("[\(timestamp())] LockScreenManager: 🔒 Screen LOCKED event received")
Logger.log("LockScreenManager: Screen locked", category: .lifecycle) Logger.log("LockScreenManager: Screen locked", category: .lifecycle)
LockSoundPlayer.shared.playLockChime() LockSoundPlayer.shared.playLockChime()
LockScreenDisplayContextProvider.shared.refresh(reason: "screen-locked")
// Update state SYNCHRONOUSLY without Task/await to avoid any delay // Update state SYNCHRONOUSLY without Task/await to avoid any delay
lastUpdated = Date() lastUpdated = Date()
@ -134,6 +135,7 @@ class LockScreenManager: ObservableObject {
print("[\(timestamp())] LockScreenManager: 🔓 Screen UNLOCKED event received") print("[\(timestamp())] LockScreenManager: 🔓 Screen UNLOCKED event received")
Logger.log("LockScreenManager: Screen unlocked", category: .lifecycle) Logger.log("LockScreenManager: Screen unlocked", category: .lifecycle)
LockSoundPlayer.shared.playUnlockChime() LockSoundPlayer.shared.playUnlockChime()
LockScreenDisplayContextProvider.shared.refresh(reason: "screen-unlocked")
lastUpdated = Date() lastUpdated = Date()
updateIdleState(locked: false) updateIdleState(locked: false)
isLocked = false isLocked = false

View file

@ -74,7 +74,7 @@ class LockScreenPanelManager {
return return
} }
guard let screen = NSScreen.main else { guard let screen = currentScreen() else {
print("[\(timestamp())] LockScreenPanelManager: no main screen available") print("[\(timestamp())] LockScreenPanelManager: no main screen available")
return return
} }
@ -188,7 +188,7 @@ class LockScreenPanelManager {
} }
func applyOffsetAdjustment(animated: Bool = true) { func applyOffsetAdjustment(animated: Bool = true) {
guard let screen = NSScreen.main else { return } guard let screen = currentScreen() else { return }
let screenFrame = screen.frame let screenFrame = screen.frame
let newCollapsed = collapsedFrame(for: screenFrame) let newCollapsed = collapsedFrame(for: screenFrame)
collapsedFrame = newCollapsed collapsedFrame = newCollapsed
@ -225,7 +225,7 @@ class LockScreenPanelManager {
private func handleScreenGeometryChange(reason: String) { private func handleScreenGeometryChange(reason: String) {
guard let window = panelWindow else { return } guard let window = panelWindow else { return }
guard window.isVisible || panelAnimator.isPresented else { return } guard window.isVisible || panelAnimator.isPresented else { return }
guard let screen = NSScreen.main else { return } guard let screen = currentScreen() else { return }
let screenFrame = screen.frame let screenFrame = screen.frame
collapsedFrame = collapsedFrame(for: screenFrame) collapsedFrame = collapsedFrame(for: screenFrame)
@ -245,4 +245,8 @@ class LockScreenPanelManager {
let originY = baseOriginY + defaultLowering + clampedOffset let originY = baseOriginY + defaultLowering + clampedOffset
return NSRect(x: originX, y: originY, width: collapsedSize.width, height: collapsedSize.height) return NSRect(x: originX, y: originY, width: collapsedSize.width, height: collapsedSize.height)
} }
private func currentScreen() -> NSScreen? {
LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main
}
} }

View file

@ -35,7 +35,7 @@ final class LockScreenReminderWidgetPanelManager {
} }
func refreshPosition(animated: Bool) { func refreshPosition(animated: Bool) {
guard let window, window.isVisible, let screen = NSScreen.main else { return } guard let window, window.isVisible, let screen = currentScreen() else { return }
let target = frame(for: window.frame.size, on: screen) let target = frame(for: window.frame.size, on: screen)
if animated { if animated {
NSAnimationContext.runAnimationGroup { context in NSAnimationContext.runAnimationGroup { context in
@ -49,7 +49,7 @@ final class LockScreenReminderWidgetPanelManager {
} }
private func render(snapshot: LockScreenReminderWidgetSnapshot, makeVisible: Bool) { private func render(snapshot: LockScreenReminderWidgetSnapshot, makeVisible: Bool) {
guard let screen = NSScreen.main else { return } guard let screen = currentScreen() else { return }
if !makeVisible, window == nil { if !makeVisible, window == nil {
return return
} }
@ -161,5 +161,9 @@ final class LockScreenReminderWidgetPanelManager {
refreshPosition(animated: false) refreshPosition(animated: false)
print("LockScreenReminderWidgetPanelManager: realigned window due to \(reason)") print("LockScreenReminderWidgetPanelManager: realigned window due to \(reason)")
} }
private func currentScreen() -> NSScreen? {
LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main
}
} }

View file

@ -110,7 +110,7 @@ final class LockScreenTimerWidgetPanelManager {
} }
func showWidget() { func showWidget() {
guard let screen = NSScreen.main else { return } guard let screen = currentScreen() else { return }
let window = ensureWindow() let window = ensureWindow()
let frame = targetFrame(on: screen) let frame = targetFrame(on: screen)
window.setFrame(frame, display: true) window.setFrame(frame, display: true)
@ -150,7 +150,7 @@ final class LockScreenTimerWidgetPanelManager {
func refreshPosition(animated: Bool) { func refreshPosition(animated: Bool) {
guard let window, window.isVisible, let screen = NSScreen.main else { return } guard let window, window.isVisible, let screen = currentScreen() else { return }
let frame = targetFrame(on: screen) let frame = targetFrame(on: screen)
if animated { if animated {
NSAnimationContext.runAnimationGroup { context in NSAnimationContext.runAnimationGroup { context in
@ -279,4 +279,8 @@ final class LockScreenTimerWidgetPanelManager {
refreshPosition(animated: false) refreshPosition(animated: false)
print("LockScreenTimerWidgetPanelManager: realigned window due to \(reason)") print("LockScreenTimerWidgetPanelManager: realigned window due to \(reason)")
} }
private func currentScreen() -> NSScreen? {
LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main
}
} }

View file

@ -35,19 +35,32 @@ final class LockScreenWeatherManager: ObservableObject {
} }
locationProvider.prepareAuthorization() locationProvider.prepareAuthorization()
let existingSnapshot = snapshot
let hadSnapshot = existingSnapshot != nil
var cachedSnapshot = existingSnapshot
if cachedSnapshot == nil, let payload = latestWeatherPayload {
let derivedSnapshot = makeSnapshot(from: payload)
snapshot = derivedSnapshot
cachedSnapshot = derivedSnapshot
}
if let cachedSnapshot {
deliver(cachedSnapshot, forceShow: true)
}
let needsForceShowOnRefresh = cachedSnapshot == nil
let shouldForceRefresh = !hadSnapshot
Task { @MainActor [weak self] in Task { @MainActor [weak self] in
guard let self else { return } guard let self else { return }
let snapshot = await self.refresh(force: self.snapshot == nil) let refreshedSnapshot = await self.refresh(force: shouldForceRefresh)
guard LockScreenManager.shared.currentLockStatus else { guard LockScreenManager.shared.currentLockStatus else { return }
LockScreenWeatherPanelManager.shared.hide()
return
}
if let snapshot { if let refreshedSnapshot {
self.deliver(snapshot, forceShow: true) self.deliver(refreshedSnapshot, forceShow: needsForceShowOnRefresh)
} else { } else if needsForceShowOnRefresh {
LockScreenWeatherPanelManager.shared.hide() LockScreenWeatherPanelManager.shared.hide()
} }
} }

View file

@ -38,7 +38,7 @@ final class LockScreenWeatherPanelManager {
} }
private func render(snapshot: LockScreenWeatherSnapshot, makeVisible: Bool) { private func render(snapshot: LockScreenWeatherSnapshot, makeVisible: Bool) {
guard let screen = NSScreen.main else { return } guard let screen = currentScreen() else { return }
if !makeVisible, window == nil { if !makeVisible, window == nil {
return return
} }
@ -111,7 +111,7 @@ final class LockScreenWeatherPanelManager {
func refreshPositionForOffsets(animated: Bool = true) { func refreshPositionForOffsets(animated: Bool = true) {
guard let window, let snapshot = lastSnapshot else { return } guard let window, let snapshot = lastSnapshot else { return }
guard let screen = NSScreen.main else { return } guard let screen = currentScreen() else { return }
let size = lastContentSize ?? window.frame.size let size = lastContentSize ?? window.frame.size
let targetFrame = frame(for: size, snapshot: snapshot, on: screen) let targetFrame = frame(for: size, snapshot: snapshot, on: screen)
latestFrame = targetFrame latestFrame = targetFrame
@ -153,4 +153,8 @@ final class LockScreenWeatherPanelManager {
refreshPositionForOffsets(animated: false) refreshPositionForOffsets(animated: false)
print("LockScreenWeatherPanelManager: realigned window due to \(reason)") print("LockScreenWeatherPanelManager: realigned window due to \(reason)")
} }
private func currentScreen() -> NSScreen? {
LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main
}
} }