mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #2769 from ghostty-org/reload-config
Use apprt action system to trigger config reload
This commit is contained in:
@ -537,6 +537,11 @@ typedef struct {
|
|||||||
ghostty_config_t config;
|
ghostty_config_t config;
|
||||||
} ghostty_action_config_change_s;
|
} ghostty_action_config_change_s;
|
||||||
|
|
||||||
|
// apprt.action.ReloadConfig
|
||||||
|
typedef struct {
|
||||||
|
bool soft;
|
||||||
|
} ghostty_action_reload_config_s;
|
||||||
|
|
||||||
// apprt.Action.Key
|
// apprt.Action.Key
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GHOSTTY_ACTION_NEW_WINDOW,
|
GHOSTTY_ACTION_NEW_WINDOW,
|
||||||
@ -572,7 +577,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_RELOAD_CONFIG,
|
||||||
GHOSTTY_ACTION_CONFIG_CHANGE,
|
GHOSTTY_ACTION_CONFIG_CHANGE,
|
||||||
} ghostty_action_tag_e;
|
} ghostty_action_tag_e;
|
||||||
|
|
||||||
@ -598,6 +603,7 @@ typedef union {
|
|||||||
ghostty_action_secure_input_e secure_input;
|
ghostty_action_secure_input_e secure_input;
|
||||||
ghostty_action_key_sequence_s key_sequence;
|
ghostty_action_key_sequence_s key_sequence;
|
||||||
ghostty_action_color_change_s color_change;
|
ghostty_action_color_change_s color_change;
|
||||||
|
ghostty_action_reload_config_s reload_config;
|
||||||
ghostty_action_config_change_s config_change;
|
ghostty_action_config_change_s config_change;
|
||||||
} ghostty_action_u;
|
} ghostty_action_u;
|
||||||
|
|
||||||
@ -607,7 +613,6 @@ typedef struct {
|
|||||||
} ghostty_action_s;
|
} ghostty_action_s;
|
||||||
|
|
||||||
typedef void (*ghostty_runtime_wakeup_cb)(void*);
|
typedef void (*ghostty_runtime_wakeup_cb)(void*);
|
||||||
typedef const ghostty_config_t (*ghostty_runtime_reload_config_cb)(void*);
|
|
||||||
typedef void (*ghostty_runtime_read_clipboard_cb)(void*,
|
typedef void (*ghostty_runtime_read_clipboard_cb)(void*,
|
||||||
ghostty_clipboard_e,
|
ghostty_clipboard_e,
|
||||||
void*);
|
void*);
|
||||||
@ -630,7 +635,6 @@ typedef struct {
|
|||||||
bool supports_selection_clipboard;
|
bool supports_selection_clipboard;
|
||||||
ghostty_runtime_wakeup_cb wakeup_cb;
|
ghostty_runtime_wakeup_cb wakeup_cb;
|
||||||
ghostty_runtime_action_cb action_cb;
|
ghostty_runtime_action_cb action_cb;
|
||||||
ghostty_runtime_reload_config_cb reload_config_cb;
|
|
||||||
ghostty_runtime_read_clipboard_cb read_clipboard_cb;
|
ghostty_runtime_read_clipboard_cb read_clipboard_cb;
|
||||||
ghostty_runtime_confirm_read_clipboard_cb confirm_read_clipboard_cb;
|
ghostty_runtime_confirm_read_clipboard_cb confirm_read_clipboard_cb;
|
||||||
ghostty_runtime_write_clipboard_cb write_clipboard_cb;
|
ghostty_runtime_write_clipboard_cb write_clipboard_cb;
|
||||||
@ -668,7 +672,7 @@ void ghostty_app_set_focus(ghostty_app_t, bool);
|
|||||||
bool ghostty_app_key(ghostty_app_t, ghostty_input_key_s);
|
bool ghostty_app_key(ghostty_app_t, ghostty_input_key_s);
|
||||||
void ghostty_app_keyboard_changed(ghostty_app_t);
|
void ghostty_app_keyboard_changed(ghostty_app_t);
|
||||||
void ghostty_app_open_config(ghostty_app_t);
|
void ghostty_app_open_config(ghostty_app_t);
|
||||||
void ghostty_app_reload_config(ghostty_app_t);
|
void ghostty_app_update_config(ghostty_app_t, ghostty_config_t);
|
||||||
bool ghostty_app_needs_confirm_quit(ghostty_app_t);
|
bool ghostty_app_needs_confirm_quit(ghostty_app_t);
|
||||||
bool ghostty_app_has_global_keybinds(ghostty_app_t);
|
bool ghostty_app_has_global_keybinds(ghostty_app_t);
|
||||||
|
|
||||||
@ -679,6 +683,7 @@ void ghostty_surface_free(ghostty_surface_t);
|
|||||||
void* ghostty_surface_userdata(ghostty_surface_t);
|
void* ghostty_surface_userdata(ghostty_surface_t);
|
||||||
ghostty_app_t ghostty_surface_app(ghostty_surface_t);
|
ghostty_app_t ghostty_surface_app(ghostty_surface_t);
|
||||||
ghostty_surface_config_s ghostty_surface_inherited_config(ghostty_surface_t);
|
ghostty_surface_config_s ghostty_surface_inherited_config(ghostty_surface_t);
|
||||||
|
void ghostty_surface_update_config(ghostty_surface_t, ghostty_config_t);
|
||||||
bool ghostty_surface_needs_confirm_quit(ghostty_surface_t);
|
bool ghostty_surface_needs_confirm_quit(ghostty_surface_t);
|
||||||
void ghostty_surface_refresh(ghostty_surface_t);
|
void ghostty_surface_refresh(ghostty_surface_t);
|
||||||
void ghostty_surface_draw(ghostty_surface_t);
|
void ghostty_surface_draw(ghostty_surface_t);
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5985CD62C320C4500C57AD3 /* String+Extension.swift */; };
|
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5985CD62C320C4500C57AD3 /* String+Extension.swift */; };
|
||||||
A5985CD82C320C4500C57AD3 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5985CD62C320C4500C57AD3 /* String+Extension.swift */; };
|
A5985CD82C320C4500C57AD3 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5985CD62C320C4500C57AD3 /* String+Extension.swift */; };
|
||||||
A5985CE62C33060F00C57AD3 /* man in Resources */ = {isa = PBXBuildFile; fileRef = A5985CE52C33060F00C57AD3 /* man */; };
|
A5985CE62C33060F00C57AD3 /* man in Resources */ = {isa = PBXBuildFile; fileRef = A5985CE52C33060F00C57AD3 /* man */; };
|
||||||
|
A599CDB02CF103F60049FA26 /* NSAppearance+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */; };
|
||||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
||||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
||||||
@ -141,6 +142,7 @@
|
|||||||
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.SplitNode.swift; sourceTree = "<group>"; };
|
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.SplitNode.swift; sourceTree = "<group>"; };
|
||||||
A5985CD62C320C4500C57AD3 /* String+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
|
A5985CD62C320C4500C57AD3 /* String+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
|
||||||
A5985CE52C33060F00C57AD3 /* man */ = {isa = PBXFileReference; lastKnownFileType = folder; name = man; path = "../zig-out/share/man"; sourceTree = "<group>"; };
|
A5985CE52C33060F00C57AD3 /* man */ = {isa = PBXFileReference; lastKnownFileType = folder; name = man; path = "../zig-out/share/man"; sourceTree = "<group>"; };
|
||||||
|
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAppearance+Extension.swift"; sourceTree = "<group>"; };
|
||||||
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
||||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
||||||
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
||||||
@ -249,6 +251,7 @@
|
|||||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */,
|
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */,
|
||||||
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
|
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
|
||||||
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
|
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
|
||||||
|
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */,
|
||||||
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
||||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||||
@ -611,6 +614,7 @@
|
|||||||
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
|
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||||
|
A599CDB02CF103F60049FA26 /* NSAppearance+Extension.swift in Sources */,
|
||||||
A52FFF572CA90484000C6A5B /* QuickTerminalScreen.swift in Sources */,
|
A52FFF572CA90484000C6A5B /* QuickTerminalScreen.swift in Sources */,
|
||||||
A5CC36132C9CD72D004D6760 /* SecureInputOverlay.swift in Sources */,
|
A5CC36132C9CD72D004D6760 /* SecureInputOverlay.swift in Sources */,
|
||||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
||||||
|
@ -65,7 +65,6 @@ extension Ghostty {
|
|||||||
supports_selection_clipboard: false,
|
supports_selection_clipboard: false,
|
||||||
wakeup_cb: { userdata in App.wakeup(userdata) },
|
wakeup_cb: { userdata in App.wakeup(userdata) },
|
||||||
action_cb: { app, target, action in App.action(app!, target: target, action: action) },
|
action_cb: { app, target, action in App.action(app!, target: target, action: action) },
|
||||||
reload_config_cb: { userdata in App.reloadConfig(userdata) },
|
|
||||||
read_clipboard_cb: { userdata, loc, state in App.readClipboard(userdata, location: loc, state: state) },
|
read_clipboard_cb: { userdata, loc, state in App.readClipboard(userdata, location: loc, state: state) },
|
||||||
confirm_read_clipboard_cb: { userdata, str, state, request in App.confirmReadClipboard(userdata, string: str, state: state, request: request ) },
|
confirm_read_clipboard_cb: { userdata, str, state, request in App.confirmReadClipboard(userdata, string: str, state: state, request: request ) },
|
||||||
write_clipboard_cb: { userdata, str, loc, confirm in App.writeClipboard(userdata, string: str, location: loc, confirm: confirm) },
|
write_clipboard_cb: { userdata, str, loc, confirm in App.writeClipboard(userdata, string: str, location: loc, confirm: confirm) },
|
||||||
@ -142,9 +141,47 @@ extension Ghostty {
|
|||||||
ghostty_app_open_config(app)
|
ghostty_app_open_config(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reloadConfig() {
|
/// Reload the configuration.
|
||||||
|
func reloadConfig(soft: Bool = false) {
|
||||||
guard let app = self.app else { return }
|
guard let app = self.app else { return }
|
||||||
ghostty_app_reload_config(app)
|
|
||||||
|
// Soft updates just call with our existing config
|
||||||
|
if (soft) {
|
||||||
|
ghostty_app_update_config(app, config.config!)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hard or full updates have to reload the full configuration
|
||||||
|
let newConfig = Config()
|
||||||
|
guard newConfig.loaded else {
|
||||||
|
Ghostty.logger.warning("failed to reload configuration")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ghostty_app_update_config(app, newConfig.config!)
|
||||||
|
|
||||||
|
// We can only set our config after updating it so that we don't free
|
||||||
|
// memory that may still be in use
|
||||||
|
self.config = newConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadConfig(surface: ghostty_surface_t, soft: Bool = false) {
|
||||||
|
// Soft updates just call with our existing config
|
||||||
|
if (soft) {
|
||||||
|
ghostty_surface_update_config(surface, config.config!)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hard or full updates have to reload the full configuration.
|
||||||
|
// NOTE: We never set this on self.config because this is a surface-only
|
||||||
|
// config. We free it after the call.
|
||||||
|
let newConfig = Config()
|
||||||
|
guard newConfig.loaded else {
|
||||||
|
Ghostty.logger.warning("failed to reload configuration")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ghostty_surface_update_config(surface, newConfig.config!)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request that the given surface is closed. This will trigger the full normal surface close event
|
/// Request that the given surface is closed. This will trigger the full normal surface close event
|
||||||
@ -237,7 +274,6 @@ extension Ghostty {
|
|||||||
|
|
||||||
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {}
|
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {}
|
||||||
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {}
|
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {}
|
||||||
static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? { return nil }
|
|
||||||
static func readClipboard(
|
static func readClipboard(
|
||||||
_ userdata: UnsafeMutableRawPointer?,
|
_ userdata: UnsafeMutableRawPointer?,
|
||||||
location: ghostty_clipboard_e,
|
location: ghostty_clipboard_e,
|
||||||
@ -365,21 +401,6 @@ extension Ghostty {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? {
|
|
||||||
let newConfig = Config()
|
|
||||||
guard newConfig.loaded else {
|
|
||||||
AppDelegate.logger.warning("failed to reload configuration")
|
|
||||||
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<Self>.fromOpaque(userdata!).takeUnretainedValue()
|
|
||||||
state.config = newConfig
|
|
||||||
|
|
||||||
return newConfig.config
|
|
||||||
}
|
|
||||||
|
|
||||||
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {
|
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {
|
||||||
let state = Unmanaged<App>.fromOpaque(userdata!).takeUnretainedValue()
|
let state = Unmanaged<App>.fromOpaque(userdata!).takeUnretainedValue()
|
||||||
|
|
||||||
@ -514,6 +535,9 @@ extension Ghostty {
|
|||||||
case GHOSTTY_ACTION_CONFIG_CHANGE:
|
case GHOSTTY_ACTION_CONFIG_CHANGE:
|
||||||
configChange(app, target: target, v: action.action.config_change)
|
configChange(app, target: target, v: action.action.config_change)
|
||||||
|
|
||||||
|
case GHOSTTY_ACTION_RELOAD_CONFIG:
|
||||||
|
configReload(app, target: target, v: action.action.reload_config)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_COLOR_CHANGE:
|
case GHOSTTY_ACTION_COLOR_CHANGE:
|
||||||
colorChange(app, target: target, change: action.action.color_change)
|
colorChange(app, target: target, change: action.action.color_change)
|
||||||
|
|
||||||
@ -1150,6 +1174,30 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func configReload(
|
||||||
|
_ app: ghostty_app_t,
|
||||||
|
target: ghostty_target_s,
|
||||||
|
v: ghostty_action_reload_config_s)
|
||||||
|
{
|
||||||
|
logger.info("config reload notification")
|
||||||
|
|
||||||
|
guard let app_ud = ghostty_app_userdata(app) else { return }
|
||||||
|
let ghostty = Unmanaged<App>.fromOpaque(app_ud).takeUnretainedValue()
|
||||||
|
|
||||||
|
switch (target.tag) {
|
||||||
|
case GHOSTTY_TARGET_APP:
|
||||||
|
ghostty.reloadConfig(soft: v.soft)
|
||||||
|
return
|
||||||
|
|
||||||
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
|
guard let surface = target.target.surface else { return }
|
||||||
|
ghostty.reloadConfig(surface: surface, soft: v.soft)
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func configChange(
|
private static func configChange(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
|
8
macos/Sources/Helpers/NSAppearance+Extension.swift
Normal file
8
macos/Sources/Helpers/NSAppearance+Extension.swift
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
extension NSAppearance {
|
||||||
|
/// Returns true if the appearance is some kind of dark.
|
||||||
|
var isDark: Bool {
|
||||||
|
return name.rawValue.lowercased().contains("dark")
|
||||||
|
}
|
||||||
|
}
|
18
src/App.zig
18
src/App.zig
@ -12,7 +12,8 @@ const apprt = @import("apprt.zig");
|
|||||||
const Surface = @import("Surface.zig");
|
const Surface = @import("Surface.zig");
|
||||||
const tracy = @import("tracy");
|
const tracy = @import("tracy");
|
||||||
const input = @import("input.zig");
|
const input = @import("input.zig");
|
||||||
const Config = @import("config.zig").Config;
|
const configpkg = @import("config.zig");
|
||||||
|
const Config = configpkg.Config;
|
||||||
const BlockingQueue = @import("datastruct/main.zig").BlockingQueue;
|
const BlockingQueue = @import("datastruct/main.zig").BlockingQueue;
|
||||||
const renderer = @import("renderer.zig");
|
const renderer = @import("renderer.zig");
|
||||||
const font = @import("font/main.zig");
|
const font = @import("font/main.zig");
|
||||||
@ -239,7 +240,6 @@ fn drainMailbox(self: *App, rt_app: *apprt.App) !void {
|
|||||||
while (self.mailbox.pop()) |message| {
|
while (self.mailbox.pop()) |message| {
|
||||||
log.debug("mailbox message={s}", .{@tagName(message)});
|
log.debug("mailbox message={s}", .{@tagName(message)});
|
||||||
switch (message) {
|
switch (message) {
|
||||||
.reload_config => try self.reloadConfig(rt_app),
|
|
||||||
.open_config => try self.performAction(rt_app, .open_config),
|
.open_config => try self.performAction(rt_app, .open_config),
|
||||||
.new_window => |msg| try self.newWindow(rt_app, msg),
|
.new_window => |msg| try self.newWindow(rt_app, msg),
|
||||||
.close => |surface| self.closeSurface(surface),
|
.close => |surface| self.closeSurface(surface),
|
||||||
@ -260,14 +260,6 @@ fn drainMailbox(self: *App, rt_app: *apprt.App) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(rt_app, new);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn closeSurface(self: *App, surface: *Surface) void {
|
pub fn closeSurface(self: *App, surface: *Surface) void {
|
||||||
if (!self.hasSurface(surface)) return;
|
if (!self.hasSurface(surface)) return;
|
||||||
surface.close();
|
surface.close();
|
||||||
@ -402,7 +394,7 @@ pub fn performAction(
|
|||||||
.quit => self.setQuit(),
|
.quit => self.setQuit(),
|
||||||
.new_window => try self.newWindow(rt_app, .{ .parent = null }),
|
.new_window => try self.newWindow(rt_app, .{ .parent = null }),
|
||||||
.open_config => try rt_app.performAction(.app, .open_config, {}),
|
.open_config => try rt_app.performAction(.app, .open_config, {}),
|
||||||
.reload_config => try self.reloadConfig(rt_app),
|
.reload_config => try rt_app.performAction(.app, .reload_config, .{}),
|
||||||
.close_all_windows => try rt_app.performAction(.app, .close_all_windows, {}),
|
.close_all_windows => try rt_app.performAction(.app, .close_all_windows, {}),
|
||||||
.toggle_quick_terminal => try rt_app.performAction(.app, .toggle_quick_terminal, {}),
|
.toggle_quick_terminal => try rt_app.performAction(.app, .toggle_quick_terminal, {}),
|
||||||
.toggle_visibility => try rt_app.performAction(.app, .toggle_visibility, {}),
|
.toggle_visibility => try rt_app.performAction(.app, .toggle_visibility, {}),
|
||||||
@ -462,10 +454,6 @@ fn hasSurface(self: *const App, surface: *const Surface) bool {
|
|||||||
|
|
||||||
/// The message types that can be sent to the app thread.
|
/// The message types that can be sent to the app thread.
|
||||||
pub const Message = union(enum) {
|
pub const Message = union(enum) {
|
||||||
/// Reload the configuration for the entire app and propagate it to
|
|
||||||
/// all the active surfaces.
|
|
||||||
reload_config: void,
|
|
||||||
|
|
||||||
// Open the configuration file
|
// Open the configuration file
|
||||||
open_config: void,
|
open_config: void,
|
||||||
|
|
||||||
|
@ -1065,8 +1065,8 @@ fn updateRendererHealth(self: *Surface, health: renderer.Health) void {
|
|||||||
fn notifyConfigConditionalState(self: *Surface) void {
|
fn notifyConfigConditionalState(self: *Surface) void {
|
||||||
self.rt_app.performAction(
|
self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.config_change_conditional_state,
|
.reload_config,
|
||||||
{},
|
.{ .soft = true },
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
log.warn("failed to notify app of config state change err={}", .{err});
|
log.warn("failed to notify app of config state change err={}", .{err});
|
||||||
};
|
};
|
||||||
@ -1077,8 +1077,22 @@ fn notifyConfigConditionalState(self: *Surface) void {
|
|||||||
/// or other surfaces.
|
/// or other surfaces.
|
||||||
pub fn updateConfig(
|
pub fn updateConfig(
|
||||||
self: *Surface,
|
self: *Surface,
|
||||||
config: *const configpkg.Config,
|
original: *const configpkg.Config,
|
||||||
) !void {
|
) !void {
|
||||||
|
// Apply our conditional state. If we fail to apply the conditional state
|
||||||
|
// then we log and attempt to move forward with the old config.
|
||||||
|
var config_: ?configpkg.Config = original.changeConditionalState(
|
||||||
|
self.config_conditional_state,
|
||||||
|
) catch |err| err: {
|
||||||
|
log.warn("failed to apply conditional state to config err={}", .{err});
|
||||||
|
break :err null;
|
||||||
|
};
|
||||||
|
defer if (config_) |*c| c.deinit();
|
||||||
|
|
||||||
|
// We want a config pointer for everything so we get that either
|
||||||
|
// based on our conditional state or the original config.
|
||||||
|
const config: *const configpkg.Config = if (config_) |*c| c else original;
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -194,12 +194,14 @@ 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
|
/// A request to reload the configuration. The reload request can be
|
||||||
/// the configuration should be reloaded. The apprt doesn't need
|
/// from a user or for some internal reason. The reload request may
|
||||||
/// to do a full physical reload; it should call the
|
/// request it is a soft reload or a full reload. See the struct for
|
||||||
/// `changeConditionalState` function and then `updateConfig`
|
/// more documentation.
|
||||||
/// on the app or surface.
|
///
|
||||||
config_change_conditional_state,
|
/// The configuration should be passed to updateConfig either at the
|
||||||
|
/// app or surface level depending on the target.
|
||||||
|
reload_config: ReloadConfig,
|
||||||
|
|
||||||
/// The configuration has changed. The value is a pointer to the new
|
/// The configuration has changed. The value is a pointer to the new
|
||||||
/// configuration. The pointer is only valid for the duration of the
|
/// configuration. The pointer is only valid for the duration of the
|
||||||
@ -250,7 +252,7 @@ pub const Action = union(Key) {
|
|||||||
secure_input,
|
secure_input,
|
||||||
key_sequence,
|
key_sequence,
|
||||||
color_change,
|
color_change,
|
||||||
config_change_conditional_state,
|
reload_config,
|
||||||
config_change,
|
config_change,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -514,6 +516,13 @@ pub const ColorKind = enum(c_int) {
|
|||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ReloadConfig = extern struct {
|
||||||
|
/// A soft reload means that the configuration doesn't need to be
|
||||||
|
/// read off disk, but libghostty needs the full config again so call
|
||||||
|
/// updateConfig with it.
|
||||||
|
soft: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
pub const ConfigChange = struct {
|
pub const ConfigChange = struct {
|
||||||
config: *const configpkg.Config,
|
config: *const configpkg.Config,
|
||||||
|
|
||||||
|
@ -47,11 +47,6 @@ pub const App = struct {
|
|||||||
/// Callback called to handle an action.
|
/// Callback called to handle an action.
|
||||||
action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.C) void,
|
action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.C) void,
|
||||||
|
|
||||||
/// Reload the configuration and return the new configuration.
|
|
||||||
/// The old configuration can be freed immediately when this is
|
|
||||||
/// called.
|
|
||||||
reload_config: *const fn (AppUD) callconv(.C) ?*const Config,
|
|
||||||
|
|
||||||
/// Read the clipboard value. The return value must be preserved
|
/// Read the clipboard value. The return value must be preserved
|
||||||
/// by the host until the next call. If there is no valid clipboard
|
/// by the host until the next call. If there is no valid clipboard
|
||||||
/// value then this should return null.
|
/// value then this should return null.
|
||||||
@ -375,16 +370,6 @@ pub const App = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reloadConfig(self: *App) !?*const Config {
|
|
||||||
// Reload
|
|
||||||
if (self.opts.reload_config(self.opts.userdata)) |new| {
|
|
||||||
self.config = new;
|
|
||||||
return self.config;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wakeup(self: App) void {
|
pub fn wakeup(self: App) void {
|
||||||
self.opts.wakeup(self.opts.userdata);
|
self.opts.wakeup(self.opts.userdata);
|
||||||
}
|
}
|
||||||
@ -465,30 +450,6 @@ 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 => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1374,10 +1335,14 @@ pub const CAPI = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reload the configuration.
|
/// Update the configuration to the provided config. This will propagate
|
||||||
export fn ghostty_app_reload_config(v: *App) void {
|
/// to all surfaces as well.
|
||||||
_ = v.core_app.reloadConfig(v) catch |err| {
|
export fn ghostty_app_update_config(
|
||||||
log.err("error reloading config err={}", .{err});
|
v: *App,
|
||||||
|
config: *const Config,
|
||||||
|
) void {
|
||||||
|
v.core_app.updateConfig(v, config) catch |err| {
|
||||||
|
log.err("error updating config err={}", .{err});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1434,6 +1399,17 @@ pub const CAPI = struct {
|
|||||||
return surface.newSurfaceOptions();
|
return surface.newSurfaceOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the configuration to the provided config for only this surface.
|
||||||
|
export fn ghostty_surface_update_config(
|
||||||
|
surface: *Surface,
|
||||||
|
config: *const Config,
|
||||||
|
) void {
|
||||||
|
surface.core_surface.updateConfig(config) catch |err| {
|
||||||
|
log.err("error updating config err={}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the surface needs to confirm quitting.
|
/// Returns true if the surface needs to confirm quitting.
|
||||||
export fn ghostty_surface_needs_confirm_quit(surface: *Surface) bool {
|
export fn ghostty_surface_needs_confirm_quit(surface: *Surface) bool {
|
||||||
return surface.core_surface.needsConfirmQuit();
|
return surface.core_surface.needsConfirmQuit();
|
||||||
|
@ -200,6 +200,8 @@ pub const App = struct {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.reload_config => try self.reloadConfig(target, value),
|
||||||
|
|
||||||
// Unimplemented
|
// Unimplemented
|
||||||
.new_split,
|
.new_split,
|
||||||
.goto_split,
|
.goto_split,
|
||||||
@ -225,7 +227,6 @@ pub const App = struct {
|
|||||||
.renderer_health,
|
.renderer_health,
|
||||||
.color_change,
|
.color_change,
|
||||||
.pwd,
|
.pwd,
|
||||||
.config_change_conditional_state,
|
|
||||||
.config_change,
|
.config_change,
|
||||||
=> log.info("unimplemented action={}", .{action}),
|
=> log.info("unimplemented action={}", .{action}),
|
||||||
}
|
}
|
||||||
@ -236,16 +237,34 @@ pub const App = struct {
|
|||||||
/// successful return.
|
/// successful return.
|
||||||
///
|
///
|
||||||
/// The returned pointer value is only valid for a stable self pointer.
|
/// The returned pointer value is only valid for a stable self pointer.
|
||||||
pub fn reloadConfig(self: *App) !?*const Config {
|
fn reloadConfig(
|
||||||
|
self: *App,
|
||||||
|
target: apprt.action.Target,
|
||||||
|
opts: apprt.action.ReloadConfig,
|
||||||
|
) !void {
|
||||||
|
if (opts.soft) {
|
||||||
|
switch (target) {
|
||||||
|
.app => try self.app.updateConfig(self, &self.config),
|
||||||
|
.surface => |core_surface| try core_surface.updateConfig(
|
||||||
|
&self.config,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Load our configuration
|
// Load our configuration
|
||||||
var config = try Config.load(self.app.alloc);
|
var config = try Config.load(self.app.alloc);
|
||||||
errdefer config.deinit();
|
errdefer config.deinit();
|
||||||
|
|
||||||
|
// Call into our app to update
|
||||||
|
switch (target) {
|
||||||
|
.app => try self.app.updateConfig(self, &config),
|
||||||
|
.surface => |core_surface| try core_surface.updateConfig(&config),
|
||||||
|
}
|
||||||
|
|
||||||
// Update the existing config, be sure to clean up the old one.
|
// Update the existing config, be sure to clean up the old one.
|
||||||
self.config.deinit();
|
self.config.deinit();
|
||||||
self.config = config;
|
self.config = config;
|
||||||
|
|
||||||
return &self.config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle the window to fullscreen mode.
|
/// Toggle the window to fullscreen mode.
|
||||||
|
@ -462,7 +462,8 @@ pub fn performAction(
|
|||||||
.equalize_splits => self.equalizeSplits(target),
|
.equalize_splits => self.equalizeSplits(target),
|
||||||
.goto_split => self.gotoSplit(target, value),
|
.goto_split => self.gotoSplit(target, value),
|
||||||
.open_config => try configpkg.edit.open(self.core_app.alloc),
|
.open_config => try configpkg.edit.open(self.core_app.alloc),
|
||||||
.config_change_conditional_state => self.configChangeConditionalState(target),
|
.config_change => self.configChange(value.config),
|
||||||
|
.reload_config => try self.reloadConfig(target, value),
|
||||||
.inspector => self.controlInspector(target, value),
|
.inspector => self.controlInspector(target, value),
|
||||||
.desktop_notification => self.showDesktopNotification(target, value),
|
.desktop_notification => self.showDesktopNotification(target, value),
|
||||||
.set_title => try self.setTitle(target, value),
|
.set_title => try self.setTitle(target, value),
|
||||||
@ -488,7 +489,6 @@ pub fn performAction(
|
|||||||
.render_inspector,
|
.render_inspector,
|
||||||
.renderer_health,
|
.renderer_health,
|
||||||
.color_change,
|
.color_change,
|
||||||
.config_change,
|
|
||||||
=> log.warn("unimplemented action={}", .{action}),
|
=> log.warn("unimplemented action={}", .{action}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -818,48 +818,9 @@ fn showDesktopNotification(
|
|||||||
c.g_application_send_notification(g_app, n.body.ptr, notification);
|
c.g_application_send_notification(g_app, n.body.ptr, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configChangeConditionalState(
|
fn configChange(self: *App, new_config: *const Config) void {
|
||||||
self: *App,
|
_ = new_config;
|
||||||
target: apprt.Target,
|
|
||||||
) void {
|
|
||||||
const surface: *CoreSurface = switch (target) {
|
|
||||||
.app => return,
|
|
||||||
.surface => |v| v,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer new_config.deinit();
|
|
||||||
|
|
||||||
// Update our surface.
|
|
||||||
surface.updateConfig(&new_config) catch |err| {
|
|
||||||
log.warn("error updating surface config for state change err={}", .{err});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reload the configuration. This should return the new configuration.
|
|
||||||
/// The old value can be freed immediately at this point assuming a
|
|
||||||
/// successful return.
|
|
||||||
///
|
|
||||||
/// The returned pointer value is only valid for a stable self pointer.
|
|
||||||
pub fn reloadConfig(self: *App) !?*const Config {
|
|
||||||
// Load our configuration
|
|
||||||
var config = try Config.load(self.core_app.alloc);
|
|
||||||
errdefer config.deinit();
|
|
||||||
|
|
||||||
// Update the existing config, be sure to clean up the old one.
|
|
||||||
self.config.deinit();
|
|
||||||
self.config = config;
|
|
||||||
self.syncConfigChanges() catch |err| {
|
self.syncConfigChanges() catch |err| {
|
||||||
log.warn("error handling configuration changes err={}", .{err});
|
log.warn("error handling configuration changes err={}", .{err});
|
||||||
};
|
};
|
||||||
@ -870,8 +831,36 @@ pub fn reloadConfig(self: *App) !?*const Config {
|
|||||||
if (surface.container.window()) |window| window.onConfigReloaded();
|
if (surface.container.window()) |window| window.onConfigReloaded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &self.config;
|
pub fn reloadConfig(
|
||||||
|
self: *App,
|
||||||
|
target: apprt.action.Target,
|
||||||
|
opts: apprt.action.ReloadConfig,
|
||||||
|
) !void {
|
||||||
|
if (opts.soft) {
|
||||||
|
switch (target) {
|
||||||
|
.app => try self.core_app.updateConfig(self, &self.config),
|
||||||
|
.surface => |core_surface| try core_surface.updateConfig(
|
||||||
|
&self.config,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load our configuration
|
||||||
|
var config = try Config.load(self.core_app.alloc);
|
||||||
|
errdefer config.deinit();
|
||||||
|
|
||||||
|
// Call into our app to update
|
||||||
|
switch (target) {
|
||||||
|
.app => try self.core_app.updateConfig(self, &config),
|
||||||
|
.surface => |core_surface| try core_surface.updateConfig(&config),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the existing config, be sure to clean up the old one.
|
||||||
|
self.config.deinit();
|
||||||
|
self.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call this anytime the configuration changes.
|
/// Call this anytime the configuration changes.
|
||||||
@ -1439,9 +1428,9 @@ fn gtkActionReloadConfig(
|
|||||||
ud: ?*anyopaque,
|
ud: ?*anyopaque,
|
||||||
) callconv(.C) void {
|
) callconv(.C) void {
|
||||||
const self: *App = @ptrCast(@alignCast(ud orelse return));
|
const self: *App = @ptrCast(@alignCast(ud orelse return));
|
||||||
_ = self.core_app.mailbox.push(.{
|
self.reloadConfig(.app, .{}) catch |err| {
|
||||||
.reload_config = {},
|
log.err("error reloading configuration: {}", .{err});
|
||||||
}, .{ .forever = {} });
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gtkActionQuit(
|
fn gtkActionQuit(
|
||||||
|
@ -203,7 +203,7 @@ const ButtonsView = struct {
|
|||||||
|
|
||||||
fn gtkReloadClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
fn gtkReloadClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
||||||
const self: *ConfigErrors = @ptrCast(@alignCast(ud));
|
const self: *ConfigErrors = @ptrCast(@alignCast(ud));
|
||||||
_ = self.app.reloadConfig() catch |err| {
|
self.app.reloadConfig(.app, .{}) catch |err| {
|
||||||
log.warn("error reloading config error={}", .{err});
|
log.warn("error reloading config error={}", .{err});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -2752,17 +2752,19 @@ pub fn finalize(self: *Config) !void {
|
|||||||
// We always load the theme first because it may set other fields
|
// We always load the theme first because it may set other fields
|
||||||
// in our config.
|
// in our config.
|
||||||
if (self.theme) |theme| {
|
if (self.theme) |theme| {
|
||||||
// If we have different light vs dark mode themes, disable
|
const different = !std.mem.eql(u8, theme.light, theme.dark);
|
||||||
// window-theme = auto since that breaks it.
|
|
||||||
if (!std.mem.eql(u8, theme.light, theme.dark)) {
|
|
||||||
// This setting doesn't make sense with different light/dark themes
|
|
||||||
// because it'll force the theme based on the Ghostty theme.
|
|
||||||
if (self.@"window-theme" == .auto) self.@"window-theme" = .system;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warning: loadTheme will deinit our existing config and replace
|
// Warning: loadTheme will deinit our existing config and replace
|
||||||
// it so all memory from self prior to this point will be freed.
|
// it so all memory from self prior to this point will be freed.
|
||||||
try self.loadTheme(theme);
|
try self.loadTheme(theme);
|
||||||
|
|
||||||
|
// If we have different light vs dark mode themes, disable
|
||||||
|
// window-theme = auto since that breaks it.
|
||||||
|
if (different) {
|
||||||
|
// This setting doesn't make sense with different light/dark themes
|
||||||
|
// because it'll force the theme based on the Ghostty theme.
|
||||||
|
if (self.@"window-theme" == .auto) self.@"window-theme" = .system;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const alloc = self._arena.?.allocator();
|
const alloc = self._arena.?.allocator();
|
||||||
@ -5380,3 +5382,21 @@ test "theme loading correct light/dark" {
|
|||||||
}, new.background);
|
}, new.background);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "theme specifying light/dark changes window-theme from auto" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
{
|
||||||
|
var cfg = try Config.default(alloc);
|
||||||
|
defer cfg.deinit();
|
||||||
|
var it: TestIterator = .{ .data = &.{
|
||||||
|
"--theme=light:foo,dark:bar",
|
||||||
|
"--window-theme=auto",
|
||||||
|
} };
|
||||||
|
try cfg.loadIter(alloc, &it);
|
||||||
|
try cfg.finalize();
|
||||||
|
|
||||||
|
try testing.expect(cfg.@"window-theme" == .system);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user