macos: implement config reloading callback

This commit is contained in:
Mitchell Hashimoto
2023-03-16 15:29:46 -07:00
parent a5cfd4b04b
commit b26e51d222
3 changed files with 70 additions and 26 deletions

View File

@ -243,6 +243,7 @@ int ghostty_init(void);
ghostty_config_t ghostty_config_new(); ghostty_config_t ghostty_config_new();
void ghostty_config_free(ghostty_config_t); void ghostty_config_free(ghostty_config_t);
void ghostty_config_load_cli_args(ghostty_config_t);
void ghostty_config_load_string(ghostty_config_t, const char *, uintptr_t); void ghostty_config_load_string(ghostty_config_t, const char *, uintptr_t);
void ghostty_config_load_default_files(ghostty_config_t); void ghostty_config_load_default_files(ghostty_config_t);
void ghostty_config_load_recursive_files(ghostty_config_t); void ghostty_config_load_recursive_files(ghostty_config_t);

View File

@ -12,12 +12,25 @@ extension Ghostty {
/// The readiness value of the state. /// The readiness value of the state.
@Published var readiness: AppReadiness = .loading @Published var readiness: AppReadiness = .loading
/// The ghostty global configuration. /// The ghostty global configuration. This should only be changed when it is definitely
var config: ghostty_config_t? = nil /// safe to change. It is definite safe to change only when the embedded app runtime
/// in Ghostty says so (usually, only in a reload configuration callback).
var config: ghostty_config_t? = nil {
didSet {
// Free the old value whenever we change
guard let old = oldValue else { return }
ghostty_config_free(old)
}
}
/// The ghostty app instance. We only have one of these for the entire app, although I guess /// The ghostty app instance. We only have one of these for the entire app, although I guess
/// in theory you can have multiple... I don't know why you would... /// in theory you can have multiple... I don't know why you would...
var app: ghostty_app_t? = nil var app: ghostty_app_t? = nil {
didSet {
guard let old = oldValue else { return }
ghostty_app_free(old)
}
}
/// Cached clipboard string for `read_clipboard` callback. /// Cached clipboard string for `read_clipboard` callback.
private var cached_clipboard_string: String? = nil private var cached_clipboard_string: String? = nil
@ -31,24 +44,12 @@ extension Ghostty {
} }
// Initialize the global configuration. // Initialize the global configuration.
guard let cfg = ghostty_config_new() else { guard let cfg = Self.reloadConfig() else {
GhosttyApp.logger.critical("ghostty_config_new failed")
readiness = .error readiness = .error
return return
} }
self.config = cfg; self.config = cfg;
// Load our configuration files from the home directory.
ghostty_config_load_default_files(cfg);
ghostty_config_load_recursive_files(cfg);
// TODO: we'd probably do some config loading here... for now we'd
// have to do this synchronously. When we support config updating we can do
// this async and update later.
// Finalize will make our defaults available.
ghostty_config_finalize(cfg)
// Create our "runtime" config. The "runtime" is the configuration that ghostty // Create our "runtime" config. The "runtime" is the configuration that ghostty
// uses to interface with the application runtime environment. // uses to interface with the application runtime environment.
var runtime_cfg = ghostty_runtime_config_s( var runtime_cfg = ghostty_runtime_config_s(
@ -75,8 +76,32 @@ extension Ghostty {
} }
deinit { deinit {
ghostty_app_free(app) // This will force the didSet callbacks to run which free.
ghostty_config_free(config) self.app = nil
self.config = nil
}
/// Initializes a new configuration and loads all the values.
static func reloadConfig() -> ghostty_config_t? {
// Initialize the global configuration.
guard let cfg = ghostty_config_new() else {
GhosttyApp.logger.critical("ghostty_config_new failed")
return nil
}
// Load our configuration files from the home directory.
ghostty_config_load_default_files(cfg);
ghostty_config_load_cli_args(cfg);
ghostty_config_load_recursive_files(cfg);
// TODO: we'd probably do some config loading here... for now we'd
// have to do this synchronously. When we support config updating we can do
// this async and update later.
// Finalize will make our defaults available.
ghostty_config_finalize(cfg)
return cfg
} }
func appTick() { func appTick() {
@ -142,12 +167,19 @@ extension Ghostty {
} }
static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? { static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? {
// TODO: implement config reloading in the mac app guard let newConfig = AppState.reloadConfig() else {
let state = Unmanaged<AppState>.fromOpaque(userdata!).takeUnretainedValue() GhosttyApp.logger.warning("failed to reload configuration")
_ = state
return nil return nil
} }
// Assign the new config. This will automatically free the old config.
// It is safe to free the old config from within this function call.
let state = Unmanaged<AppState>.fromOpaque(userdata!).takeUnretainedValue()
state.config = newConfig
return newConfig
}
static func wakeup(_ userdata: UnsafeMutableRawPointer?) { static func wakeup(_ userdata: UnsafeMutableRawPointer?) {
let state = Unmanaged<AppState>.fromOpaque(userdata!).takeUnretainedValue() let state = Unmanaged<AppState>.fromOpaque(userdata!).takeUnretainedValue()

View File

@ -217,11 +217,7 @@ pub const Config = struct {
try result.loadDefaultFiles(alloc_gpa); try result.loadDefaultFiles(alloc_gpa);
// Parse the config from the CLI args // Parse the config from the CLI args
{ try result.loadCliArgs(alloc_gpa);
var iter = try std.process.argsWithAllocator(alloc_gpa);
defer iter.deinit();
try cli_args.parse(Config, alloc_gpa, &result, &iter);
}
// Parse the config files that were added from our file and CLI args. // Parse the config files that were added from our file and CLI args.
try result.loadRecursiveFiles(alloc_gpa); try result.loadRecursiveFiles(alloc_gpa);
@ -564,6 +560,14 @@ pub const Config = struct {
} }
} }
/// Load and parse the CLI args.
pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
// Parse the config from the CLI args
var iter = try std.process.argsWithAllocator(alloc_gpa);
defer iter.deinit();
try cli_args.parse(Config, alloc_gpa, self, &iter);
}
/// Load and parse the config files that were added in the "config-file" key. /// Load and parse the config files that were added in the "config-file" key.
pub fn loadRecursiveFiles(self: *Config, alloc: Allocator) !void { pub fn loadRecursiveFiles(self: *Config, alloc: Allocator) !void {
// TODO(mitchellh): we should parse the files form the homedir first // TODO(mitchellh): we should parse the files form the homedir first
@ -1190,6 +1194,13 @@ pub const CAPI = struct {
} }
} }
/// Load the configuration from the CLI args.
export fn ghostty_config_load_cli_args(self: *Config) void {
self.loadCliArgs(global.alloc) catch |err| {
log.err("error loading config err={}", .{err});
};
}
/// Load the configuration from a string in the same format as /// Load the configuration from a string in the same format as
/// the file-based syntax for the desktop version of the terminal. /// the file-based syntax for the desktop version of the terminal.
export fn ghostty_config_load_string( export fn ghostty_config_load_string(