From 4e39144d39c0dbb88ebc0060dffc3b145556df3c Mon Sep 17 00:00:00 2001 From: Leorize Date: Tue, 3 Jun 2025 01:34:37 -0500 Subject: [PATCH] gtk/TabView: do not closeTab within close-page signal handler `TabView` assumes to be the sole owner of all `Tab`s within a Window. As such, it could close the managed `Window` once all tabs are removed from its widget. However, during `AdwTabView::close-page` signal triggered by libadwaita, the `Tab` to be closed will gain an another reference for the duration of the signal, breaking `TabView.closeTab` (called via `Tab.closeWithConfirmation`) assumptions that having no tabs meant they are all destroyed. This commit solves the issue by scheduling `Tab.closeWithConfirmation` to be run after `AdwTabView::close-page` signal has finished processing. --- src/apprt/gtk/TabView.zig | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/apprt/gtk/TabView.zig b/src/apprt/gtk/TabView.zig index 29a069a6d..8a4145b5f 100644 --- a/src/apprt/gtk/TabView.zig +++ b/src/apprt/gtk/TabView.zig @@ -7,6 +7,7 @@ const std = @import("std"); const gtk = @import("gtk"); const adw = @import("adw"); const gobject = @import("gobject"); +const glib = @import("glib"); const Window = @import("Window.zig"); const Tab = @import("Tab.zig"); @@ -243,7 +244,14 @@ fn adwClosePage( const child = page.getChild().as(gobject.Object); const tab: *Tab = @ptrCast(@alignCast(child.getData(Tab.GHOSTTY_TAB) orelse return 0)); self.tab_view.closePageFinish(page, @intFromBool(self.forcing_close)); - if (!self.forcing_close) tab.closeWithConfirmation(); + if (!self.forcing_close) { + // We cannot trigger a close directly in here as the page will stay + // alive until this handler returns, breaking the assumption where + // no pages means they are all destroyed. + // + // Schedule the close request to happen in the next event cycle. + _ = glib.idleAddOnce(glibIdleOnceCloseTab, tab); + } return 1; } @@ -269,3 +277,8 @@ fn adwSelectPage(_: *adw.TabView, _: *gobject.ParamSpec, self: *TabView) callcon const title = page.getTitle(); self.window.setTitle(std.mem.span(title)); } + +fn glibIdleOnceCloseTab(data: ?*anyopaque) callconv(.c) void { + const tab: *Tab = @ptrCast(@alignCast(data orelse return)); + tab.closeWithConfirmation(); +}