Merge pull request #345 from manaflow-ai/feat-theme-switch-frame-sync

Keep chrome/theme updates in lockstep on theme switch
This commit is contained in:
Lawrence Chen 2026-02-23 02:46:08 -08:00 committed by GitHub
commit 4fb5da373d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 163 additions and 25 deletions

View file

@ -183,7 +183,9 @@ final class GhosttyDefaultBackgroundNotificationDispatcher {
let source = pendingSource
pendingUserInfo = nil
logEvent?("bg notify flushed id=\(eventId) source=\(source)")
logEvent?("bg notify posting id=\(eventId) source=\(source)")
postNotification(userInfo)
logEvent?("bg notify posted id=\(eventId) source=\(source)")
}
}
@ -278,12 +280,17 @@ class GhosttyApp {
return UserDefaults.standard.bool(forKey: "GhosttyTabsDebugBG")
}()
private let backgroundLogURL = GhosttyApp.resolveBackgroundLogURL()
private let backgroundLogStartUptime = ProcessInfo.processInfo.systemUptime
private let backgroundLogLock = NSLock()
private var backgroundLogSequence: UInt64 = 0
private var appObservers: [NSObjectProtocol] = []
private var backgroundEventCounter: UInt64 = 0
private var defaultBackgroundUpdateScope: GhosttyDefaultBackgroundUpdateScope = .unscoped
private var defaultBackgroundScopeSource: String = "initialize"
private lazy var defaultBackgroundNotificationDispatcher: GhosttyDefaultBackgroundNotificationDispatcher =
GhosttyDefaultBackgroundNotificationDispatcher(logEvent: { [weak self] message in
// Theme chrome should track terminal theme changes in the same frame.
// Keep coalescing semantics, but flush in the next main turn instead of waiting ~1 frame.
GhosttyDefaultBackgroundNotificationDispatcher(delay: 0, logEvent: { [weak self] message in
guard let self, self.backgroundLogEnabled else { return }
self.logBackground(message)
})
@ -1166,8 +1173,10 @@ class GhosttyApp {
source: "action.config_change.surface tab=\(surfaceView.tabId?.uuidString ?? "nil") surface=\(surfaceView.terminalSurface?.id.uuidString ?? "nil")",
scope: .surface
)
DispatchQueue.main.async {
surfaceView.applyWindowBackgroundIfActive()
if backgroundLogEnabled {
logBackground(
"surface config change deferred terminal bg apply tab=\(surfaceView.tabId?.uuidString ?? "nil") surface=\(surfaceView.terminalSurface?.id.uuidString ?? "nil")"
)
}
return true
case GHOSTTY_ACTION_RELOAD_CONFIG:
@ -1273,7 +1282,16 @@ class GhosttyApp {
func logBackground(_ message: String) {
let timestamp = Self.backgroundLogTimestampFormatter.string(from: Date())
let line = "\(timestamp) cmux bg: \(message)\n"
let uptimeMs = (ProcessInfo.processInfo.systemUptime - backgroundLogStartUptime) * 1000
let frame60 = Int((CACurrentMediaTime() * 60.0).rounded(.down))
let frame120 = Int((CACurrentMediaTime() * 120.0).rounded(.down))
let threadLabel = Thread.isMainThread ? "main" : "background"
backgroundLogLock.lock()
defer { backgroundLogLock.unlock() }
backgroundLogSequence &+= 1
let sequence = backgroundLogSequence
let line =
"\(timestamp) seq=\(sequence) t+\(String(format: "%.3f", uptimeMs))ms thread=\(threadLabel) frame60=\(frame60) frame120=\(frame120) cmux bg: \(message)\n"
if let data = line.data(using: .utf8) {
if FileManager.default.fileExists(atPath: backgroundLogURL.path) == false {
FileManager.default.createFile(atPath: backgroundLogURL.path, contents: nil)
@ -1965,6 +1983,8 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
var onTriggerFlash: (() -> Void)?
var backgroundColor: NSColor?
private var appliedColorScheme: ghostty_color_scheme_e?
private var lastLoggedSurfaceBackgroundSignature: String?
private var lastLoggedWindowBackgroundSignature: String?
private var keySequence: [ghostty_input_trigger_s] = []
private var keyTables: [String] = []
#if DEBUG
@ -2025,6 +2045,15 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
CATransaction.commit()
}
terminalSurface?.hostedView.setBackgroundColor(color)
if GhosttyApp.shared.backgroundLogEnabled {
let signature = "\(color.hexString()):\(String(format: "%.3f", color.alphaComponent))"
if signature != lastLoggedSurfaceBackgroundSignature {
lastLoggedSurfaceBackgroundSignature = signature
GhosttyApp.shared.logBackground(
"surface background applied tab=\(tabId?.uuidString ?? "unknown") surface=\(terminalSurface?.id.uuidString ?? "unknown") color=\(color.hexString()) opacity=\(String(format: "%.3f", color.alphaComponent))"
)
}
}
}
func applyWindowBackgroundIfActive() {
@ -2042,7 +2071,13 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
window.isOpaque = color.alphaComponent >= 1.0
}
if GhosttyApp.shared.backgroundLogEnabled {
GhosttyApp.shared.logBackground("applied window background tab=\(tabId?.uuidString ?? "unknown") color=\(color) opacity=\(String(format: "%.3f", color.alphaComponent))")
let signature = "\(cmuxShouldUseTransparentBackgroundWindow() ? "transparent" : color.hexString()):\(String(format: "%.3f", color.alphaComponent))"
if signature != lastLoggedWindowBackgroundSignature {
lastLoggedWindowBackgroundSignature = signature
GhosttyApp.shared.logBackground(
"window background applied tab=\(tabId?.uuidString ?? "unknown") surface=\(terminalSurface?.id.uuidString ?? "unknown") transparent=\(cmuxShouldUseTransparentBackgroundWindow()) color=\(color.hexString()) opacity=\(String(format: "%.3f", color.alphaComponent))"
)
}
}
}