Files
Termind/TFM/Platform/MetricsActivityObserver.swift

76 lines
2.4 KiB
Swift

// SPDX-FileCopyrightText: 2026 Kaede Akino
// SPDX-License-Identifier: GPL-3.0-only
import AppKit
import TermindApplication
/// System and login-session notifications must remain active after the last window closes.
/// A wake alone is insufficient: the Data Protection Keychain remains unavailable
/// until the user's login session becomes active again.
@MainActor
final class MetricsActivityObserver {
nonisolated enum Event: Sendable {
case systemWillSleep
case systemDidWake
case sessionDidResignActive
case sessionDidBecomeActive
}
private enum Blocker: Hashable {
case systemSleep
case inactiveSession
}
private var tokens: [any NSObjectProtocol] = []
private var blockers: Set<Blocker> = []
private weak var model: ServerMetricsModel?
func start(_ model: ServerMetricsModel) {
guard tokens.isEmpty else { return }
self.model = model
let notifications: [(Notification.Name, Event)] = [
(NSWorkspace.willSleepNotification, .systemWillSleep),
(NSWorkspace.didWakeNotification, .systemDidWake),
(NSWorkspace.sessionDidResignActiveNotification, .sessionDidResignActive),
(NSWorkspace.sessionDidBecomeActiveNotification, .sessionDidBecomeActive),
]
for (name, event) in notifications {
tokens.append(
NSWorkspace.shared.notificationCenter.addObserver(forName: name, object: nil, queue: .main) {
[weak self] _ in
Task { @MainActor in
self?.receive(event)
}
})
}
}
func receive(_ event: Event) {
let wasSuspended = !blockers.isEmpty
switch event {
case .systemWillSleep:
blockers.insert(.systemSleep)
case .systemDidWake:
blockers.remove(.systemSleep)
case .sessionDidResignActive:
blockers.insert(.inactiveSession)
case .sessionDidBecomeActive:
blockers.remove(.inactiveSession)
}
let isSuspended = !blockers.isEmpty
guard wasSuspended != isSuspended else { return }
if isSuspended {
model?.suspend()
} else {
model?.resume()
}
}
func stop() {
for token in tokens { NSWorkspace.shared.notificationCenter.removeObserver(token) }
tokens = []
blockers = []
model = nil
}
}