mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
apprt: action to change conditional state, implement for embedded
This commit is contained in:
@ -567,6 +567,7 @@ typedef enum {
|
|||||||
GHOSTTY_ACTION_SECURE_INPUT,
|
GHOSTTY_ACTION_SECURE_INPUT,
|
||||||
GHOSTTY_ACTION_KEY_SEQUENCE,
|
GHOSTTY_ACTION_KEY_SEQUENCE,
|
||||||
GHOSTTY_ACTION_COLOR_CHANGE,
|
GHOSTTY_ACTION_COLOR_CHANGE,
|
||||||
|
GHOSTTY_ACTION_CONFIG_CHANGE_CONDITIONAL_STATE,
|
||||||
} ghostty_action_tag_e;
|
} ghostty_action_tag_e;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
|
@ -94,11 +94,6 @@ keyboard: Keyboard,
|
|||||||
/// less important.
|
/// less important.
|
||||||
pressed_key: ?input.KeyEvent = null,
|
pressed_key: ?input.KeyEvent = null,
|
||||||
|
|
||||||
/// The current color scheme of the GUI element containing this surface.
|
|
||||||
/// This will default to light until the apprt sends us the actual color
|
|
||||||
/// scheme. This is used by mode 3031 and CSI 996 n.
|
|
||||||
color_scheme: apprt.ColorScheme = .light,
|
|
||||||
|
|
||||||
/// The hash value of the last keybinding trigger that we performed. This
|
/// The hash value of the last keybinding trigger that we performed. This
|
||||||
/// is only set if the last key input matched a keybinding, consumed it,
|
/// is only set if the last key input matched a keybinding, consumed it,
|
||||||
/// and performed it. This is used to prevent sending release/repeat events
|
/// and performed it. This is used to prevent sending release/repeat events
|
||||||
@ -121,6 +116,12 @@ size: renderer.Size,
|
|||||||
/// the lifetime of. This makes updating config at runtime easier.
|
/// the lifetime of. This makes updating config at runtime easier.
|
||||||
config: DerivedConfig,
|
config: DerivedConfig,
|
||||||
|
|
||||||
|
/// The conditional state of the configuration. This can affect
|
||||||
|
/// how certain configurations take effect such as light/dark mode.
|
||||||
|
/// This is managed completely by Ghostty core but an apprt action
|
||||||
|
/// is sent whenever this changes.
|
||||||
|
config_conditional_state: configpkg.ConditionalState,
|
||||||
|
|
||||||
/// This is set to true if our IO thread notifies us our child exited.
|
/// This is set to true if our IO thread notifies us our child exited.
|
||||||
/// This is used to determine if we need to confirm, hold open, etc.
|
/// This is used to determine if we need to confirm, hold open, etc.
|
||||||
child_exited: bool = false,
|
child_exited: bool = false,
|
||||||
@ -480,6 +481,7 @@ pub fn init(
|
|||||||
.io_thr = undefined,
|
.io_thr = undefined,
|
||||||
.size = size,
|
.size = size,
|
||||||
.config = derived_config,
|
.config = derived_config,
|
||||||
|
.config_conditional_state = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
// The command we're going to execute
|
// The command we're going to execute
|
||||||
@ -777,7 +779,7 @@ pub fn needsConfirmQuit(self: *Surface) bool {
|
|||||||
/// surface.
|
/// surface.
|
||||||
pub fn handleMessage(self: *Surface, msg: Message) !void {
|
pub fn handleMessage(self: *Surface, msg: Message) !void {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
.change_config => |config| try self.changeConfig(config),
|
.change_config => |config| try self.updateConfig(config),
|
||||||
|
|
||||||
.set_title => |*v| {
|
.set_title => |*v| {
|
||||||
// We ignore the message in case the title was set via config.
|
// We ignore the message in case the title was set via config.
|
||||||
@ -935,7 +937,7 @@ fn passwordInput(self: *Surface, v: bool) !void {
|
|||||||
|
|
||||||
/// Sends a DSR response for the current color scheme to the pty.
|
/// Sends a DSR response for the current color scheme to the pty.
|
||||||
fn reportColorScheme(self: *Surface) !void {
|
fn reportColorScheme(self: *Surface) !void {
|
||||||
const output = switch (self.color_scheme) {
|
const output = switch (self.config_conditional_state.theme) {
|
||||||
.light => "\x1B[?997;2n",
|
.light => "\x1B[?997;2n",
|
||||||
.dark => "\x1B[?997;1n",
|
.dark => "\x1B[?997;1n",
|
||||||
};
|
};
|
||||||
@ -1058,8 +1060,25 @@ fn updateRendererHealth(self: *Surface, health: renderer.Health) void {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update our configuration at runtime.
|
/// This should be called anytime `config_conditional_state` changes
|
||||||
fn changeConfig(self: *Surface, config: *const configpkg.Config) !void {
|
/// so that the apprt can reload the configuration.
|
||||||
|
fn notifyConfigConditionalState(self: *Surface) void {
|
||||||
|
self.rt_app.performAction(
|
||||||
|
.{ .surface = self },
|
||||||
|
.config_change_conditional_state,
|
||||||
|
{},
|
||||||
|
) catch |err| {
|
||||||
|
log.warn("failed to notify app of config state change err={}", .{err});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update our configuration at runtime. This can be called by the apprt
|
||||||
|
/// to set a surface-specific configuration that differs from the app
|
||||||
|
/// or other surfaces.
|
||||||
|
pub fn updateConfig(
|
||||||
|
self: *Surface,
|
||||||
|
config: *const configpkg.Config,
|
||||||
|
) !void {
|
||||||
// Update our new derived config immediately
|
// Update our new derived config immediately
|
||||||
const derived = DerivedConfig.init(self.alloc, config) catch |err| {
|
const derived = DerivedConfig.init(self.alloc, config) catch |err| {
|
||||||
// If the derivation fails then we just log and return. We don't
|
// If the derivation fails then we just log and return. We don't
|
||||||
@ -3590,11 +3609,17 @@ pub fn colorSchemeCallback(self: *Surface, scheme: apprt.ColorScheme) !void {
|
|||||||
crash.sentry.thread_state = self.crashThreadState();
|
crash.sentry.thread_state = self.crashThreadState();
|
||||||
defer crash.sentry.thread_state = null;
|
defer crash.sentry.thread_state = null;
|
||||||
|
|
||||||
// If our scheme didn't change, then we don't do anything.
|
const new_scheme: configpkg.ConditionalState.Theme = switch (scheme) {
|
||||||
if (self.color_scheme == scheme) return;
|
.light => .light,
|
||||||
|
.dark => .dark,
|
||||||
|
};
|
||||||
|
|
||||||
// Set our new scheme
|
// If our scheme didn't change, then we don't do anything.
|
||||||
self.color_scheme = scheme;
|
if (self.config_conditional_state.theme == new_scheme) return;
|
||||||
|
|
||||||
|
// Setup our conditional state which has the current color theme.
|
||||||
|
self.config_conditional_state.theme = new_scheme;
|
||||||
|
self.notifyConfigConditionalState();
|
||||||
|
|
||||||
// If mode 2031 is on, then we report the change live.
|
// If mode 2031 is on, then we report the change live.
|
||||||
const report = report: {
|
const report = report: {
|
||||||
|
@ -193,6 +193,13 @@ pub const Action = union(Key) {
|
|||||||
/// such as OSC 10/11.
|
/// such as OSC 10/11.
|
||||||
color_change: ColorChange,
|
color_change: ColorChange,
|
||||||
|
|
||||||
|
/// The state of conditionals in the configuration has changed, so
|
||||||
|
/// the configuration should be reloaded. The apprt doesn't need
|
||||||
|
/// to do a full physical reload; it should call the
|
||||||
|
/// `changeConditionalState` function and then `updateConfig`
|
||||||
|
/// on the app or surface.
|
||||||
|
config_change_conditional_state,
|
||||||
|
|
||||||
/// Sync with: ghostty_action_tag_e
|
/// Sync with: ghostty_action_tag_e
|
||||||
pub const Key = enum(c_int) {
|
pub const Key = enum(c_int) {
|
||||||
new_window,
|
new_window,
|
||||||
@ -228,6 +235,7 @@ pub const Action = union(Key) {
|
|||||||
secure_input,
|
secure_input,
|
||||||
key_sequence,
|
key_sequence,
|
||||||
color_change,
|
color_change,
|
||||||
|
config_change_conditional_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Sync with: ghostty_action_u
|
/// Sync with: ghostty_action_u
|
||||||
|
@ -430,6 +430,28 @@ pub const App = struct {
|
|||||||
comptime action: apprt.Action.Key,
|
comptime action: apprt.Action.Key,
|
||||||
value: apprt.Action.Value(action),
|
value: apprt.Action.Value(action),
|
||||||
) !void {
|
) !void {
|
||||||
|
// Special case certain actions before they are sent to the
|
||||||
|
// embedded apprt.
|
||||||
|
self.performPreAction(target, action, value);
|
||||||
|
|
||||||
|
log.debug("dispatching action target={s} action={} value={}", .{
|
||||||
|
@tagName(target),
|
||||||
|
action,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
self.opts.action(
|
||||||
|
self,
|
||||||
|
target.cval(),
|
||||||
|
@unionInit(apprt.Action, @tagName(action), value).cval(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn performPreAction(
|
||||||
|
self: *App,
|
||||||
|
target: apprt.Target,
|
||||||
|
comptime action: apprt.Action.Key,
|
||||||
|
value: apprt.Action.Value(action),
|
||||||
|
) void {
|
||||||
// Special case certain actions before they are sent to the embedder
|
// Special case certain actions before they are sent to the embedder
|
||||||
switch (action) {
|
switch (action) {
|
||||||
.set_title => switch (target) {
|
.set_title => switch (target) {
|
||||||
@ -443,19 +465,32 @@ pub const App = struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.config_change_conditional_state => switch (target) {
|
||||||
|
.app => {},
|
||||||
|
.surface => |surface| action: {
|
||||||
|
// Build our new configuration. We can free the memory
|
||||||
|
// immediately after because the surface will derive any
|
||||||
|
// values it needs to.
|
||||||
|
var new_config = self.config.changeConditionalState(
|
||||||
|
surface.config_conditional_state,
|
||||||
|
) catch |err| {
|
||||||
|
// Not a big deal if we error... we just don't update
|
||||||
|
// the config. We log the error and move on.
|
||||||
|
log.warn("error changing config conditional state err={}", .{err});
|
||||||
|
break :action;
|
||||||
|
};
|
||||||
|
defer new_config.deinit();
|
||||||
|
|
||||||
|
// Update our surface.
|
||||||
|
surface.updateConfig(&new_config) catch |err| {
|
||||||
|
log.warn("error updating surface config for state change err={}", .{err});
|
||||||
|
break :action;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("dispatching action target={s} action={} value={}", .{
|
|
||||||
@tagName(target),
|
|
||||||
action,
|
|
||||||
value,
|
|
||||||
});
|
|
||||||
self.opts.action(
|
|
||||||
self,
|
|
||||||
target.cval(),
|
|
||||||
@unionInit(apprt.Action, @tagName(action), value).cval(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,6 +225,7 @@ pub const App = struct {
|
|||||||
.renderer_health,
|
.renderer_health,
|
||||||
.color_change,
|
.color_change,
|
||||||
.pwd,
|
.pwd,
|
||||||
|
.config_change_conditional_state,
|
||||||
=> log.info("unimplemented action={}", .{action}),
|
=> log.info("unimplemented action={}", .{action}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,6 +487,7 @@ pub fn performAction(
|
|||||||
.render_inspector,
|
.render_inspector,
|
||||||
.renderer_health,
|
.renderer_health,
|
||||||
.color_change,
|
.color_change,
|
||||||
|
.config_change_conditional_state,
|
||||||
=> log.warn("unimplemented action={}", .{action}),
|
=> log.warn("unimplemented action={}", .{action}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ pub const string = @import("config/string.zig");
|
|||||||
pub const edit = @import("config/edit.zig");
|
pub const edit = @import("config/edit.zig");
|
||||||
pub const url = @import("config/url.zig");
|
pub const url = @import("config/url.zig");
|
||||||
|
|
||||||
|
pub const ConditionalState = conditional.State;
|
||||||
pub const FileFormatter = formatter.FileFormatter;
|
pub const FileFormatter = formatter.FileFormatter;
|
||||||
pub const entryFormatter = formatter.entryFormatter;
|
pub const entryFormatter = formatter.entryFormatter;
|
||||||
pub const formatEntry = formatter.formatEntry;
|
pub const formatEntry = formatter.formatEntry;
|
||||||
|
@ -2583,7 +2583,7 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
|
|||||||
/// deleted a file that was referenced in the configuration, then the
|
/// deleted a file that was referenced in the configuration, then the
|
||||||
/// configuration can still be reloaded.
|
/// configuration can still be reloaded.
|
||||||
pub fn changeConditionalState(
|
pub fn changeConditionalState(
|
||||||
self: *Config,
|
self: *const Config,
|
||||||
new: conditional.State,
|
new: conditional.State,
|
||||||
) !Config {
|
) !Config {
|
||||||
// Create our new configuration
|
// Create our new configuration
|
||||||
|
Reference in New Issue
Block a user