Files
ghostty/macos/Sources/Helpers/Extensions/NSScreen+Extension.swift
Mitchell Hashimoto ea505ec51d macos: use stable display UUID for quick terminal screen tracking
NSScreen instances can be garbage collected at any time, even for
screens that remain connected, making NSMapTable with weak keys
unreliable for tracking per-screen state.

This changes the quick terminal to use CGDisplay UUIDs as stable
identifiers, keyed in a strong dictionary. Each entry stores the
window frame along with screen dimensions, scale factor, and last-seen
timestamp.

Rules for pruning:
- Entries are invalidated when screens shrink or change scale
- Entries persist and update when screens grow (allowing cached state
  to work with larger resolutions)
- Stale entries for disconnected screens expire after 14 days.
- Maximum of 10 screen entries to prevent unbounded growth
2025-10-17 21:04:23 -07:00

68 lines
3.0 KiB
Swift

import Cocoa
extension NSScreen {
/// The unique CoreGraphics display ID for this screen.
var displayID: UInt32? {
deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? UInt32
}
/// The stable UUID for this display, suitable for tracking across reconnects and NSScreen garbage collection.
var displayUUID: UUID? {
guard let displayID = displayID else { return nil }
guard let cfuuid = CGDisplayCreateUUIDFromDisplayID(displayID)?.takeRetainedValue() else { return nil }
return UUID(cfuuid)
}
// Returns true if the given screen has a visible dock. This isn't
// point-in-time visible, this is true if the dock is always visible
// AND present on this screen.
var hasDock: Bool {
// If the dock autohides then we don't have a dock ever.
if let dockAutohide = UserDefaults.standard.persistentDomain(forName: "com.apple.dock")?["autohide"] as? Bool {
if (dockAutohide) { return false }
}
// There is no public API to directly ask about dock visibility, so we have to figure it out
// by comparing the sizes of visibleFrame (the currently usable area of the screen) and
// frame (the full screen size). We also need to account for the menubar, any inset caused
// by the notch on macbooks, and a little extra padding to compensate for the boundary area
// which triggers showing the dock.
// If our visible width is less than the frame we assume its the dock.
if (visibleFrame.width < frame.width) {
return true
}
// We need to see if our visible frame height is less than the full
// screen height minus the menu and notch and such.
let menuHeight = NSApp.mainMenu?.menuBarHeight ?? 0
let notchInset: CGFloat = safeAreaInsets.top
let boundaryAreaPadding = 5.0
return visibleFrame.height < (frame.height - max(menuHeight, notchInset) - boundaryAreaPadding)
}
/// Returns true if the screen has a visible notch (i.e., a non-zero safe area inset at the top).
var hasNotch: Bool {
// We assume that a top safe area means notch, since we don't currently
// know any other situation this is true.
return safeAreaInsets.top > 0
}
/// Converts top-left offset coordinates to bottom-left origin coordinates for window positioning.
/// - Parameters:
/// - x: X offset from top-left corner
/// - y: Y offset from top-left corner
/// - windowSize: Size of the window to be positioned
/// - Returns: CGPoint suitable for setFrameOrigin that positions the window as requested
func origin(fromTopLeftOffsetX x: CGFloat, offsetY y: CGFloat, windowSize: CGSize) -> CGPoint {
let vf = visibleFrame
// Convert top-left coordinates to bottom-left origin
let originX = vf.minX + x
let originY = vf.maxY - y - windowSize.height
return CGPoint(x: originX, y: originY)
}
}