mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
apprt/gtk: update app color scheme state
Fixes #2781 This commit contains two separate changes but very related: 1. We update the color scheme state of the app on app start. This is necessary so that the configuration properly reflects the conditional state of the theme at the app level (i.e. the window headerbar). 2. We take ownership of the new config when it changes, matching macOS. This ensures that things like our GTK headerbar update when the theme changes but more generally whenever any config changes. And some housekeeping: - We remove runtime CSS setup from init. We can do it on the first tick of `run` instead. This will probably save some CPU cycles especially when we're just notifying a single instance to create a new window. - I moved dbus event setup to `run` as well. We don't need to know these events unless we're actually running the app. Similar to the above, should save some CPU time on single instance runs.
This commit is contained in:
@ -385,22 +385,6 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
|||||||
if (config.@"initial-window")
|
if (config.@"initial-window")
|
||||||
c.g_application_activate(gapp);
|
c.g_application_activate(gapp);
|
||||||
|
|
||||||
// Register for dbus events
|
|
||||||
if (c.g_application_get_dbus_connection(gapp)) |dbus_connection| {
|
|
||||||
_ = c.g_dbus_connection_signal_subscribe(
|
|
||||||
dbus_connection,
|
|
||||||
null,
|
|
||||||
"org.freedesktop.portal.Settings",
|
|
||||||
"SettingChanged",
|
|
||||||
"/org/freedesktop/portal/desktop",
|
|
||||||
"org.freedesktop.appearance",
|
|
||||||
c.G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE,
|
|
||||||
>kNotifyColorScheme,
|
|
||||||
core_app,
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internally, GTK ensures that only one instance of this provider exists in the provider list
|
// Internally, GTK ensures that only one instance of this provider exists in the provider list
|
||||||
// for the display.
|
// for the display.
|
||||||
const css_provider = c.gtk_css_provider_new();
|
const css_provider = c.gtk_css_provider_new();
|
||||||
@ -409,12 +393,6 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
|||||||
@ptrCast(css_provider),
|
@ptrCast(css_provider),
|
||||||
c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 3,
|
c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 3,
|
||||||
);
|
);
|
||||||
loadRuntimeCss(core_app.alloc, &config, css_provider) catch |err| switch (err) {
|
|
||||||
error.OutOfMemory => log.warn(
|
|
||||||
"out of memory loading runtime CSS, no runtime CSS applied",
|
|
||||||
.{},
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.core_app = core_app,
|
.core_app = core_app,
|
||||||
@ -831,14 +809,20 @@ fn configChange(
|
|||||||
target: apprt.Target,
|
target: apprt.Target,
|
||||||
new_config: *const Config,
|
new_config: *const Config,
|
||||||
) void {
|
) void {
|
||||||
_ = new_config;
|
|
||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
// We don't do anything for surface config change events. There
|
// We don't do anything for surface config change events. There
|
||||||
// is nothing to sync with regards to a surface today.
|
// is nothing to sync with regards to a surface today.
|
||||||
.surface => {},
|
.surface => {},
|
||||||
|
|
||||||
.app => {
|
.app => {
|
||||||
|
// We clone (to take ownership) and update our configuration.
|
||||||
|
if (new_config.clone(self.core_app.alloc)) |config_clone| {
|
||||||
|
self.config.deinit();
|
||||||
|
self.config = config_clone;
|
||||||
|
} else |err| {
|
||||||
|
log.warn("error cloning configuration err={}", .{err});
|
||||||
|
}
|
||||||
|
|
||||||
self.syncConfigChanges() catch |err| {
|
self.syncConfigChanges() catch |err| {
|
||||||
log.warn("error handling configuration changes err={}", .{err});
|
log.warn("error handling configuration changes err={}", .{err});
|
||||||
};
|
};
|
||||||
@ -892,7 +876,7 @@ fn syncConfigChanges(self: *App) !void {
|
|||||||
|
|
||||||
// Load our runtime CSS. If this fails then our window is just stuck
|
// Load our runtime CSS. If this fails then our window is just stuck
|
||||||
// with the old CSS but we don't want to fail the entire sync operation.
|
// with the old CSS but we don't want to fail the entire sync operation.
|
||||||
loadRuntimeCss(self.core_app.alloc, &self.config, self.css_provider) catch |err| switch (err) {
|
self.loadRuntimeCss() catch |err| switch (err) {
|
||||||
error.OutOfMemory => log.warn(
|
error.OutOfMemory => log.warn(
|
||||||
"out of memory loading runtime CSS, no runtime CSS applied",
|
"out of memory loading runtime CSS, no runtime CSS applied",
|
||||||
.{},
|
.{},
|
||||||
@ -956,15 +940,14 @@ fn syncActionAccelerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn loadRuntimeCss(
|
fn loadRuntimeCss(
|
||||||
alloc: Allocator,
|
self: *const App,
|
||||||
config: *const Config,
|
|
||||||
provider: *c.GtkCssProvider,
|
|
||||||
) Allocator.Error!void {
|
) Allocator.Error!void {
|
||||||
var stack_alloc = std.heap.stackFallback(4096, alloc);
|
var stack_alloc = std.heap.stackFallback(4096, self.core_app.alloc);
|
||||||
var buf = std.ArrayList(u8).init(stack_alloc.get());
|
var buf = std.ArrayList(u8).init(stack_alloc.get());
|
||||||
defer buf.deinit();
|
defer buf.deinit();
|
||||||
const writer = buf.writer();
|
const writer = buf.writer();
|
||||||
|
|
||||||
|
const config: *const Config = &self.config;
|
||||||
const window_theme = config.@"window-theme";
|
const window_theme = config.@"window-theme";
|
||||||
const unfocused_fill: Config.Color = config.@"unfocused-split-fill" orelse config.background;
|
const unfocused_fill: Config.Color = config.@"unfocused-split-fill" orelse config.background;
|
||||||
const headerbar_background = config.background;
|
const headerbar_background = config.background;
|
||||||
@ -1027,7 +1010,7 @@ fn loadRuntimeCss(
|
|||||||
|
|
||||||
// Clears any previously loaded CSS from this provider
|
// Clears any previously loaded CSS from this provider
|
||||||
c.gtk_css_provider_load_from_data(
|
c.gtk_css_provider_load_from_data(
|
||||||
provider,
|
self.css_provider,
|
||||||
buf.items.ptr,
|
buf.items.ptr,
|
||||||
@intCast(buf.items.len),
|
@intCast(buf.items.len),
|
||||||
);
|
);
|
||||||
@ -1076,11 +1059,17 @@ pub fn run(self: *App) !void {
|
|||||||
self.transient_cgroup_base = path;
|
self.transient_cgroup_base = path;
|
||||||
} else log.debug("cgroup isolation disabled config={}", .{self.config.@"linux-cgroup"});
|
} else log.debug("cgroup isolation disabled config={}", .{self.config.@"linux-cgroup"});
|
||||||
|
|
||||||
|
// Setup our D-Bus connection for listening to settings changes.
|
||||||
|
self.initDbus();
|
||||||
|
|
||||||
// Setup our menu items
|
// Setup our menu items
|
||||||
self.initActions();
|
self.initActions();
|
||||||
self.initMenu();
|
self.initMenu();
|
||||||
self.initContextMenu();
|
self.initContextMenu();
|
||||||
|
|
||||||
|
// Setup our initial color scheme
|
||||||
|
self.colorSchemeEvent(self.getColorScheme());
|
||||||
|
|
||||||
// On startup, we want to check for configuration errors right away
|
// On startup, we want to check for configuration errors right away
|
||||||
// so we can show our error window. We also need to setup other initial
|
// so we can show our error window. We also need to setup other initial
|
||||||
// state.
|
// state.
|
||||||
@ -1114,6 +1103,26 @@ pub fn run(self: *App) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn initDbus(self: *App) void {
|
||||||
|
const dbus = c.g_application_get_dbus_connection(@ptrCast(self.app)) orelse {
|
||||||
|
log.warn("unable to get dbus connection, not setting up events", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
_ = c.g_dbus_connection_signal_subscribe(
|
||||||
|
dbus,
|
||||||
|
null,
|
||||||
|
"org.freedesktop.portal.Settings",
|
||||||
|
"SettingChanged",
|
||||||
|
"/org/freedesktop/portal/desktop",
|
||||||
|
"org.freedesktop.appearance",
|
||||||
|
c.G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE,
|
||||||
|
>kNotifyColorScheme,
|
||||||
|
self,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// This timeout function is started when no surfaces are open. It can be
|
// This timeout function is started when no surfaces are open. It can be
|
||||||
// cancelled if a new surface is opened before the timer expires.
|
// cancelled if a new surface is opened before the timer expires.
|
||||||
pub fn gtkQuitTimerExpired(ud: ?*anyopaque) callconv(.C) c.gboolean {
|
pub fn gtkQuitTimerExpired(ud: ?*anyopaque) callconv(.C) c.gboolean {
|
||||||
@ -1394,7 +1403,7 @@ fn gtkNotifyColorScheme(
|
|||||||
parameters: ?*c.GVariant,
|
parameters: ?*c.GVariant,
|
||||||
user_data: ?*anyopaque,
|
user_data: ?*anyopaque,
|
||||||
) callconv(.C) void {
|
) callconv(.C) void {
|
||||||
const core_app: *CoreApp = @ptrCast(@alignCast(user_data orelse {
|
const self: *App = @ptrCast(@alignCast(user_data orelse {
|
||||||
log.err("style change notification: userdata is null", .{});
|
log.err("style change notification: userdata is null", .{});
|
||||||
return;
|
return;
|
||||||
}));
|
}));
|
||||||
@ -1426,9 +1435,20 @@ fn gtkNotifyColorScheme(
|
|||||||
else
|
else
|
||||||
.light;
|
.light;
|
||||||
|
|
||||||
for (core_app.surfaces.items) |surface| {
|
self.colorSchemeEvent(color_scheme);
|
||||||
surface.core_surface.colorSchemeCallback(color_scheme) catch |err| {
|
}
|
||||||
log.err("unable to tell surface about color scheme change: {}", .{err});
|
|
||||||
|
fn colorSchemeEvent(
|
||||||
|
self: *App,
|
||||||
|
scheme: apprt.ColorScheme,
|
||||||
|
) void {
|
||||||
|
self.core_app.colorSchemeEvent(self, scheme) catch |err| {
|
||||||
|
log.err("error updating app color scheme err={}", .{err});
|
||||||
|
};
|
||||||
|
|
||||||
|
for (self.core_app.surfaces.items) |surface| {
|
||||||
|
surface.core_surface.colorSchemeCallback(scheme) catch |err| {
|
||||||
|
log.err("unable to tell surface about color scheme change err={}", .{err});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user