mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
apprt/gtk: report proper app focus state
This commit is contained in:
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
|||||||
c.gtk_get_minor_version(),
|
c.gtk_get_minor_version(),
|
||||||
c.gtk_get_micro_version(),
|
c.gtk_get_micro_version(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (version.atLeast(4, 16, 0)) {
|
if (version.atLeast(4, 16, 0)) {
|
||||||
// From gtk 4.16, GDK_DEBUG is split into GDK_DEBUG and GDK_DISABLE
|
// From gtk 4.16, GDK_DEBUG is split into GDK_DEBUG and GDK_DISABLE
|
||||||
_ = internal_os.setenv("GDK_DISABLE", "gles-api");
|
_ = internal_os.setenv("GDK_DISABLE", "gles-api");
|
||||||
@ -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(>kWindowAdded),
|
||||||
|
core_app,
|
||||||
|
null,
|
||||||
|
c.G_CONNECT_DEFAULT,
|
||||||
|
);
|
||||||
|
_ = c.g_signal_connect_data(
|
||||||
|
app,
|
||||||
|
"window-removed",
|
||||||
|
c.G_CALLBACK(>kWindowRemoved),
|
||||||
|
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(>kWindowIsActive),
|
||||||
|
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 {
|
||||||
|
Reference in New Issue
Block a user