Atoll: clear lock screen widgets faster on unlock

- Add session-active and CGSession poll fallbacks for unlock detection
- Force weather panel alpha to 0 before orderOut for instant hide

Signed-off-by: Jis G Jacob <studiokeys@blissroms.org>
This commit is contained in:
Jis G Jacob 2026-05-18 09:51:50 +05:30
commit 3829627dbd
2 changed files with 61 additions and 4 deletions

View file

@ -48,6 +48,7 @@ class LockScreenManager: ObservableObject {
private var debounceIdleTask: Task<Void, Never>? private var debounceIdleTask: Task<Void, Never>?
private var collapseTask: Task<Void, Never>? private var collapseTask: Task<Void, Never>?
private var postUnlockMusicHUDTask: Task<Void, Never>? private var postUnlockMusicHUDTask: Task<Void, Never>?
private var lockStatePollTask: Task<Void, Never>?
// MARK: - Helpers // MARK: - Helpers
@ -65,9 +66,11 @@ class LockScreenManager: ObservableObject {
deinit { deinit {
DistributedNotificationCenter.default().removeObserver(self) DistributedNotificationCenter.default().removeObserver(self)
NSWorkspace.shared.notificationCenter.removeObserver(self)
debounceIdleTask?.cancel() debounceIdleTask?.cancel()
collapseTask?.cancel() collapseTask?.cancel()
postUnlockMusicHUDTask?.cancel() postUnlockMusicHUDTask?.cancel()
lockStatePollTask?.cancel()
} }
// MARK: - Setup // MARK: - Setup
@ -89,6 +92,17 @@ class LockScreenManager: ObservableObject {
object: nil object: nil
) )
// Fallback: macOS sometimes delays `com.apple.screenIsUnlocked`, leaving
// lock-screen widgets visible after the user-perceived unlock.
// The workspace session-active notification typically fires earlier; the
// guard at the top of `screenUnlocked` makes the call idempotent.
NSWorkspace.shared.notificationCenter.addObserver(
self,
selector: #selector(screenUnlocked),
name: NSWorkspace.sessionDidBecomeActiveNotification,
object: nil
)
print("LockScreenManager: ✅ Observers registered for lock/unlock events") print("LockScreenManager: ✅ Observers registered for lock/unlock events")
} }
@ -141,6 +155,8 @@ class LockScreenManager: ObservableObject {
print("[\(timestamp())] LockScreenManager: ⏭️ Lock icon disabled in settings") print("[\(timestamp())] LockScreenManager: ⏭️ Lock icon disabled in settings")
} }
startLockStatePolling()
print("[\(timestamp())] LockScreenManager: ✅ Lock screen activated") print("[\(timestamp())] LockScreenManager: ✅ Lock screen activated")
} }
@ -156,6 +172,7 @@ class LockScreenManager: ObservableObject {
lastUpdated = Date() lastUpdated = Date()
updateIdleState(locked: false) updateIdleState(locked: false)
isLocked = false isLocked = false
stopLockStatePolling()
postUnlockMusicHUDTask?.cancel() postUnlockMusicHUDTask?.cancel()
shouldDelayPostUnlockMusicHUD = Defaults[.enableLockScreenLiveActivity] shouldDelayPostUnlockMusicHUD = Defaults[.enableLockScreenLiveActivity]
@ -207,6 +224,44 @@ class LockScreenManager: ObservableObject {
print("[\(self.timestamp())] LockScreenManager: ✅ Lock screen deactivated") print("[\(self.timestamp())] LockScreenManager: ✅ Lock screen deactivated")
} }
// MARK: - Lock State Polling
// Defensive fallback against late/missed `com.apple.screenIsUnlocked` and
// `NSWorkspace.sessionDidBecomeActiveNotification` notifications, which
// macOS sometimes delivers well after the user-perceived unlock — leaving
// lock-screen widgets visible for an extra moment. While we believe we are
// locked, poll the canonical session-lock state and fire `screenUnlocked()`
// the moment the OS flips. The handler's duplicate-event guard makes this
// safe to call alongside any later-arriving notification.
private static func isSessionScreenLocked() -> Bool {
guard let session = CGSessionCopyCurrentDictionary() as? [String: Any] else {
return false
}
return session["CGSSessionScreenIsLocked"] as? Bool ?? false
}
private func startLockStatePolling() {
lockStatePollTask?.cancel()
lockStatePollTask = Task { [weak self] in
while !Task.isCancelled {
try? await Task.sleep(for: .milliseconds(500))
if Task.isCancelled { return }
await MainActor.run {
guard let self, self.isLocked else { return }
if !Self.isSessionScreenLocked() {
print("[\(self.timestamp())] LockScreenManager: 🔓 Polling detected unlock ahead of notification")
self.screenUnlocked()
}
}
}
}
}
private func stopLockStatePolling() {
lockStatePollTask?.cancel()
lockStatePollTask = nil
}
// MARK: - Idle State Management // MARK: - Idle State Management
/// Copy EXACT logic from ScreenRecordingManager /// Copy EXACT logic from ScreenRecordingManager

View file

@ -49,8 +49,10 @@ final class LockScreenWeatherPanelManager {
func hide() { func hide() {
guard let window else { return } guard let window else { return }
window.alphaValue = 0
window.orderOut(nil) window.orderOut(nil)
window.contentView = nil window.contentView = nil
window.alphaValue = 1
latestFrame = nil latestFrame = nil
lastSnapshot = nil lastSnapshot = nil
lastContentSize = nil lastContentSize = nil