mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-15 03:52:39 +00:00
Enhance GTK settings handling with well-defined types and utility functions
This commit is contained in:
@@ -3,32 +3,143 @@ const builtin = @import("builtin");
|
||||
const gtk = @import("gtk");
|
||||
const gobject = @import("gobject");
|
||||
|
||||
/// Reads a GTK setting using the GTK Settings API.
|
||||
/// GTK Settings keys with well-defined types.
|
||||
pub const Key = enum {
|
||||
gtk_enable_primary_paste,
|
||||
gtk_xft_dpi,
|
||||
gtk_font_name,
|
||||
|
||||
fn Type(comptime self: Key) type {
|
||||
return switch (self) {
|
||||
.gtk_enable_primary_paste => bool,
|
||||
.gtk_xft_dpi => c_int,
|
||||
.gtk_font_name => []const u8,
|
||||
};
|
||||
}
|
||||
|
||||
fn GValueType(comptime self: Key) type {
|
||||
return switch (self) {
|
||||
// Booleans are stored as integers in GTK's internal representation
|
||||
.gtk_enable_primary_paste,
|
||||
=> c_int,
|
||||
|
||||
// Integer types
|
||||
.gtk_xft_dpi,
|
||||
=> c_int,
|
||||
|
||||
// String types (returned as null-terminated C strings from GTK)
|
||||
.gtk_font_name,
|
||||
=> ?[*:0]const u8,
|
||||
};
|
||||
}
|
||||
|
||||
fn propertyName(comptime self: Key) [*:0]const u8 {
|
||||
return switch (self) {
|
||||
.gtk_enable_primary_paste => "gtk-enable-primary-paste",
|
||||
.gtk_xft_dpi => "gtk-xft-dpi",
|
||||
.gtk_font_name => "gtk-font-name",
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true if this setting type requires memory allocation.
|
||||
/// this is defensive: types that do not need allocation need to be
|
||||
/// explicitly marked here
|
||||
fn requiresAllocation(comptime self: Key) bool {
|
||||
const T = self.Type();
|
||||
return switch (T) {
|
||||
bool, c_int => false,
|
||||
else => true,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Reads a GTK setting using the GTK Settings API for non-allocating types.
|
||||
/// This automatically uses XDG Desktop Portal in Flatpak environments.
|
||||
/// Returns null if not on a GTK-supported platform or if the setting cannot be read.
|
||||
///
|
||||
/// Supported platforms: Linux, FreeBSD
|
||||
/// Supported types: bool, c_int
|
||||
/// No allocator is required or used. Returns null if the setting is not available or cannot be read.
|
||||
///
|
||||
/// Example usage:
|
||||
/// const enabled = readSetting(bool, "gtk-enable-primary-paste");
|
||||
/// const dpi = readSetting(c_int, "gtk-xft-dpi");
|
||||
pub fn readSetting(comptime T: type, key: [*:0]const u8) ?T {
|
||||
// Only available on systems that use GTK (Linux, FreeBSD)
|
||||
if (comptime builtin.os.tag != .linux and builtin.os.tag != .freebsd) return null;
|
||||
|
||||
/// const enabled = get(.gtk_enable_primary_paste);
|
||||
/// const dpi = get(.gtk_xft_dpi);
|
||||
pub fn get(comptime key: Key) ?key.Type() {
|
||||
comptime {
|
||||
if (key.requiresAllocation()) {
|
||||
@compileError("Allocating types require an allocator; use getAlloc() instead");
|
||||
}
|
||||
}
|
||||
const settings = gtk.Settings.getDefault() orelse return null;
|
||||
return getImpl(settings, null, key) catch unreachable;
|
||||
}
|
||||
|
||||
// For bool and c_int, we use c_int as the underlying GObject type
|
||||
// because GTK boolean properties are stored as integers
|
||||
var value = gobject.ext.Value.new(c_int);
|
||||
/// Reads a GTK setting using the GTK Settings API, allocating if necessary.
|
||||
/// This automatically uses XDG Desktop Portal in Flatpak environments.
|
||||
///
|
||||
/// The caller must free any returned allocated memory with the provided allocator.
|
||||
/// Returns null if the setting is not available or cannot be read.
|
||||
/// May return an allocation error if memory allocation fails.
|
||||
///
|
||||
/// Example usage:
|
||||
/// const theme = try getAlloc(allocator, .gtk_theme_name);
|
||||
/// defer if (theme) |t| allocator.free(t);
|
||||
pub fn getAlloc(allocator: std.mem.Allocator, comptime key: Key) !?key.Type() {
|
||||
const settings = gtk.Settings.getDefault() orelse return null;
|
||||
return getImpl(settings, allocator, key);
|
||||
}
|
||||
|
||||
/// Shared implementation for reading GTK settings.
|
||||
/// If allocator is null, only non-allocating types can be used.
|
||||
/// Note: When adding a new type, research if it requires allocation (strings and boxed types do)
|
||||
/// if allocation is NOT needed, list it inside the switch statement in the function requiresAllocation()
|
||||
fn getImpl(settings: *gtk.Settings, allocator: ?std.mem.Allocator, comptime key: Key) !?key.Type() {
|
||||
const GValType = key.GValueType();
|
||||
var value = gobject.ext.Value.new(GValType);
|
||||
defer value.unset();
|
||||
|
||||
settings.as(gobject.Object).getProperty(key, &value);
|
||||
settings.as(gobject.Object).getProperty(key.propertyName(), &value);
|
||||
|
||||
return switch (T) {
|
||||
bool => value.getInt() != 0,
|
||||
c_int => value.getInt(),
|
||||
else => @compileError("Unsupported type '" ++ @typeName(T) ++ "' for GTK setting. Supported types: bool, c_int"),
|
||||
return switch (key) {
|
||||
// Booleans are stored as integers in GTK, convert to bool
|
||||
.gtk_enable_primary_paste,
|
||||
=> value.getInt() != 0,
|
||||
|
||||
// Integer types are returned directly
|
||||
.gtk_xft_dpi,
|
||||
=> value.getInt(),
|
||||
|
||||
// Strings: GTK owns the GValue's pointer, so we must duplicate it
|
||||
// before the GValue is destroyed by defer value.unset()
|
||||
.gtk_font_name,
|
||||
=> blk: {
|
||||
// This is defensive: we have already checked at compile-time that
|
||||
// an allocator is provided for allocating types
|
||||
const alloc = allocator.?;
|
||||
const ptr = value.getString() orelse break :blk null;
|
||||
const str = std.mem.span(ptr);
|
||||
break :blk try alloc.dupe(u8, str);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test "Key.Type returns correct types" {
|
||||
try std.testing.expectEqual(bool, Key.gtk_enable_primary_paste.Type());
|
||||
try std.testing.expectEqual(c_int, Key.gtk_xft_dpi.Type());
|
||||
try std.testing.expectEqual([]const u8, Key.gtk_font_name.Type());
|
||||
}
|
||||
|
||||
test "Key.requiresAllocation identifies allocating types" {
|
||||
try std.testing.expectEqual(false, Key.gtk_enable_primary_paste.requiresAllocation());
|
||||
try std.testing.expectEqual(false, Key.gtk_xft_dpi.requiresAllocation());
|
||||
try std.testing.expectEqual(true, Key.gtk_font_name.requiresAllocation());
|
||||
}
|
||||
|
||||
test "Key.GValueType returns correct GObject types" {
|
||||
try std.testing.expectEqual(c_int, Key.gtk_enable_primary_paste.GValueType());
|
||||
try std.testing.expectEqual(c_int, Key.gtk_xft_dpi.GValueType());
|
||||
try std.testing.expectEqual(?[*:0]const u8, Key.gtk_font_name.GValueType());
|
||||
}
|
||||
|
||||
test "Key.propertyName returns correct GTK property names" {
|
||||
try std.testing.expectEqualSlices(u8, "gtk-enable-primary-paste", std.mem.span(Key.gtk_enable_primary_paste.propertyName()));
|
||||
try std.testing.expectEqualSlices(u8, "gtk-xft-dpi", std.mem.span(Key.gtk_xft_dpi.propertyName()));
|
||||
try std.testing.expectEqualSlices(u8, "gtk-font-name", std.mem.span(Key.gtk_font_name.propertyName()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user