From f0bf0fd8885506b3360fd50cca0b07b12ab0e7f7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 22 Dec 2023 21:47:20 -0800 Subject: [PATCH] config: window-save-state --- macos/Sources/AppDelegate.swift | 22 +++++++++++-------- macos/Sources/Ghostty/AppState.swift | 14 ++++++++++-- src/config/Config.zig | 32 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/macos/Sources/AppDelegate.swift b/macos/Sources/AppDelegate.swift index f0e835d08..fb10b2b1d 100644 --- a/macos/Sources/AppDelegate.swift +++ b/macos/Sources/AppDelegate.swift @@ -96,17 +96,14 @@ class AppDelegate: NSObject, // Disable this so that repeated key events make it through to our terminal views. "ApplePressAndHoldEnabled": false, ]) - - // TODO: make this configurable via ghostty - // reset to system defaults - //UserDefaults.standard.removeObject(forKey: "NSQuitAlwaysKeepsWindows") - // force state save - UserDefaults.standard.setValue(true, forKey: "NSQuitAlwaysKeepsWindows") // Hook up updater menu menuCheckForUpdates?.target = updaterController menuCheckForUpdates?.action = #selector(SPUStandardUpdaterController.checkForUpdates(_:)) + // Initial config loading + configDidReload(ghostty) + // Let's launch our first window. We only do this if we have no other windows. It // is possible to have other windows if we're opening a URL since `application(_:openFile:)` // is called before this. @@ -114,9 +111,6 @@ class AppDelegate: NSObject, terminalManager.newWindow() } - // Initial config loading - configDidReload(ghostty) - // Register our service provider. This must happen after everything // else is initialized. NSApp.servicesProvider = ServiceProvider() @@ -339,6 +333,16 @@ class AppDelegate: NSObject, //MARK: - GhosttyAppStateDelegate func configDidReload(_ state: Ghostty.AppState) { + // Depending on the "window-save-state" setting we have to set the NSQuitAlwaysKeepsWindows + // configuration. This is the only way to carefully control whether macOS invokes the + // state restoration system. + switch (ghostty.windowSaveState) { + case "never": UserDefaults.standard.setValue(false, forKey: "NSQuitAlwaysKeepsWindows") + case "always": UserDefaults.standard.setValue(true, forKey: "NSQuitAlwaysKeepsWindows") + case "default": fallthrough + default: UserDefaults.standard.removeObject(forKey: "NSQuitAlwaysKeepsWindows") + } + // Config could change keybindings, so update everything that depends on that syncMenuShortcuts() terminalManager.relabelAllTabs() diff --git a/macos/Sources/Ghostty/AppState.swift b/macos/Sources/Ghostty/AppState.swift index ca457f211..32f22da59 100644 --- a/macos/Sources/Ghostty/AppState.swift +++ b/macos/Sources/Ghostty/AppState.swift @@ -58,9 +58,19 @@ extension Ghostty { var v = false; let key = "quit-after-last-window-closed" _ = ghostty_config_get(config, &v, key, UInt(key.count)) - return v; + return v } - + + /// window-save-state + var windowSaveState: String { + guard let config = self.config else { return "" } + var v: UnsafePointer? = nil + let key = "window-save-state" + guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" } + guard let ptr = v else { return "" } + return String(cString: ptr) + } + /// True if we need to confirm before quitting. var needsConfirmQuit: Bool { guard let app = app else { return false } diff --git a/src/config/Config.zig b/src/config/Config.zig index 0a019b70d..c74fbc990 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -563,6 +563,31 @@ keybind: Keybinds = .{}, @"window-height": u32 = 0, @"window-width": u32 = 0, +/// Whether to enable saving and restoring window state. Window state +/// includes their position, size, tabs, splits, etc. Some window state +/// requires shell integration, such as preserving working directories. +/// See shell-integration for more information. +/// +/// There are three valid values for this configuration: +/// - "default" will use the default system behavior. On macOS, this +/// will only save state if the application is forcibly terminated +/// or if it is configured systemwide via Settings.app. +/// - "never" will never save window state. +/// - "always" will always save window state whenever Ghostty is exited. +/// +/// If you change this value so that window state is NOT saved while +/// window state is already saved, the next Ghostty launch will NOT restore +/// the window state. +/// +/// If you change this value so that window state is saved while Ghostty +/// is not running, the previous window state will not be restored because +/// Ghostty only saves state on exit if this is enabled. +/// +/// The default value is "default". +/// +/// This is currently only supported on macOS. This has no effect on Linux. +@"window-save-state": WindowSaveState = .always, // TODO: change before PR + /// Resize the window in discrete increments of the focused surface's /// cell size. If this is disabled, surfaces are resized in pixel increments. /// Currently only supported on macOS. @@ -2744,3 +2769,10 @@ pub const ClipboardAccess = enum { deny, ask, }; + +/// See window-save-state +pub const WindowSaveState = enum { + default, + never, + always, +};