diff --git a/include/ghostty.h b/include/ghostty.h index d11b49fb1..94b69b829 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -301,6 +301,7 @@ 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_recursive_files(ghostty_config_t); void ghostty_config_finalize(ghostty_config_t); +bool ghostty_config_get(ghostty_config_t, void *, const char *, uintptr_t); ghostty_input_trigger_s ghostty_config_trigger(ghostty_config_t, const char *, uintptr_t); ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s *, ghostty_config_t); diff --git a/src/config.zig b/src/config.zig index 8d0dd2e44..23687d14b 100644 --- a/src/config.zig +++ b/src/config.zig @@ -1,5 +1,7 @@ +const builtin = @import("builtin"); + +pub usingnamespace @import("config/key.zig"); pub const Config = @import("config/Config.zig"); -pub const Key = @import("config/key.zig").Key; // Field types pub const CopyOnSelect = Config.CopyOnSelect; @@ -9,8 +11,10 @@ pub const OptionAsAlt = Config.OptionAsAlt; // Alternate APIs pub const CAPI = @import("config/CAPI.zig"); -pub const Wasm = @import("config/Wasm.zig"); +pub const Wasm = if (!builtin.target.isWasm()) struct {} else @import("config/Wasm.zig"); test { @import("std").testing.refAllDecls(@This()); + + _ = @import("config/c_get.zig"); } diff --git a/src/config/CAPI.zig b/src/config/CAPI.zig index 1d559a51e..cc031ad7a 100644 --- a/src/config/CAPI.zig +++ b/src/config/CAPI.zig @@ -4,6 +4,8 @@ const inputpkg = @import("../input.zig"); const global = &@import("../main.zig").state; const Config = @import("Config.zig"); +const c_get = @import("c_get.zig"); +const Key = @import("key.zig").Key; const log = std.log.scoped(.config); @@ -78,6 +80,16 @@ export fn ghostty_config_finalize(self: *Config) void { }; } +export fn ghostty_config_get( + self: *Config, + ptr: *anyopaque, + key_str: [*]const u8, + len: usize, +) bool { + const key = std.meta.stringToEnum(Key, key_str[0..len]) orelse return false; + return c_get.get(self, key, ptr); +} + export fn ghostty_config_trigger( self: *Config, str: [*]const u8, diff --git a/src/config/Config.zig b/src/config/Config.zig index 9868860a5..c37939d22 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -13,6 +13,7 @@ const internal_os = @import("../os/main.zig"); const cli_args = @import("../cli_args.zig"); const Key = @import("key.zig").Key; +const KeyValue = @import("key.zig").Value; const log = std.log.scoped(.config); diff --git a/src/config/c_get.zig b/src/config/c_get.zig new file mode 100644 index 000000000..05fa5fe7d --- /dev/null +++ b/src/config/c_get.zig @@ -0,0 +1,76 @@ +const std = @import("std"); + +const key = @import("key.zig"); +const Config = @import("Config.zig"); +const Key = key.Key; +const Value = key.Value; + +/// Get a value from the config by key into the given pointer. This is +/// specifically for C-compatible APIs. If you're using Zig, just access +/// the configuration directly. +/// +/// The return value is false if the given key is not supported by the +/// C API yet. This is a fixable problem so if it is important to support +/// some key, please open an issue. +pub fn get(config: *const Config, k: Key, ptr_raw: *anyopaque) bool { + @setEvalBranchQuota(10_000); + switch (k) { + inline else => |tag| { + const value = fieldByKey(config, tag); + switch (@TypeOf(value)) { + ?[:0]const u8 => { + const ptr: *[*c]const u8 = @ptrCast(@alignCast(ptr_raw)); + ptr.* = if (value) |slice| @ptrCast(slice.ptr) else null; + }, + + bool => { + const ptr: *bool = @ptrCast(@alignCast(ptr_raw)); + ptr.* = value; + }, + + u8, u32 => { + const ptr: *c_uint = @ptrCast(@alignCast(ptr_raw)); + ptr.* = @intCast(value); + }, + + f32, f64 => { + const ptr: *f64 = @ptrCast(@alignCast(ptr_raw)); + ptr.* = @floatCast(value); + }, + + else => return false, + } + + return true; + }, + } +} + +/// Get a value from the config by key. +fn fieldByKey(self: *const Config, comptime k: Key) Value(k) { + const field = comptime field: { + const fields = std.meta.fields(Config); + for (fields) |field| { + if (@field(Key, field.name) == k) { + break :field field; + } + } + + unreachable; + }; + + return @field(self, field.name); +} + +test "u8" { + const testing = std.testing; + const alloc = testing.allocator; + + var c = try Config.default(alloc); + defer c.deinit(); + c.@"font-size" = 24; + + var cval: c_uint = undefined; + try testing.expect(get(&c, .@"font-size", &cval)); + try testing.expectEqual(@as(c_uint, 24), cval); +} diff --git a/src/config/key.zig b/src/config/key.zig index bd50a684d..a6ad127c7 100644 --- a/src/config/key.zig +++ b/src/config/key.zig @@ -30,3 +30,26 @@ pub const Key = key: { }, }); }; + +/// Returns the value type for a key +pub fn Value(comptime key: Key) type { + const field = comptime field: { + const fields = std.meta.fields(Config); + for (fields) |field| { + if (@field(Key, field.name) == key) { + break :field field; + } + } + + unreachable; + }; + + return field.type; +} + +test "Value" { + const testing = std.testing; + + try testing.expectEqual(?[:0]const u8, Value(.@"font-family")); + try testing.expectEqual(bool, Value(.@"cursor-style-blink")); +}