gtk/SurfaceScrolledWindow: wrap root child with another Adw.Bin (#12426)

Due to a known Gtk issue, the scrolled_window at the root of the
template is free-ed twice on dispose. This causes crashes when used with
GNOME 49 platform (Gtk 4.20, libadwaita 1.8.5).

Workaround this issue by wrapping the root child in another Adw.Bin,
similar to widgets like ResizeOverlay.

LLM was used to perform discovery against a manually recorded Valgrind
trace, and helped tracking down known fixes for this problem. The
comment in code was taken from another instance in the repository.

Fixes https://github.com/ghostty-org/ghostty/discussions/12306

Assisted-by: OpenAI GPT-5.4
This commit is contained in:
Mitchell Hashimoto
2026-04-25 09:22:59 -07:00
committed by GitHub
2 changed files with 10 additions and 6 deletions

View File

@@ -163,8 +163,7 @@ pub const SurfaceScrolledWindow = extern struct {
_: ?*anyopaque,
) callconv(.c) void {
const priv = self.private();
const child: *gtk.Widget = self.as(Parent).getChild().?;
const scrolled_window = gobject.ext.cast(gtk.ScrolledWindow, child).?;
const scrolled_window = self.private().scrolled_window.as(gtk.ScrolledWindow);
scrolled_window.setChild(if (priv.surface) |s| s.as(gtk.Widget) else null);
// Unbind old config binding if it exists

View File

@@ -3,9 +3,14 @@ using Adw 1;
template $GhostttySurfaceScrolledWindow: Adw.Bin {
notify::surface => $notify_surface();
Gtk.ScrolledWindow scrolled_window {
hscrollbar-policy: never;
vscrollbar-policy: bind $scrollbar_policy(template.config) as <Gtk.PolicyType>;
// The double-nesting is required due to a GTK bug where you can't
// bind the first child of a builder layout. If you do, you get a double
// dispose. Easiest way to see that is simply remove this and see the
// GTK critical errors (and sometimes crashes).
Adw.Bin {
Gtk.ScrolledWindow scrolled_window {
hscrollbar-policy: never;
vscrollbar-policy: bind $scrollbar_policy(template.config) as <Gtk.PolicyType>;
}
}
}