Skip to content

Commit

Permalink
fix: quick terminal CPU spikes (#4055)
Browse files Browse the repository at this point in the history
# Description

The following code is causing an infinite loop that causes a CPU spikes
until the quick terminal is displayed:

https://github.com/ghostty-org/ghostty/blob/87bd0bb744d6a1c45022aa39f21219d0b6ff3261/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift#L314-L317

## Reproduce steps
1. Open Ghostty.
2. Open the Quick Terminal.
3. Close the Quick Terminal.
4. Reload the configuration (Ghostty > Reload Configuration or
`shift+cmd+,`).
5. Observe CPU spike.

## Fix
Now, `syncAppearance` doesn't postpone the process until it can be
consumed, and the appearance is synchronized once the animation is done
and the quick terminal is visible.

Fixes #3998
  • Loading branch information
mitchellh authored Dec 30, 2024
2 parents da186fb + 011c17d commit ff50b55
Showing 1 changed file with 18 additions and 14 deletions.
32 changes: 18 additions & 14 deletions macos/Sources/Features/QuickTerminal/QuickTerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class QuickTerminalController: BaseTerminalController {
window.isRestorable = false

// Setup our configured appearance that we support.
syncAppearance(ghostty.config)
syncAppearance()

// Setup our initial size based on our configured position
position.setLoaded(window)
Expand Down Expand Up @@ -214,6 +214,10 @@ class QuickTerminalController: BaseTerminalController {
// If we canceled our animation in we do nothing
guard self.visible else { return }

// Now that the window is visible, sync our appearance. This function
// requires the window is visible.
self.syncAppearance()

// Once our animation is done, we must grab focus since we can't grab
// focus of a non-visible window.
self.makeWindowKey(window)
Expand Down Expand Up @@ -304,24 +308,18 @@ class QuickTerminalController: BaseTerminalController {
})
}

private func syncAppearance(_ config: Ghostty.Config) {
private func syncAppearance() {
guard let window else { return }

// If our window is not visible, then delay this. This is possible specifically
// during state restoration but probably in other scenarios as well. To delay,
// we just loop directly on the dispatch queue. We have to delay because some
// APIs such as window blur have no effect unless the window is visible.
guard window.isVisible else {
// Weak window so that if the window changes or is destroyed we aren't holding a ref
DispatchQueue.main.async { [weak self] in self?.syncAppearance(config) }
return
}
// If our window is not visible, then no need to sync the appearance yet.
// Some APIs such as window blur have no effect unless the window is visible.
guard window.isVisible else { return }

// Terminals typically operate in sRGB color space and macOS defaults
// to "native" which is typically P3. There is a lot more resources
// covered in this GitHub issue: https://github.com/mitchellh/ghostty/pull/376
// Ghostty defaults to sRGB but this can be overridden.
switch (config.windowColorspace) {
switch (self.derivedConfig.windowColorspace) {
case "display-p3":
window.colorSpace = .displayP3
case "srgb":
Expand All @@ -331,7 +329,7 @@ class QuickTerminalController: BaseTerminalController {
}

// If we have window transparency then set it transparent. Otherwise set it opaque.
if (config.backgroundOpacity < 1) {
if (self.derivedConfig.backgroundOpacity < 1) {
window.isOpaque = false

// This is weird, but we don't use ".clear" because this creates a look that
Expand Down Expand Up @@ -391,24 +389,30 @@ class QuickTerminalController: BaseTerminalController {
// Update our derived config
self.derivedConfig = DerivedConfig(config)

syncAppearance(config)
syncAppearance()
}

private struct DerivedConfig {
let quickTerminalScreen: QuickTerminalScreen
let quickTerminalAnimationDuration: Double
let quickTerminalAutoHide: Bool
let windowColorspace: String
let backgroundOpacity: Double

init() {
self.quickTerminalScreen = .main
self.quickTerminalAnimationDuration = 0.2
self.quickTerminalAutoHide = true
self.windowColorspace = ""
self.backgroundOpacity = 1.0
}

init(_ config: Ghostty.Config) {
self.quickTerminalScreen = config.quickTerminalScreen
self.quickTerminalAnimationDuration = config.quickTerminalAnimationDuration
self.quickTerminalAutoHide = config.quickTerminalAutoHide
self.windowColorspace = config.windowColorspace
self.backgroundOpacity = config.backgroundOpacity
}
}
}
Expand Down

0 comments on commit ff50b55

Please sign in to comment.