aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/apprt/gtk/class/window.zig
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-01 23:05:23 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-01 23:05:51 +0300
commit5bd21b7ed468b5ff4d1a5b2ed7e211f401eb138a (patch)
tree672fa4625cca0ecb3e9376fc7f8e33d7b0e9deef /vendor/libghostty-vt/src/apprt/gtk/class/window.zig
parent32abb3776668ee768200019c170956f67fe33a86 (diff)
chore: update libghostty vendor
refs #276
Diffstat (limited to 'vendor/libghostty-vt/src/apprt/gtk/class/window.zig')
-rw-r--r--vendor/libghostty-vt/src/apprt/gtk/class/window.zig87
1 files changed, 68 insertions, 19 deletions
diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/window.zig b/vendor/libghostty-vt/src/apprt/gtk/class/window.zig
index 0c8dfaa7..2a1a5435 100644
--- a/vendor/libghostty-vt/src/apprt/gtk/class/window.zig
+++ b/vendor/libghostty-vt/src/apprt/gtk/class/window.zig
@@ -220,6 +220,9 @@ pub const Window = extern struct {
/// behaves slightly differently under certain scenarios.
quick_terminal: bool = false,
+ /// Timeout source to react to this window becoming (in)active.
+ handle_active_state_source: ?c_uint = null,
+
/// The window decoration override. If this is not set then we'll
/// inherit whatever the config has. This allows overriding the
/// config on a per-window basis.
@@ -855,6 +858,38 @@ pub const Window = extern struct {
}
}
+ /// Callback to handle this window becoming active or inactive.
+ /// Triggered by propIsActive with a timeout to debounce temporary
+ /// changes in active state.
+ fn handleActiveState(ud: ?*anyopaque) callconv(.c) c_int {
+ const self: *Self = @ptrCast(@alignCast(ud orelse return 0));
+ const priv = self.private();
+ priv.handle_active_state_source = null;
+
+ // Hide quick-terminal if set to autohide
+ if (self.isQuickTerminal()) {
+ if (self.getConfig()) |cfg| {
+ if (cfg.get().@"quick-terminal-autohide" and
+ self.as(gtk.Window).isActive() == 0 and
+ self.as(gtk.Widget).isVisible() == 1)
+ {
+ self.toggleVisibility();
+ }
+ }
+ }
+
+ // Don't change urgency if we're not the active window.
+ if (self.as(gtk.Window).isActive() == 0) return 0;
+
+ self.winproto().setUrgent(false) catch |err| {
+ log.warn(
+ "winproto failed to reset urgency={}",
+ .{err},
+ );
+ };
+ return 0;
+ }
+
//---------------------------------------------------------------
// Properties
@@ -1076,27 +1111,34 @@ pub const Window = extern struct {
_: *gobject.ParamSpec,
self: *Self,
) callconv(.c) void {
- // Hide quick-terminal if set to autohide
- if (self.isQuickTerminal()) {
- if (self.getConfig()) |cfg| {
- if (cfg.get().@"quick-terminal-autohide" and
- self.as(gtk.Window).isActive() == 0 and
- self.as(gtk.Widget).isVisible() == 1)
- {
- self.toggleVisibility();
- }
- }
- }
-
- // Don't change urgency if we're not the active window.
- if (self.as(gtk.Window).isActive() == 0) return;
+ const priv = self.private();
- self.winproto().setUrgent(false) catch |err| {
- log.warn(
- "winproto failed to reset urgency={}",
- .{err},
+ // Use a timeout callback to wait for focus state to settle,
+ // because depending on the windowing backend the window might
+ // become inactive and immediately active again. This happens
+ // e.g. on Wayland when opening a context menu or a submenu
+ // inside a context menu.
+ if (priv.handle_active_state_source == null) {
+ priv.handle_active_state_source = glib.timeoutAddFull(
+ // Use priority of an idle callback instead of the higher
+ // default timeout priority. This allows us to use a shorter
+ // timeout duration.
+ glib.PRIORITY_DEFAULT_IDLE,
+ // 50ms was chosen to be conservative. From testing we know
+ // that, depending on the backend and system performance, a
+ // shorter timeout or just an idle callback can be enough for
+ // the focus to settle. On the other hand a delay of e.g. 10ms
+ // does not work reliably on some slow systems. The downside
+ // of a high value is that some operations in handleActiveState,
+ // e.g. hiding the quick-terminal, will be visibly delayed.
+ // However, 50ms should barely be noticeable. We can change
+ // this in the future if necessary.
+ 50,
+ handleActiveState,
+ self,
+ null,
);
- };
+ }
}
fn propGdkSurfaceDims(
@@ -1215,6 +1257,13 @@ pub const Window = extern struct {
fn dispose(self: *Self) callconv(.c) void {
const priv = self.private();
+ if (priv.handle_active_state_source) |v| {
+ if (glib.Source.remove(v) == 0) {
+ log.warn("unable to remove handle active state source", .{});
+ }
+ priv.handle_active_state_source = null;
+ }
+
priv.command_palette.set(null);
if (priv.config) |v| {