diff --git a/include/ghostty.h b/include/ghostty.h index d0426e995..5186ad783 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -532,6 +532,11 @@ typedef struct { uint8_t b; } ghostty_action_color_change_s; +// apprt.action.ConfigChange +typedef struct { + ghostty_config_t config; +} ghostty_action_config_change_s; + // apprt.Action.Key typedef enum { GHOSTTY_ACTION_NEW_WINDOW, @@ -568,6 +573,7 @@ typedef enum { GHOSTTY_ACTION_KEY_SEQUENCE, GHOSTTY_ACTION_COLOR_CHANGE, GHOSTTY_ACTION_CONFIG_CHANGE_CONDITIONAL_STATE, + GHOSTTY_ACTION_CONFIG_CHANGE, } ghostty_action_tag_e; typedef union { @@ -592,6 +598,7 @@ typedef union { ghostty_action_secure_input_e secure_input; ghostty_action_key_sequence_s key_sequence; ghostty_action_color_change_s color_change; + ghostty_action_config_change_s config_change; } ghostty_action_u; typedef struct { diff --git a/src/App.zig b/src/App.zig index 271ba2043..ebf257f04 100644 --- a/src/App.zig +++ b/src/App.zig @@ -147,11 +147,18 @@ pub fn tick(self: *App, rt_app: *apprt.App) !bool { /// Update the configuration associated with the app. This can only be /// called from the main thread. The caller owns the config memory. The /// memory can be freed immediately when this returns. -pub fn updateConfig(self: *App, config: *const Config) !void { +pub fn updateConfig(self: *App, rt_app: *apprt.App, config: *const Config) !void { // Go through and update all of the surface configurations. for (self.surfaces.items) |surface| { try surface.core_surface.handleMessage(.{ .change_config = config }); } + + // Notify the apprt that the app has changed configuration. + try rt_app.performAction( + .app, + .config_change, + .{ .config = config }, + ); } /// Add an initialized surface. This is really only for the runtime @@ -257,7 +264,7 @@ pub fn reloadConfig(self: *App, rt_app: *apprt.App) !void { log.debug("reloading configuration", .{}); if (try rt_app.reloadConfig()) |new| { log.debug("new configuration received, applying", .{}); - try self.updateConfig(new); + try self.updateConfig(rt_app, new); } } diff --git a/src/Surface.zig b/src/Surface.zig index 27a3fb5a8..ebeac4b97 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1127,6 +1127,13 @@ pub fn updateConfig( self.queueRender() catch |err| { log.warn("failed to notify renderer of config change err={}", .{err}); }; + + // Notify the window + try self.rt_app.performAction( + .{ .surface = self }, + .config_change, + .{ .config = config }, + ); } /// Returns true if the terminal has a selection. diff --git a/src/apprt/action.zig b/src/apprt/action.zig index aef6937a8..136f5fd7e 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -1,6 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; const apprt = @import("../apprt.zig"); +const configpkg = @import("../config.zig"); const input = @import("../input.zig"); const renderer = @import("../renderer.zig"); const terminal = @import("../terminal/main.zig"); @@ -200,6 +201,20 @@ pub const Action = union(Key) { /// on the app or surface. config_change_conditional_state, + /// The configuration has changed. The value is a pointer to the new + /// configuration. The pointer is only valid for the duration of the + /// action and should not be stored. + /// + /// This should be used by apprts to update any internal state that + /// depends on configuration for the given target (i.e. headerbar colors). + /// The apprt should copy any data it needs since the memory lifetime + /// is only valid for the duration of the action. + /// + /// This allows an apprt to have config-dependent state reactively + /// change without having to store the entire configuration or poll + /// for changes. + config_change: ConfigChange, + /// Sync with: ghostty_action_tag_e pub const Key = enum(c_int) { new_window, @@ -236,6 +251,7 @@ pub const Action = union(Key) { key_sequence, color_change, config_change_conditional_state, + config_change, }; /// Sync with: ghostty_action_u @@ -497,3 +513,18 @@ pub const ColorKind = enum(c_int) { // 0+ values indicate a palette index _, }; + +pub const ConfigChange = struct { + config: *const configpkg.Config, + + // Sync with: ghostty_action_config_change_s + pub const C = extern struct { + config: *const configpkg.Config, + }; + + pub fn cval(self: ConfigChange) C { + return .{ + .config = self.config, + }; + } +}; diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 3c866a1de..19be46778 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -226,6 +226,7 @@ pub const App = struct { .color_change, .pwd, .config_change_conditional_state, + .config_change, => log.info("unimplemented action={}", .{action}), } } diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 99148fd87..fa8a73830 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -488,6 +488,7 @@ pub fn performAction( .render_inspector, .renderer_health, .color_change, + .config_change, => log.warn("unimplemented action={}", .{action}), } }