macos: change config access to evented, derived config like libghostty

Previously, we would access the `ghostty.config` object from anywhere.
The issue with this is that memory lifetime access to the underlying
`ghostty_config_t` was messy. It was easy when the apprt owned every
reference but since automatic theme changes were implemented, this isn't
always true anymore.

To fix this, we move to the same pattern we use internally in the core
of ghostty: whenever the config changes, we handle an event, derive our
desired values out of the config (copy them), and then let the caller
free the config if they want to. This way, we can be sure that any
information we need from the config is always owned by us.
This commit is contained in:
Mitchell Hashimoto
2024-11-20 15:28:10 -08:00
parent 037d4364e5
commit 35fcb1a29b
9 changed files with 395 additions and 199 deletions

View File

@@ -60,6 +60,9 @@ class BaseTerminalController: NSWindowController,
/// The previous frame information from the window
private var savedFrame: SavedFrame? = nil
/// The configuration derived from the Ghostty config so we don't need to rely on references.
private var derivedConfig: DerivedConfig
struct SavedFrame {
let window: NSRect
let screen: NSRect
@@ -74,6 +77,7 @@ class BaseTerminalController: NSWindowController,
surfaceTree tree: Ghostty.SplitNode? = nil
) {
self.ghostty = ghostty
self.derivedConfig = DerivedConfig(ghostty.config)
super.init(window: nil)
@@ -93,6 +97,11 @@ class BaseTerminalController: NSWindowController,
selector: #selector(didChangeScreenParametersNotification),
name: NSApplication.didChangeScreenParametersNotification,
object: nil)
center.addObserver(
self,
selector: #selector(ghosttyConfigDidChangeBase(_:)),
name: .ghosttyConfigDidChange,
object: nil)
// Listen for local events that we need to know of outside of
// single surface handlers.
@@ -191,6 +200,20 @@ class BaseTerminalController: NSWindowController,
window.setFrame(newFrame, display: true)
}
@objc private func ghosttyConfigDidChangeBase(_ notification: Notification) {
// We only care if the configuration is a global configuration, not a
// surface-specific one.
guard notification.object == nil else { return }
// Get our managed configuration object out
guard let config = notification.userInfo?[
Notification.Name.GhosttyConfigChangeKey
] as? Ghostty.Config else { return }
// Update our derived config
self.derivedConfig = DerivedConfig(config)
}
// MARK: Local Events
private func localEventHandler(_ event: NSEvent) -> NSEvent? {
@@ -245,7 +268,7 @@ class BaseTerminalController: NSWindowController,
func pwdDidChange(to: URL?) {
guard let window else { return }
if ghostty.config.macosTitlebarProxyIcon == .visible {
if derivedConfig.macosTitlebarProxyIcon == .visible {
// Use the 'to' URL directly
window.representedURL = to
} else {
@@ -255,7 +278,7 @@ class BaseTerminalController: NSWindowController,
func cellSizeDidChange(to: NSSize) {
guard ghostty.config.windowStepResize else { return }
guard derivedConfig.windowStepResize else { return }
self.window?.contentResizeIncrements = to
}
@@ -563,4 +586,19 @@ class BaseTerminalController: NSWindowController,
guard let surface = focusedSurface?.surface else { return }
ghostty.resetTerminal(surface: surface)
}
private struct DerivedConfig {
let macosTitlebarProxyIcon: Ghostty.MacOSTitlebarProxyIcon
let windowStepResize: Bool
init() {
self.macosTitlebarProxyIcon = .visible
self.windowStepResize = false
}
init(_ config: Ghostty.Config) {
self.macosTitlebarProxyIcon = config.macosTitlebarProxyIcon
self.windowStepResize = config.windowStepResize
}
}
}