diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index 9056e692a..2d9822d6e 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -947,14 +947,7 @@ extension Ghostty { guard let surface = target.target.surface else { return } guard let surfaceView = self.surfaceView(from: surface) else { return } guard let title = String(cString: v.title!, encoding: .utf8) else { return } - - // We must set this in a dispatchqueue to avoid a deadlock on startup on some - // versions of macOS. I unfortunately didn't document the exact versions so - // I don't know when its safe to remove this. - DispatchQueue.main.async { - surfaceView.title = title - } - + surfaceView.setTitle(title) default: assertionFailure() @@ -1089,7 +1082,10 @@ extension Ghostty { guard let surface = target.target.surface else { return } guard let surfaceView = self.surfaceView(from: surface) else { return } let backingSize = NSSize(width: Double(v.width), height: Double(v.height)) - surfaceView.cellSize = surfaceView.convertFromBacking(backingSize) + DispatchQueue.main.async { [weak surfaceView] in + guard let surfaceView else { return } + surfaceView.cellSize = surfaceView.convertFromBacking(backingSize) + } default: assertionFailure() diff --git a/macos/Sources/Ghostty/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/SurfaceView_AppKit.swift index 7e861a229..d06ab36eb 100644 --- a/macos/Sources/Ghostty/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_AppKit.swift @@ -12,7 +12,7 @@ extension Ghostty { // The current title of the surface as defined by the pty. This can be // changed with escape codes. This is public because the callbacks go // to the app level and it is set from there. - @Published var title: String = "👻" + @Published private(set) var title: String = "👻" // The current pwd of the surface as defined by the pty. This can be // changed with escape codes. @@ -110,6 +110,9 @@ extension Ghostty { // This is set to non-null during keyDown to accumulate insertText contents private var keyTextAccumulator: [String]? = nil + // A small delay that is introduced before a title change to avoid flickers + private var titleChangeTimer: Timer? + // We need to support being a first responder so that we can get input events override var acceptsFirstResponder: Bool { return true } @@ -339,6 +342,20 @@ extension Ghostty { NSCursor.setHiddenUntilMouseMoves(!visible) } + func setTitle(_ title: String) { + // This fixes an issue where very quick changes to the title could + // cause an unpleasant flickering. We set a timer so that we can + // coalesce rapid changes. The timer is short enough that it still + // feels "instant". + titleChangeTimer?.invalidate() + titleChangeTimer = Timer.scheduledTimer( + withTimeInterval: 0.075, + repeats: false + ) { [weak self] _ in + self?.title = title + } + } + // MARK: - Notifications @objc private func onUpdateRendererHealth(notification: SwiftUI.Notification) {