Something went wrong. Try again.
Something went wrong. Try again.
4.5 kB · 116 lines
Swift
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117import Foundationimport Cocoa
/// Manages cached window state per screen for the quick terminal.////// This cache tracks the last closed window frame for each screen, allowing the quick terminal/// to restore to its previous size and position when reopened. It uses stable display UUIDs/// to survive NSScreen garbage collection and automatically prunes stale entries.class QuickTerminalScreenStateCache { typealias Entries = [UUID: DisplayEntry]
/// The maximum number of saved screen states we retain. This is to avoid some kind of /// pathological memory growth in case we get our screen state serializing wrong. I don't /// know anyone with more than 10 screens, so let's just arbitrarily go with that. private static let maxSavedScreens = 10
/// Time-to-live for screen entries that are no longer present (14 days). private static let screenStaleTTL: TimeInterval = 14 * 24 * 60 * 60
/// Keyed by display UUID to survive NSScreen garbage collection. private(set) var stateByDisplay: Entries = [:]
init(stateByDisplay: Entries = [:]) { self.stateByDisplay = stateByDisplay NotificationCenter.default.addObserver( self, selector: #selector(onScreensChanged(_:)), name: NSApplication.didChangeScreenParametersNotification, object: nil) }
deinit { NotificationCenter.default.removeObserver(self) }
/// Save the window frame for a screen. func save(frame: NSRect, for screen: NSScreen) { guard let key = screen.displayUUID else { return } let entry = DisplayEntry( frame: frame, screenSize: screen.frame.size, scale: screen.backingScaleFactor, lastSeen: Date() ) stateByDisplay[key] = entry pruneCapacity() }
/// Retrieve the last closed frame for a screen, if valid. func frame(for screen: NSScreen) -> NSRect? { guard let key = screen.displayUUID, var entry = stateByDisplay[key] else { return nil }
// Drop on dimension/scale change that makes the entry invalid if !entry.isValid(for: screen) { stateByDisplay.removeValue(forKey: key) return nil }
entry.lastSeen = Date() stateByDisplay[key] = entry return entry.frame }
@objc private func onScreensChanged(_ note: Notification) { let screens = NSScreen.screens let now = Date() let currentIDs = Set(screens.compactMap { $0.displayUUID })
for screen in screens { guard let key = screen.displayUUID else { continue } if var entry = stateByDisplay[key] { // Drop on dimension/scale change that makes the entry invalid if !entry.isValid(for: screen) { stateByDisplay.removeValue(forKey: key) } else { // Update the screen size if it grew (keep entry valid for larger screens) entry.screenSize = screen.frame.size entry.lastSeen = now stateByDisplay[key] = entry } } }
// TTL prune for non-present screens stateByDisplay = stateByDisplay.filter { key, entry in currentIDs.contains(key) || now.timeIntervalSince(entry.lastSeen) < Self.screenStaleTTL }
pruneCapacity() }
private func pruneCapacity() { guard stateByDisplay.count > Self.maxSavedScreens else { return } let toRemove = stateByDisplay .sorted { $0.value.lastSeen < $1.value.lastSeen } .prefix(stateByDisplay.count - Self.maxSavedScreens) for (key, _) in toRemove { stateByDisplay.removeValue(forKey: key) } }
struct DisplayEntry: Codable { var frame: NSRect var screenSize: CGSize var scale: CGFloat var lastSeen: Date
/// Returns true if this entry is still valid for the given screen. /// Valid if the scale matches and the cached size is not larger than the current screen size. /// This allows entries to persist when screens grow, but invalidates them when screens shrink. func isValid(for screen: NSScreen) -> Bool { guard scale == screen.backingScaleFactor else { return false } return screenSize.width <= screen.frame.size.width && screenSize.height <= screen.frame.size.height } }}