mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-09-14 18:01:58 +00:00
gtk: fix stale pointers to property bindings
Previously, the property binding created in `Surface.bindIsSplit` would get freed automatically when the source object (the SplitTree widget) got finalized. A subsequent call to `bindIsSplit` could then cause a crash by using the stale pointer to the binding. This bug could e.g. be triggered by dragging the surface from a single-surface tab to another tab. We now create an extra reference to the binding object so that Surface essentially owns the binding and is responsible for freeing it. Updated the binding in `SurfaceScrolledWindow` to use the same pattern. That one was probably fine, because the binding is only created once, but let's be safe.
This commit is contained in:
@@ -853,14 +853,24 @@ pub const Surface = extern struct {
|
||||
|
||||
pub fn bindIsSplit(self: *Self, tree: *SplitTree) void {
|
||||
const priv = self.private();
|
||||
if (priv.is_split_binding) |bind| bind.unbind();
|
||||
if (priv.is_split_binding) |binding| {
|
||||
binding.unbind();
|
||||
binding.unref();
|
||||
priv.is_split_binding = null;
|
||||
}
|
||||
|
||||
priv.is_split_binding = tree.as(gobject.Object).bindProperty(
|
||||
const binding = tree.as(gobject.Object).bindProperty(
|
||||
"is-split",
|
||||
self.as(gobject.Object),
|
||||
"is-split",
|
||||
.{ .sync_create = true },
|
||||
);
|
||||
// The ref created by bindProperty is owned by the binding itself.
|
||||
// We need another ref to prevent the binding object from being
|
||||
// freed if the source object (SplitTree) is finalized. Otherwise
|
||||
// our pointer to the binding could become stale.
|
||||
binding.ref();
|
||||
priv.is_split_binding = binding;
|
||||
}
|
||||
|
||||
/// Callback used to determine whether unfocused-split-fill / unfocused-split-opacity
|
||||
@@ -1881,6 +1891,12 @@ pub const Surface = extern struct {
|
||||
priv.config = null;
|
||||
}
|
||||
|
||||
if (priv.is_split_binding) |binding| {
|
||||
binding.unbind();
|
||||
binding.unref();
|
||||
priv.is_split_binding = null;
|
||||
}
|
||||
|
||||
if (priv.vadj_signal_group) |group| {
|
||||
group.setTarget(null);
|
||||
group.as(gobject.Object).unref();
|
||||
|
||||
@@ -89,7 +89,8 @@ pub const SurfaceScrolledWindow = extern struct {
|
||||
const priv = self.private();
|
||||
|
||||
if (priv.config_binding) |binding| {
|
||||
binding.as(gobject.Object).unref();
|
||||
binding.unbind();
|
||||
binding.unref();
|
||||
priv.config_binding = null;
|
||||
}
|
||||
|
||||
@@ -168,18 +169,23 @@ pub const SurfaceScrolledWindow = extern struct {
|
||||
|
||||
// Unbind old config binding if it exists
|
||||
if (priv.config_binding) |binding| {
|
||||
binding.as(gobject.Object).unref();
|
||||
binding.unbind();
|
||||
binding.unref();
|
||||
priv.config_binding = null;
|
||||
}
|
||||
|
||||
// Bind config from surface to our config property
|
||||
if (priv.surface) |surface| {
|
||||
priv.config_binding = surface.as(gobject.Object).bindProperty(
|
||||
const binding = surface.as(gobject.Object).bindProperty(
|
||||
properties.config.name,
|
||||
self.as(gobject.Object),
|
||||
properties.config.name,
|
||||
.{ .sync_create = true },
|
||||
);
|
||||
// Keep another ref, otherwise the binding would be freed and
|
||||
// our pointer become stale if the surface gets finalized.
|
||||
binding.ref();
|
||||
priv.config_binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user