diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index 2ee395f0..5ff4359a 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -2677,6 +2677,15 @@ final class TerminalSurface: Identifiable, ObservableObject { private(set) var surface: ghostty_surface_t? private weak var attachedView: GhosttyNSView? + + /// Whether the runtime Ghostty surface exists and has not begun teardown. + /// + /// Use this before passing `surface` to Ghostty C APIs that dereference the + /// pointer (e.g. `ghostty_surface_inherited_config`, `ghostty_surface_quicklook_font`). + /// A non-nil `surface` alone is not sufficient — the underlying native state may + /// already be closing or closed. + var hasLiveSurface: Bool { surface != nil && portalLifecycleState == .live } + /// Whether the terminal surface view is currently attached to a window. /// /// Use the hosted view rather than the inner surface view, since the surface can be diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 3734b412..ed36d8fb 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -26,6 +26,13 @@ func cmuxCurrentSurfaceFontSizePoints(_ surface: ghostty_surface_t) -> Float? { return nil } + // Validate the font pointer is a live heap allocation before interpreting + // it as a CTFont. ghostty_surface_quicklook_font returns an unretained + // pointer that can become stale on Intel Macs (#1496, #1870). + guard malloc_size(quicklookFont) > 0 else { + return nil + } + let ctFont = Unmanaged.fromOpaque(quicklookFont).takeUnretainedValue() let points = Float(CTFontGetSize(ctFont)) guard points > 0 else { return nil } @@ -6797,7 +6804,8 @@ final class Workspace: Identifiable, ObservableObject { private func rememberTerminalConfigInheritanceSource(_ terminalPanel: TerminalPanel) { lastTerminalConfigInheritancePanelId = terminalPanel.id - if let sourceSurface = terminalPanel.surface.surface, + if terminalPanel.surface.hasLiveSurface, + let sourceSurface = terminalPanel.surface.surface, let runtimePoints = cmuxCurrentSurfaceFontSizePoints(sourceSurface) { let existing = terminalInheritanceFontPointsByPanelId[terminalPanel.id] if existing == nil || abs((existing ?? runtimePoints) - runtimePoints) > 0.05 { @@ -6895,7 +6903,8 @@ final class Workspace: Identifiable, ObservableObject { preferredPanelId: preferredPanelId, inPane: preferredPaneId ) { - guard let sourceSurface = terminalPanel.surface.surface else { continue } + guard terminalPanel.surface.hasLiveSurface, + let sourceSurface = terminalPanel.surface.surface else { continue } var config = cmuxInheritedSurfaceConfig( sourceSurface: sourceSurface, context: GHOSTTY_SURFACE_CONTEXT_SPLIT