Renamed timeout source and callback function. Added comment explaining timeout delay.

pull/12843/head
Daniel Kinzler 2026-05-29 17:40:25 +02:00
parent 1753d57bfd
commit ff963f3119
1 changed files with 32 additions and 12 deletions

View File

@ -221,7 +221,7 @@ pub const Window = extern struct {
quick_terminal: bool = false, quick_terminal: bool = false,
/// Timeout source to react to this window becoming (in)active. /// Timeout source to react to this window becoming (in)active.
timeout: ?c_uint = null, handle_active_state_source: ?c_uint = null,
/// The window decoration override. If this is not set then we'll /// The window decoration override. If this is not set then we'll
/// inherit whatever the config has. This allows overriding the /// inherit whatever the config has. This allows overriding the
@ -858,10 +858,13 @@ pub const Window = extern struct {
} }
} }
fn onTimeout(ud: ?*anyopaque) callconv(.c) c_int { /// 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 self: *Self = @ptrCast(@alignCast(ud orelse return 0));
const priv = self.private(); const priv = self.private();
priv.timeout = null; priv.handle_active_state_source = null;
// Hide quick-terminal if set to autohide // Hide quick-terminal if set to autohide
if (self.isQuickTerminal()) { if (self.isQuickTerminal()) {
@ -1113,12 +1116,29 @@ pub const Window = extern struct {
// Use a timeout callback to wait for focus state to settle, // Use a timeout callback to wait for focus state to settle,
// because depending on the windowing backend the window might // because depending on the windowing backend the window might
// become inactive and immediately active again. This happens // become inactive and immediately active again. This happens
// e.g. on Wayland when opening a context menu. // e.g. on Wayland when opening a context menu or a submenu
if (priv.timeout == null) priv.timeout = glib.timeoutAdd( // inside a context menu.
100, if (priv.handle_active_state_source == null) {
onTimeout, priv.handle_active_state_source = glib.timeoutAddFull(
self, // 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( fn propGdkSurfaceDims(
@ -1237,11 +1257,11 @@ pub const Window = extern struct {
fn dispose(self: *Self) callconv(.c) void { fn dispose(self: *Self) callconv(.c) void {
const priv = self.private(); const priv = self.private();
if (priv.timeout) |v| { if (priv.handle_active_state_source) |v| {
if (glib.Source.remove(v) == 0) { if (glib.Source.remove(v) == 0) {
log.warn("unable to remove timeout source", .{}); log.warn("unable to remove handle active state source", .{});
} }
priv.timeout = null; priv.handle_active_state_source = null;
} }
priv.command_palette.set(null); priv.command_palette.set(null);