apprt/gtk: report proper app focus state

This commit is contained in:
Mitchell Hashimoto
2024-10-06 13:30:53 -07:00
parent e56cfbdc8b
commit 494fedca2f
2 changed files with 88 additions and 1 deletions

View File

@ -288,6 +288,9 @@ pub fn setQuit(self: *App) !void {
/// This is separate from surface focus events. See the `focused` /// This is separate from surface focus events. See the `focused`
/// field for more information. /// field for more information.
pub fn focusEvent(self: *App, focused: bool) void { pub fn focusEvent(self: *App, focused: bool) void {
// Prevent redundant focus events
if (self.focused == focused) return;
log.debug("focus event focused={}", .{focused}); log.debug("focus event focused={}", .{focused});
self.focused = focused; self.focused = focused;
} }

View File

@ -235,6 +235,24 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
c.G_CONNECT_DEFAULT, c.G_CONNECT_DEFAULT,
); );
// Other signals
_ = c.g_signal_connect_data(
app,
"window-added",
c.G_CALLBACK(&gtkWindowAdded),
core_app,
null,
c.G_CONNECT_DEFAULT,
);
_ = c.g_signal_connect_data(
app,
"window-removed",
c.G_CALLBACK(&gtkWindowRemoved),
core_app,
null,
c.G_CONNECT_DEFAULT,
);
// We don't use g_application_run, we want to manually control the // We don't use g_application_run, we want to manually control the
// loop so we have to do the same things the run function does: // loop so we have to do the same things the run function does:
// https://github.com/GNOME/glib/blob/a8e8b742e7926e33eb635a8edceac74cf239d6ed/gio/gapplication.c#L2533 // https://github.com/GNOME/glib/blob/a8e8b742e7926e33eb635a8edceac74cf239d6ed/gio/gapplication.c#L2533
@ -1065,6 +1083,72 @@ fn gtkActivate(app: *c.GtkApplication, ud: ?*anyopaque) callconv(.C) void {
}, .{ .forever = {} }); }, .{ .forever = {} });
} }
fn gtkWindowAdded(
_: *c.GtkApplication,
window: *c.GtkWindow,
ud: ?*anyopaque,
) callconv(.C) void {
const core_app: *CoreApp = @ptrCast(@alignCast(ud orelse return));
// Request the is-active property change so we can detect
// when our app loses focus.
_ = c.g_signal_connect_data(
window,
"notify::is-active",
c.G_CALLBACK(&gtkWindowIsActive),
core_app,
null,
c.G_CONNECT_DEFAULT,
);
}
fn gtkWindowRemoved(
_: *c.GtkApplication,
_: *c.GtkWindow,
ud: ?*anyopaque,
) callconv(.C) void {
const core_app: *CoreApp = @ptrCast(@alignCast(ud orelse return));
// Recheck if we are focused
gtkWindowIsActive(null, undefined, core_app);
}
fn gtkWindowIsActive(
window: ?*c.GtkWindow,
_: *c.GParamSpec,
ud: ?*anyopaque,
) callconv(.C) void {
const core_app: *CoreApp = @ptrCast(@alignCast(ud orelse return));
// If our window is active, then we can tell the app
// that we are focused.
if (window) |w| {
if (c.gtk_window_is_active(w) == 1) {
core_app.focusEvent(true);
return;
}
}
// If the window becomes inactive, we need to check if any
// other windows are active. If not, then we are no longer
// focused.
if (c.gtk_window_list_toplevels()) |list| {
defer c.g_list_free(list);
var current: ?*c.GList = list;
while (current) |elem| : (current = elem.next) {
// If the window is active then we are still focused.
// This is another window since we did our check above.
// That window should trigger its own is-active
// callback so we don't need to call it here.
const w: *c.GtkWindow = @alignCast(@ptrCast(elem.data));
if (c.gtk_window_is_active(w) == 1) return;
}
}
// We are not focused
core_app.focusEvent(false);
}
/// Call a D-Bus method to determine the current color scheme. If there /// Call a D-Bus method to determine the current color scheme. If there
/// is any error at any point we'll log the error and return "light" /// is any error at any point we'll log the error and return "light"
pub fn getColorScheme(self: *App) apprt.ColorScheme { pub fn getColorScheme(self: *App) apprt.ColorScheme {