mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
Merge pull request #2552 from ghostty-org/push-ryrokukruoxr
macos: setup colorspace in base terminal controller
This commit is contained in:
@ -496,11 +496,6 @@ class AppDelegate: NSObject,
|
||||
// AppKit mutex on the appearance.
|
||||
DispatchQueue.main.async { self.syncAppearance() }
|
||||
|
||||
// Update all of our windows
|
||||
terminalManager.windows.forEach { window in
|
||||
window.controller.configDidReload()
|
||||
}
|
||||
|
||||
// If we have configuration errors, we need to show them.
|
||||
let c = ConfigurationErrorsController.sharedInstance
|
||||
c.errors = state.config.errors
|
||||
|
@ -33,11 +33,6 @@ class QuickTerminalController: BaseTerminalController {
|
||||
selector: #selector(onToggleFullscreen),
|
||||
name: Ghostty.Notification.ghosttyToggleFullscreen,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(ghosttyDidReloadConfig),
|
||||
name: Ghostty.Notification.ghosttyDidReloadConfig,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@ -53,6 +48,8 @@ class QuickTerminalController: BaseTerminalController {
|
||||
// MARK: NSWindowController
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
|
||||
guard let window = self.window else { return }
|
||||
|
||||
// The controller is the window delegate so we can detect events such as
|
||||
@ -63,9 +60,6 @@ class QuickTerminalController: BaseTerminalController {
|
||||
// make this restorable, but it isn't currently implemented.
|
||||
window.isRestorable = false
|
||||
|
||||
// Setup our configured appearance that we support.
|
||||
syncAppearance()
|
||||
|
||||
// Setup our initial size based on our configured position
|
||||
position.setLoaded(window)
|
||||
|
||||
@ -297,35 +291,6 @@ class QuickTerminalController: BaseTerminalController {
|
||||
})
|
||||
}
|
||||
|
||||
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() }
|
||||
return
|
||||
}
|
||||
|
||||
// If we have window transparency then set it transparent. Otherwise set it opaque.
|
||||
if (ghostty.config.backgroundOpacity < 1) {
|
||||
window.isOpaque = false
|
||||
|
||||
// This is weird, but we don't use ".clear" because this creates a look that
|
||||
// matches Terminal.app much more closer. This lets users transition from
|
||||
// Terminal.app more easily.
|
||||
window.backgroundColor = .white.withAlphaComponent(0.001)
|
||||
|
||||
ghostty_set_window_background_blur(ghostty.app, Unmanaged.passUnretained(window).toOpaque())
|
||||
} else {
|
||||
window.isOpaque = true
|
||||
window.backgroundColor = .windowBackgroundColor
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: First Responder
|
||||
|
||||
@IBAction override func closeWindow(_ sender: Any) {
|
||||
@ -357,10 +322,6 @@ class QuickTerminalController: BaseTerminalController {
|
||||
// We ignore the requested mode and always use non-native for the quick terminal
|
||||
toggleFullscreen(mode: .nonNative)
|
||||
}
|
||||
|
||||
@objc private func ghosttyDidReloadConfig(notification: SwiftUI.Notification) {
|
||||
syncAppearance()
|
||||
}
|
||||
}
|
||||
|
||||
extension Notification.Name {
|
||||
|
@ -93,6 +93,11 @@ class BaseTerminalController: NSWindowController,
|
||||
selector: #selector(didChangeScreenParametersNotification),
|
||||
name: NSApplication.didChangeScreenParametersNotification,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(ghosttyDidReloadConfigNotification),
|
||||
name: Ghostty.Notification.ghosttyDidReloadConfig,
|
||||
object: nil)
|
||||
|
||||
// Listen for local events that we need to know of outside of
|
||||
// single surface handlers.
|
||||
@ -123,7 +128,7 @@ class BaseTerminalController: NSWindowController,
|
||||
|
||||
/// Update all surfaces with the focus state. This ensures that libghostty has an accurate view about
|
||||
/// what surface is focused. This must be called whenever a surface OR window changes focus.
|
||||
func syncFocusToSurfaceTree() {
|
||||
private func syncFocusToSurfaceTree() {
|
||||
guard let tree = self.surfaceTree else { return }
|
||||
|
||||
for leaf in tree {
|
||||
@ -136,6 +141,48 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
}
|
||||
|
||||
// Call this whenever we want to setup our appearance parameters based on
|
||||
// configuration changes.
|
||||
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() }
|
||||
return
|
||||
}
|
||||
|
||||
// If we have window transparency then set it transparent. Otherwise set it opaque.
|
||||
if (ghostty.config.backgroundOpacity < 1) {
|
||||
window.isOpaque = false
|
||||
|
||||
// This is weird, but we don't use ".clear" because this creates a look that
|
||||
// matches Terminal.app much more closer. This lets users transition from
|
||||
// Terminal.app more easily.
|
||||
window.backgroundColor = .white.withAlphaComponent(0.001)
|
||||
|
||||
ghostty_set_window_background_blur(ghostty.app, Unmanaged.passUnretained(window).toOpaque())
|
||||
} else {
|
||||
window.isOpaque = true
|
||||
window.backgroundColor = .windowBackgroundColor
|
||||
}
|
||||
|
||||
// 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 (ghostty.config.windowColorspace) {
|
||||
case .displayP3:
|
||||
window.colorSpace = .displayP3
|
||||
case .srgb:
|
||||
window.colorSpace = .sRGB
|
||||
}
|
||||
}
|
||||
|
||||
// Call this whenever the frame changes
|
||||
private func windowFrameDidChange() {
|
||||
// We need to update our saved frame information in case of monitor
|
||||
@ -145,6 +192,14 @@ class BaseTerminalController: NSWindowController,
|
||||
savedFrame = .init(window: window.frame, screen: screen.visibleFrame)
|
||||
}
|
||||
|
||||
// MARK: Overridable Callbacks
|
||||
|
||||
/// Called whenever Ghostty reloads the configuration. Callers should call super.
|
||||
open func ghosttyDidReloadConfig() {
|
||||
// Whenever the config changes we setup our appearance.
|
||||
syncAppearance()
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc private func didChangeScreenParametersNotification(_ notification: Notification) {
|
||||
@ -191,6 +246,10 @@ class BaseTerminalController: NSWindowController,
|
||||
window.setFrame(newFrame, display: true)
|
||||
}
|
||||
|
||||
@objc private func ghosttyDidReloadConfigNotification(notification: SwiftUI.Notification) {
|
||||
ghosttyDidReloadConfig()
|
||||
}
|
||||
|
||||
// MARK: Local Events
|
||||
|
||||
private func localEventHandler(_ event: NSEvent) -> NSEvent? {
|
||||
@ -381,7 +440,16 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - NSWindowDelegate
|
||||
// MARK: NSWindowController
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
|
||||
// Setup our configured appearance that we support.
|
||||
syncAppearance()
|
||||
}
|
||||
|
||||
// MARK: NSWindowDelegate
|
||||
|
||||
// This is called when performClose is called on a window (NOT when close()
|
||||
// is called directly). performClose is called primarily when UI elements such
|
||||
|
@ -78,14 +78,16 @@ class TerminalController: BaseTerminalController {
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Methods
|
||||
override func ghosttyDidReloadConfig() {
|
||||
super.ghosttyDidReloadConfig()
|
||||
|
||||
func configDidReload() {
|
||||
guard let window = window as? TerminalWindow else { return }
|
||||
window.focusFollowsMouse = ghostty.config.focusFollowsMouse
|
||||
syncAppearance()
|
||||
}
|
||||
|
||||
//MARK: - Methods
|
||||
|
||||
/// Update the accessory view of each tab according to the keyboard
|
||||
/// shortcut that activates it (if any). This is called when the key window
|
||||
/// changes, when a window is closed, and when tabs are reordered
|
||||
@ -164,21 +166,6 @@ class TerminalController: BaseTerminalController {
|
||||
window.titlebarFont = nil
|
||||
}
|
||||
|
||||
// If we have window transparency then set it transparent. Otherwise set it opaque.
|
||||
if (ghostty.config.backgroundOpacity < 1) {
|
||||
window.isOpaque = false
|
||||
|
||||
// This is weird, but we don't use ".clear" because this creates a look that
|
||||
// matches Terminal.app much more closer. This lets users transition from
|
||||
// Terminal.app more easily.
|
||||
window.backgroundColor = .white.withAlphaComponent(0.001)
|
||||
|
||||
ghostty_set_window_background_blur(ghostty.app, Unmanaged.passUnretained(window).toOpaque())
|
||||
} else {
|
||||
window.isOpaque = true
|
||||
window.backgroundColor = .windowBackgroundColor
|
||||
}
|
||||
|
||||
window.hasShadow = ghostty.config.macosWindowShadow
|
||||
|
||||
guard window.hasStyledTabs else { return }
|
||||
@ -208,31 +195,20 @@ class TerminalController: BaseTerminalController {
|
||||
}
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
|
||||
guard let window = window as? TerminalWindow else { return }
|
||||
|
||||
|
||||
// Setting all three of these is required for restoration to work.
|
||||
window.isRestorable = restorable
|
||||
if (restorable) {
|
||||
window.restorationClass = TerminalWindowRestoration.self
|
||||
window.identifier = .init(String(describing: TerminalWindowRestoration.self))
|
||||
}
|
||||
|
||||
|
||||
// If window decorations are disabled, remove our title
|
||||
if (!ghostty.config.windowDecorations) { window.styleMask.remove(.titled) }
|
||||
|
||||
// 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 (ghostty.config.windowColorspace) {
|
||||
case "display-p3":
|
||||
window.colorSpace = .displayP3
|
||||
case "srgb":
|
||||
fallthrough
|
||||
default:
|
||||
window.colorSpace = .sRGB
|
||||
}
|
||||
|
||||
|
||||
// If we have only a single surface (no splits) and that surface requested
|
||||
// an initial size then we set it here now.
|
||||
if case let .leaf(leaf) = surfaceTree {
|
||||
@ -245,21 +221,21 @@ class TerminalController: BaseTerminalController {
|
||||
frame.size.height -= leaf.surface.frame.size.height
|
||||
frame.size.width += min(initialSize.width, screen.frame.width)
|
||||
frame.size.height += min(initialSize.height, screen.frame.height)
|
||||
|
||||
|
||||
// We have no tabs and we are not a split, so set the initial size of the window.
|
||||
window.setFrame(frame, display: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Center the window to start, we'll move the window frame automatically
|
||||
// when cascading.
|
||||
window.center()
|
||||
|
||||
|
||||
// Make sure our theme is set on the window so styling is correct.
|
||||
if let windowTheme = ghostty.config.windowTheme {
|
||||
window.windowTheme = .init(rawValue: windowTheme)
|
||||
}
|
||||
|
||||
|
||||
// Handle titlebar tabs config option. Something about what we do while setting up the
|
||||
// titlebar tabs interferes with the window restore process unless window.tabbingMode
|
||||
// is set to .preferred, so we set it, and switch back to automatic as soon as we can.
|
||||
@ -272,50 +248,50 @@ class TerminalController: BaseTerminalController {
|
||||
} else if (ghostty.config.macosTitlebarStyle == "transparent") {
|
||||
window.transparentTabs = true
|
||||
}
|
||||
|
||||
|
||||
if window.hasStyledTabs {
|
||||
// Set the background color of the window
|
||||
let backgroundColor = NSColor(ghostty.config.backgroundColor)
|
||||
window.backgroundColor = backgroundColor
|
||||
|
||||
|
||||
// This makes sure our titlebar renders correctly when there is a transparent background
|
||||
window.titlebarColor = backgroundColor.withAlphaComponent(ghostty.config.backgroundOpacity)
|
||||
}
|
||||
|
||||
|
||||
// Initialize our content view to the SwiftUI root
|
||||
window.contentView = NSHostingView(rootView: TerminalView(
|
||||
ghostty: self.ghostty,
|
||||
viewModel: self,
|
||||
delegate: self
|
||||
))
|
||||
|
||||
|
||||
// If our titlebar style is "hidden" we adjust the style appropriately
|
||||
if (ghostty.config.macosTitlebarStyle == "hidden") {
|
||||
window.styleMask = [
|
||||
// We need `titled` in the mask to get the normal window frame
|
||||
.titled,
|
||||
|
||||
|
||||
// Full size content view so we can extend
|
||||
// content in to the hidden titlebar's area
|
||||
.fullSizeContentView,
|
||||
|
||||
.resizable,
|
||||
.fullSizeContentView,
|
||||
|
||||
.resizable,
|
||||
.closable,
|
||||
.miniaturizable,
|
||||
]
|
||||
|
||||
|
||||
// Hide the title
|
||||
window.titleVisibility = .hidden
|
||||
window.titlebarAppearsTransparent = true
|
||||
|
||||
|
||||
// Hide the traffic lights (window control buttons)
|
||||
window.standardWindowButton(.closeButton)?.isHidden = true
|
||||
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
||||
window.standardWindowButton(.zoomButton)?.isHidden = true
|
||||
|
||||
|
||||
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
|
||||
window.tabbingMode = .disallowed
|
||||
|
||||
|
||||
// Nuke it from orbit -- hide the titlebar container entirely, just in case. There are
|
||||
// some operations that appear to bring back the titlebar visibility so this ensures
|
||||
// it is gone forever.
|
||||
@ -324,7 +300,7 @@ class TerminalController: BaseTerminalController {
|
||||
titleBarContainer.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// In various situations, macOS automatically tabs new windows. Ghostty handles
|
||||
// its own tabbing so we DONT want this behavior. This detects this scenario and undoes
|
||||
// it.
|
||||
@ -344,9 +320,9 @@ class TerminalController: BaseTerminalController {
|
||||
window.tabGroup?.removeWindow(window)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
window.focusFollowsMouse = ghostty.config.focusFollowsMouse
|
||||
|
||||
|
||||
// Apply any additional appearance-related properties to the new window.
|
||||
syncAppearance()
|
||||
}
|
||||
|
@ -128,13 +128,14 @@ extension Ghostty {
|
||||
return v
|
||||
}
|
||||
|
||||
var windowColorspace: String {
|
||||
guard let config = self.config else { return "" }
|
||||
var windowColorspace: WindowColorspace {
|
||||
guard let config = self.config else { return .srgb }
|
||||
var v: UnsafePointer<Int8>? = nil
|
||||
let key = "window-colorspace"
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
|
||||
guard let ptr = v else { return "" }
|
||||
return String(cString: ptr)
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .srgb }
|
||||
guard let ptr = v else { return .srgb }
|
||||
let str = String(cString: ptr)
|
||||
return WindowColorspace(rawValue: str) ?? .srgb
|
||||
}
|
||||
|
||||
var windowSaveState: String {
|
||||
@ -474,4 +475,9 @@ extension Ghostty.Config {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum WindowColorspace : String {
|
||||
case srgb
|
||||
case displayP3 = "display-p3"
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user