From 2a5b7aab86e4c0b5327f53a6aa7affde45be05d5 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Fri, 8 Aug 2025 12:57:39 -0500 Subject: [PATCH 1/6] gtk-ng: don't use signals to toggle command palette --- src/apprt/gtk-ng/class/command_palette.zig | 18 +++++++------- src/apprt/gtk-ng/class/surface.zig | 22 +---------------- src/apprt/gtk-ng/class/window.zig | 26 ++++----------------- src/apprt/gtk-ng/ui/1.5/command-palette.blp | 1 + 4 files changed, 17 insertions(+), 50 deletions(-) diff --git a/src/apprt/gtk-ng/class/command_palette.zig b/src/apprt/gtk-ng/class/command_palette.zig index ee10989b7..eb00f6df4 100644 --- a/src/apprt/gtk-ng/class/command_palette.zig +++ b/src/apprt/gtk-ng/class/command_palette.zig @@ -174,11 +174,14 @@ pub const CommandPalette = extern struct { } } + fn dialogClosed(_: adw.Dialog, self: *CommandPalette) callconv(.c) void { + self.unref(); + } + fn searchStopped(_: *gtk.SearchEntry, self: *CommandPalette) callconv(.c) void { // ESC was pressed - close the palette const priv = self.private(); _ = priv.dialog.close(); - self.unref(); } fn searchActivated(_: *gtk.SearchEntry, self: *CommandPalette) callconv(.c) void { @@ -198,11 +201,9 @@ pub const CommandPalette = extern struct { pub fn toggle(self: *CommandPalette, window: *Window) void { const priv = self.private(); - // If the dialog has been shown, close it and unref ourselves so all of - // our memory is reclaimed. + // If the dialog has been shown, close it. if (priv.dialog.as(gtk.Widget).getRealized() != 0) { _ = priv.dialog.close(); - self.unref(); return; } @@ -216,6 +217,10 @@ pub const CommandPalette = extern struct { /// Helper function to send a signal containing the action that should be /// performed. fn activated(self: *CommandPalette, pos: c_uint) void { + // add a reference to keep ourselves around until we're done + _ = self.ref(); + defer self.unref(); + const priv = self.private(); // Close before running the action in order to avoid being replaced by @@ -224,10 +229,6 @@ pub const CommandPalette = extern struct { // and cannot receive focus when reopened. _ = priv.dialog.close(); - // We are always done with the command palette when this finishes, even - // if there were errors. - defer self.unref(); - // Use priv.model and not priv.source here to use the list of *visible* results const object = priv.model.as(gio.ListModel).getObject(pos) orelse return; defer object.unref(); @@ -277,6 +278,7 @@ pub const CommandPalette = extern struct { class.bindTemplateChildPrivate("source", .{}); // Template Callbacks + class.bindTemplateCallback("closed", &dialogClosed); class.bindTemplateCallback("notify_config", &propConfig); class.bindTemplateCallback("search_stopped", &searchStopped); class.bindTemplateCallback("search_activated", &searchActivated); diff --git a/src/apprt/gtk-ng/class/surface.zig b/src/apprt/gtk-ng/class/surface.zig index 05393bd4f..2891840a0 100644 --- a/src/apprt/gtk-ng/class/surface.zig +++ b/src/apprt/gtk-ng/class/surface.zig @@ -361,19 +361,6 @@ pub const Surface = extern struct { void, ); }; - - /// Emitted when this surface requests that the command palette be - /// toggled. - pub const @"toggle-command-palette" = struct { - pub const name = "toggle-command-palette"; - pub const connect = impl.connect; - const impl = gobject.ext.defineSignal( - name, - Self, - &.{}, - void, - ); - }; }; const Private = struct { @@ -564,13 +551,7 @@ pub const Surface = extern struct { } pub fn toggleCommandPalette(self: *Self) bool { - signals.@"toggle-command-palette".impl.emit( - self, - null, - .{}, - null, - ); - return true; + return self.as(gtk.Widget).activateAction("win.toggle-command-palette", null) != 0; } /// Set the current progress report state. @@ -2423,7 +2404,6 @@ pub const Surface = extern struct { signals.@"present-request".impl.register(.{}); signals.@"toggle-fullscreen".impl.register(.{}); signals.@"toggle-maximize".impl.register(.{}); - signals.@"toggle-command-palette".impl.register(.{}); // Virtual methods gobject.Object.virtual_methods.dispose.implement(class, &dispose); diff --git a/src/apprt/gtk-ng/class/window.zig b/src/apprt/gtk-ng/class/window.zig index f3e8ee129..ee5753560 100644 --- a/src/apprt/gtk-ng/class/window.zig +++ b/src/apprt/gtk-ng/class/window.zig @@ -714,13 +714,6 @@ pub const Window = extern struct { self, .{}, ); - _ = Surface.signals.@"toggle-command-palette".connect( - surface, - *Self, - surfaceToggleCommandPalette, - self, - .{}, - ); // If we've never had a surface initialize yet, then we register // this signal. Its theoretically possible to launch multiple surfaces @@ -1529,15 +1522,6 @@ pub const Window = extern struct { // We react to the changes in the propMaximized callback } - /// React to a signal from a surface requesting that the command palette - /// be toggled. - fn surfaceToggleCommandPalette( - _: *Surface, - self: *Self, - ) callconv(.c) void { - self.toggleCommandPalette(); - } - fn surfaceInit( surface: *Surface, self: *Self, @@ -1722,7 +1706,7 @@ pub const Window = extern struct { fn toggleCommandPalette(self: *Window) void { const priv = self.private(); // Get a reference to a command palette. First check the weak reference - // that we save to see if we already have stored. If we don't then + // that we save to see if we already have one stored. If we don't then // create a new one. const command_palette = gobject.ext.cast(CommandPalette, priv.command_palette.get()) orelse command_palette: { // Create a fresh command palette. @@ -1747,14 +1731,14 @@ pub const Window = extern struct { .{}, ); + // Save a weak reference to the command palette. We use a weak reference to avoid + // reference counting cycles that might cause problems later. + priv.command_palette.set(command_palette.as(gobject.Object)); + break :command_palette command_palette; }; defer command_palette.unref(); - // Save a weak reference to the command palette. We use a weak reference to avoid - // reference counting cycles that might cause problems later. - priv.command_palette.set(command_palette.as(gobject.Object)); - // Tell the command palette to toggle itself. If the dialog gets // presented (instead of hidden) it will be modal over our window. command_palette.toggle(self); diff --git a/src/apprt/gtk-ng/ui/1.5/command-palette.blp b/src/apprt/gtk-ng/ui/1.5/command-palette.blp index 0ccae1f0a..473fb1f06 100644 --- a/src/apprt/gtk-ng/ui/1.5/command-palette.blp +++ b/src/apprt/gtk-ng/ui/1.5/command-palette.blp @@ -4,6 +4,7 @@ using Adw 1; Adw.Dialog dialog { content-width: 700; + closed => $closed(); Adw.ToolbarView { top-bar-style: flat; From 8af1230228ef4d3f592cfc21a662e18a0e6c97a1 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Fri, 8 Aug 2025 13:49:13 -0500 Subject: [PATCH 2/6] gtk-ng: don't add extra refs when activating a command in the palette --- src/apprt/gtk-ng/class/command_palette.zig | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/apprt/gtk-ng/class/command_palette.zig b/src/apprt/gtk-ng/class/command_palette.zig index eb00f6df4..5d00cf446 100644 --- a/src/apprt/gtk-ng/class/command_palette.zig +++ b/src/apprt/gtk-ng/class/command_palette.zig @@ -217,24 +217,19 @@ pub const CommandPalette = extern struct { /// Helper function to send a signal containing the action that should be /// performed. fn activated(self: *CommandPalette, pos: c_uint) void { - // add a reference to keep ourselves around until we're done - _ = self.ref(); - defer self.unref(); - const priv = self.private(); + // Use priv.model and not priv.source here to use the list of *visible* results + const object_ = priv.model.as(gio.ListModel).getObject(pos); + defer if (object_) |object| object.unref(); + // Close before running the action in order to avoid being replaced by // another dialog (such as the change title dialog). If that occurs then // the command palette dialog won't be counted as having closed properly // and cannot receive focus when reopened. _ = priv.dialog.close(); - // Use priv.model and not priv.source here to use the list of *visible* results - const object = priv.model.as(gio.ListModel).getObject(pos) orelse return; - defer object.unref(); - - const cmd = gobject.ext.cast(Command, object) orelse return; - + const cmd = gobject.ext.cast(Command, object_ orelse return) orelse return; const action = cmd.getAction() orelse return; // Signal that an an action has been selected. Signals are synchronous From 3221421a74dbc1d4fb432b45110b595ce6ee1081 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Fri, 8 Aug 2025 13:51:51 -0500 Subject: [PATCH 3/6] gtk-ng: add TODOs about passing surface that toggled command palette --- src/apprt/gtk-ng/class/surface.zig | 1 + src/apprt/gtk-ng/class/window.zig | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/apprt/gtk-ng/class/surface.zig b/src/apprt/gtk-ng/class/surface.zig index 2891840a0..8487b24b0 100644 --- a/src/apprt/gtk-ng/class/surface.zig +++ b/src/apprt/gtk-ng/class/surface.zig @@ -551,6 +551,7 @@ pub const Surface = extern struct { } pub fn toggleCommandPalette(self: *Self) bool { + // TODO: pass the surface with the action return self.as(gtk.Widget).activateAction("win.toggle-command-palette", null) != 0; } diff --git a/src/apprt/gtk-ng/class/window.zig b/src/apprt/gtk-ng/class/window.zig index ee5753560..fe9a2d7b6 100644 --- a/src/apprt/gtk-ng/class/window.zig +++ b/src/apprt/gtk-ng/class/window.zig @@ -343,6 +343,7 @@ pub const Window = extern struct { .{ "paste", actionPaste, null }, .{ "reset", actionReset, null }, .{ "clear", actionClear, null }, + // TODO: accept the surface that toggled the command palette .{ "toggle-command-palette", actionToggleCommandPalette, null }, }; @@ -1703,6 +1704,8 @@ pub const Window = extern struct { } /// Toggle the command palette. + /// + /// TODO: accept the surface that toggled the command palette as a parameter fn toggleCommandPalette(self: *Window) void { const priv = self.private(); // Get a reference to a command palette. First check the weak reference @@ -1756,6 +1759,8 @@ pub const Window = extern struct { _: ?*glib.Variant, self: *Window, ) callconv(.c) void { + // TODO: accept the surface that toggled the command palette as a + // parameter self.toggleCommandPalette(); } From 5c088d10a495cb04ec1b83204c62fda8e86b64af Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Fri, 8 Aug 2025 21:07:54 -0500 Subject: [PATCH 4/6] gtk-ng: fix memory leaks in command palette --- src/apprt/gtk-ng/class/command_palette.zig | 14 +++++++++----- src/apprt/gtk-ng/class/window.zig | 11 +++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/apprt/gtk-ng/class/command_palette.zig b/src/apprt/gtk-ng/class/command_palette.zig index 5d00cf446..8b7bb328c 100644 --- a/src/apprt/gtk-ng/class/command_palette.zig +++ b/src/apprt/gtk-ng/class/command_palette.zig @@ -174,14 +174,18 @@ pub const CommandPalette = extern struct { } } - fn dialogClosed(_: adw.Dialog, self: *CommandPalette) callconv(.c) void { + fn close(self: *CommandPalette) void { + const priv = self.private(); + _ = priv.dialog.close(); + } + + fn dialogClosed(_: *adw.Dialog, self: *CommandPalette) callconv(.c) void { self.unref(); } fn searchStopped(_: *gtk.SearchEntry, self: *CommandPalette) callconv(.c) void { // ESC was pressed - close the palette - const priv = self.private(); - _ = priv.dialog.close(); + self.close(); } fn searchActivated(_: *gtk.SearchEntry, self: *CommandPalette) callconv(.c) void { @@ -203,7 +207,7 @@ pub const CommandPalette = extern struct { // If the dialog has been shown, close it. if (priv.dialog.as(gtk.Widget).getRealized() != 0) { - _ = priv.dialog.close(); + self.close(); return; } @@ -227,7 +231,7 @@ pub const CommandPalette = extern struct { // another dialog (such as the change title dialog). If that occurs then // the command palette dialog won't be counted as having closed properly // and cannot receive focus when reopened. - _ = priv.dialog.close(); + self.close(); const cmd = gobject.ext.cast(Command, object_ orelse return) orelse return; const action = cmd.getAction() orelse return; diff --git a/src/apprt/gtk-ng/class/window.zig b/src/apprt/gtk-ng/class/window.zig index fe9a2d7b6..ae953eee0 100644 --- a/src/apprt/gtk-ng/class/window.zig +++ b/src/apprt/gtk-ng/class/window.zig @@ -1064,10 +1064,18 @@ pub const Window = extern struct { fn dispose(self: *Self) callconv(.c) void { const priv = self.private(); + + command_palette: { + // TODO: this can be simplified once WeakRef.get() can return a null. + const command_palette = gobject.ext.cast(CommandPalette, priv.command_palette.get()) orelse break :command_palette; + command_palette.unref(); + } + if (priv.config) |v| { v.unref(); priv.config = null; } + priv.tab_bindings.setSource(null); gtk.Widget.disposeTemplate( @@ -1708,9 +1716,12 @@ pub const Window = extern struct { /// TODO: accept the surface that toggled the command palette as a parameter fn toggleCommandPalette(self: *Window) void { const priv = self.private(); + // Get a reference to a command palette. First check the weak reference // that we save to see if we already have one stored. If we don't then // create a new one. + // + // TODO: once WeakRef.get() can return a null this will need to be fixed up. const command_palette = gobject.ext.cast(CommandPalette, priv.command_palette.get()) orelse command_palette: { // Create a fresh command palette. const command_palette = CommandPalette.new(); From 2de0c108ba0448b391eb65b80533bd323ddd2fd3 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Fri, 8 Aug 2025 21:45:41 -0500 Subject: [PATCH 5/6] gtk-ng: better handling of weak references The upstream GIR for g_weak_ref_get is incorrect - it does not allow the returned value to be NULL. This PR pulls in a new version of our GObject bindings with that patched and improves the safety of dealing with the command palette weak reference held by the window. See ianprime0509/zig-gobject#117 --- build.zig.zon | 4 ++-- build.zig.zon.json | 6 +++--- build.zig.zon.nix | 6 +++--- build.zig.zon.txt | 2 +- flatpak/zig-packages.json | 6 +++--- src/apprt/gtk-ng/class/window.zig | 14 ++++++-------- 6 files changed, 18 insertions(+), 20 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index ee283870b..55a693496 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -55,8 +55,8 @@ .gobject = .{ // https://github.com/jcollie/ghostty-gobject based on zig_gobject // Temporary until we generate them at build time automatically. - .url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst", - .hash = "gobject-0.3.0-Skun7F_XnABQYabYdzLoVbO3bCcJIwxE3NCPs1_fG2ma", + .url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst", + .hash = "gobject-0.3.0-Skun7AngnABC2BPiaoobs6YSSzSgMuEIcjb2rYrRyaAM", .lazy = true, }, diff --git a/build.zig.zon.json b/build.zig.zon.json index 190fc1cd2..24f1053ba 100644 --- a/build.zig.zon.json +++ b/build.zig.zon.json @@ -24,10 +24,10 @@ "url": "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz", "hash": "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U=" }, - "gobject-0.3.0-Skun7F_XnABQYabYdzLoVbO3bCcJIwxE3NCPs1_fG2ma": { + "gobject-0.3.0-Skun7AngnABC2BPiaoobs6YSSzSgMuEIcjb2rYrRyaAM": { "name": "gobject", - "url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst", - "hash": "sha256-43IIiHR5J7PfgG9JXSlGgC6WztC10fXyIhGZfY9xceQ=" + "url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst", + "hash": "sha256-B0ziLzKud+kdKu5T1BTE9GMh8EPM/KhhhoNJlys5QPI=" }, "N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr": { "name": "gtk4_layer_shell", diff --git a/build.zig.zon.nix b/build.zig.zon.nix index 3708d61ed..380bafaeb 100644 --- a/build.zig.zon.nix +++ b/build.zig.zon.nix @@ -122,11 +122,11 @@ in }; } { - name = "gobject-0.3.0-Skun7F_XnABQYabYdzLoVbO3bCcJIwxE3NCPs1_fG2ma"; + name = "gobject-0.3.0-Skun7AngnABC2BPiaoobs6YSSzSgMuEIcjb2rYrRyaAM"; path = fetchZigArtifact { name = "gobject"; - url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst"; - hash = "sha256-43IIiHR5J7PfgG9JXSlGgC6WztC10fXyIhGZfY9xceQ="; + url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst"; + hash = "sha256-B0ziLzKud+kdKu5T1BTE9GMh8EPM/KhhhoNJlys5QPI="; }; } { diff --git a/build.zig.zon.txt b/build.zig.zon.txt index 2f3a9cab9..14bb0e8df 100644 --- a/build.zig.zon.txt +++ b/build.zig.zon.txt @@ -27,7 +27,7 @@ https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d6 https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz -https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst +https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst https://github.com/mbadolato/iTerm2-Color-Schemes/archive/3cbeca99efa10beba24b0efe86331736f09f9ed1.tar.gz https://github.com/mitchellh/libxev/archive/7f803181b158a10fec8619f793e3b4df515566cb.tar.gz https://github.com/mitchellh/zig-objc/archive/c9e917a4e15a983b672ca779c7985d738a2d517c.tar.gz diff --git a/flatpak/zig-packages.json b/flatpak/zig-packages.json index 56f4dc6a4..d50371f5f 100644 --- a/flatpak/zig-packages.json +++ b/flatpak/zig-packages.json @@ -31,9 +31,9 @@ }, { "type": "archive", - "url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-07-34-1/ghostty-gobject-0.14.1-2025-08-07-34-1.tar.zst", - "dest": "vendor/p/gobject-0.3.0-Skun7F_XnABQYabYdzLoVbO3bCcJIwxE3NCPs1_fG2ma", - "sha256": "e3720888747927b3df806f495d2946802e96ced0b5d1f5f22211997d8f7171e4" + "url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.1-2025-08-09-37-1/ghostty-gobject-0.14.1-2025-08-09-37-1.tar.zst", + "dest": "vendor/p/gobject-0.3.0-Skun7AngnABC2BPiaoobs6YSSzSgMuEIcjb2rYrRyaAM", + "sha256": "074ce22f32ae77e91d2aee53d414c4f46321f043ccfca861868349972b3940f2" }, { "type": "archive", diff --git a/src/apprt/gtk-ng/class/window.zig b/src/apprt/gtk-ng/class/window.zig index ae953eee0..47f95aece 100644 --- a/src/apprt/gtk-ng/class/window.zig +++ b/src/apprt/gtk-ng/class/window.zig @@ -1065,11 +1065,7 @@ pub const Window = extern struct { fn dispose(self: *Self) callconv(.c) void { const priv = self.private(); - command_palette: { - // TODO: this can be simplified once WeakRef.get() can return a null. - const command_palette = gobject.ext.cast(CommandPalette, priv.command_palette.get()) orelse break :command_palette; - command_palette.unref(); - } + if (priv.command_palette.get()) |object| object.unref(); if (priv.config) |v| { v.unref(); @@ -1720,9 +1716,11 @@ pub const Window = extern struct { // Get a reference to a command palette. First check the weak reference // that we save to see if we already have one stored. If we don't then // create a new one. - // - // TODO: once WeakRef.get() can return a null this will need to be fixed up. - const command_palette = gobject.ext.cast(CommandPalette, priv.command_palette.get()) orelse command_palette: { + const command_palette = command_palette: { + if (priv.command_palette.get()) |object| not_command_palette: { + break :command_palette gobject.ext.cast(CommandPalette, object) orelse break :not_command_palette; + } + // Create a fresh command palette. const command_palette = CommandPalette.new(); From 5bb88d259c11160973e1bd7dd0643c13e6c0ec47 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sat, 9 Aug 2025 11:12:06 -0500 Subject: [PATCH 6/6] gtk-ng: use WeakRef helper for type safety --- src/apprt/gtk-ng/class/application.zig | 2 +- src/apprt/gtk-ng/class/split_tree.zig | 2 +- src/apprt/gtk-ng/class/window.zig | 13 +++++-------- src/apprt/gtk-ng/weak_ref.zig | 11 ++++------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/apprt/gtk-ng/class/application.zig b/src/apprt/gtk-ng/class/application.zig index eda2a49eb..4a14434fa 100644 --- a/src/apprt/gtk-ng/class/application.zig +++ b/src/apprt/gtk-ng/class/application.zig @@ -133,7 +133,7 @@ pub const Application = extern struct { /// If non-null, we're currently showing a config errors dialog. /// This is a WeakRef because the dialog can close on its own /// outside of our own lifecycle and that's okay. - config_errors_dialog: WeakRef(ConfigErrorsDialog) = .{}, + config_errors_dialog: WeakRef(ConfigErrorsDialog) = .empty, /// glib source for our signal handler. signal_source: ?c_uint = null, diff --git a/src/apprt/gtk-ng/class/split_tree.zig b/src/apprt/gtk-ng/class/split_tree.zig index 3018afdb4..5eb0a5472 100644 --- a/src/apprt/gtk-ng/class/split_tree.zig +++ b/src/apprt/gtk-ng/class/split_tree.zig @@ -119,7 +119,7 @@ pub const SplitTree = extern struct { /// Last focused surface in the tree. We need this to handle various /// tree change states. - last_focused: WeakRef(Surface) = .{}, + last_focused: WeakRef(Surface) = .empty, /// The source that we use to rebuild the tree. This is also /// used to debounce updates. diff --git a/src/apprt/gtk-ng/class/window.zig b/src/apprt/gtk-ng/class/window.zig index 47f95aece..eb41b61d0 100644 --- a/src/apprt/gtk-ng/class/window.zig +++ b/src/apprt/gtk-ng/class/window.zig @@ -28,6 +28,7 @@ const Surface = @import("surface.zig").Surface; const Tab = @import("tab.zig").Tab; const DebugWarning = @import("debug_warning.zig").DebugWarning; const CommandPalette = @import("command_palette.zig").CommandPalette; +const WeakRef = @import("../weak_ref.zig").WeakRef; const log = std.log.scoped(.gtk_ghostty_window); @@ -249,7 +250,7 @@ pub const Window = extern struct { tab_overview_focus_timer: ?c_uint = null, /// A weak reference to a command palette. - command_palette: gobject.WeakRef = std.mem.zeroes(gobject.WeakRef), + command_palette: WeakRef(CommandPalette) = .empty, // Template bindings tab_overview: *adw.TabOverview, @@ -1065,7 +1066,7 @@ pub const Window = extern struct { fn dispose(self: *Self) callconv(.c) void { const priv = self.private(); - if (priv.command_palette.get()) |object| object.unref(); + priv.command_palette.set(null); if (priv.config) |v| { v.unref(); @@ -1716,11 +1717,7 @@ pub const Window = extern struct { // Get a reference to a command palette. First check the weak reference // that we save to see if we already have one stored. If we don't then // create a new one. - const command_palette = command_palette: { - if (priv.command_palette.get()) |object| not_command_palette: { - break :command_palette gobject.ext.cast(CommandPalette, object) orelse break :not_command_palette; - } - + const command_palette = priv.command_palette.get() orelse command_palette: { // Create a fresh command palette. const command_palette = CommandPalette.new(); @@ -1745,7 +1742,7 @@ pub const Window = extern struct { // Save a weak reference to the command palette. We use a weak reference to avoid // reference counting cycles that might cause problems later. - priv.command_palette.set(command_palette.as(gobject.Object)); + priv.command_palette.set(command_palette); break :command_palette command_palette; }; diff --git a/src/apprt/gtk-ng/weak_ref.zig b/src/apprt/gtk-ng/weak_ref.zig index 7ee5cf730..f689e45fa 100644 --- a/src/apprt/gtk-ng/weak_ref.zig +++ b/src/apprt/gtk-ng/weak_ref.zig @@ -10,6 +10,8 @@ pub fn WeakRef(comptime T: type) type { ref: gobject.WeakRef = std.mem.zeroes(gobject.WeakRef), + pub const empty: Self = .{}; + /// Set the weak reference to the given object. This will not /// increase the reference count of the object. pub fn set(self: *Self, v_: ?*T) void { @@ -23,14 +25,9 @@ pub fn WeakRef(comptime T: type) type { /// Get a strong reference to the object, or null if the object /// has been finalized. This increases the reference count by one. pub fn get(self: *Self) ?*T { - // The GIR of g_weak_ref_get has a bug where the optional - // is not encoded. Or, it may be a bug in zig-gobject. - const obj_: ?*gobject.Object = @ptrCast(self.ref.get()); - const obj = obj_ orelse return null; - // We can't use `as` because `as` guarantees conversion and // that can't be statically guaranteed. - return gobject.ext.cast(T, obj); + return gobject.ext.cast(T, self.ref.get() orelse return null); } }; } @@ -38,7 +35,7 @@ pub fn WeakRef(comptime T: type) type { test WeakRef { const testing = std.testing; - var ref: WeakRef(gtk.TextBuffer) = .{}; + var ref: WeakRef(gtk.TextBuffer) = .empty; const obj: *gtk.TextBuffer = .new(null); ref.set(obj); ref.get().?.unref(); // The "?" asserts non-null