From a262da92bfa6b67837b664bd5cf85615167528d4 Mon Sep 17 00:00:00 2001 From: Ronit Gandhi Date: Sat, 22 Feb 2025 10:49:27 +0530 Subject: [PATCH 1/9] using only note instead of both for docs. --- src/config/Config.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/config/Config.zig b/src/config/Config.zig index a5f5b56b3..1c32c731c 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -2060,7 +2060,9 @@ keybind: Keybinds = .{}, /// * `plastic` - A glossy, dark plastic frame. /// * `chrome` - A shiny chrome frame. /// -/// This only has an effect when `macos-icon` is set to `custom-style`. +/// Note: This configuration is required when `macos-icon` is set to +/// `custom-style`. +/// @"macos-icon-frame": MacAppIconFrame = .aluminum, /// The color of the ghost in the macOS app icon. @@ -2068,8 +2070,6 @@ keybind: Keybinds = .{}, /// Note: This configuration is required when `macos-icon` is set to /// `custom-style`. /// -/// This only has an effect when `macos-icon` is set to `custom-style`. -/// /// Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. @"macos-icon-ghost-color": ?Color = null, @@ -2082,7 +2082,6 @@ keybind: Keybinds = .{}, /// Note: This configuration is required when `macos-icon` is set to /// `custom-style`. /// -/// This only has an effect when `macos-icon` is set to `custom-style`. @"macos-icon-screen-color": ?ColorList = null, /// Put every surface (tab, split, window) into a dedicated Linux cgroup. From aa4aaa200facbc1a063efb658c545bd45970de4f Mon Sep 17 00:00:00 2001 From: McNight Date: Sun, 23 Feb 2025 19:34:27 +0100 Subject: [PATCH 2/9] fix(macos): prevent performing newTab shortcut on QuickTerminalWindow #5939 --- .../QuickTerminalController.swift | 18 +++++++++++------- .../Features/Terminal/TerminalManager.swift | 6 ++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift index 807935806..7681a00fa 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift @@ -437,6 +437,16 @@ class QuickTerminalController: BaseTerminalController { } } + func showNoNewTabAlert() { + guard let window else { return } + let alert = NSAlert() + alert.messageText = "Cannot Create New Tab" + alert.informativeText = "Tabs aren't supported in the Quick Terminal." + alert.addButton(withTitle: "OK") + alert.alertStyle = .warning + alert.beginSheetModal(for: window) + } + // MARK: First Responder @IBAction override func closeWindow(_ sender: Any) { @@ -445,13 +455,7 @@ class QuickTerminalController: BaseTerminalController { } @IBAction func newTab(_ sender: Any?) { - guard let window else { return } - let alert = NSAlert() - alert.messageText = "Cannot Create New Tab" - alert.informativeText = "Tabs aren't supported in the Quick Terminal." - alert.addButton(withTitle: "OK") - alert.alertStyle = .warning - alert.beginSheetModal(for: window) + showNoNewTabAlert() } @IBAction func toggleGhosttyFullScreen(_ sender: Any) { diff --git a/macos/Sources/Features/Terminal/TerminalManager.swift b/macos/Sources/Features/Terminal/TerminalManager.swift index a75ee78f8..1647d066a 100644 --- a/macos/Sources/Features/Terminal/TerminalManager.swift +++ b/macos/Sources/Features/Terminal/TerminalManager.swift @@ -125,6 +125,12 @@ class TerminalManager { } private func newTab(to parent: NSWindow, withBaseConfig base: Ghostty.SurfaceConfiguration?) { + // If the parent window is a QuickTerminalWindow, we early return with an alert. + if let controller = parent.windowController as? QuickTerminalController { + controller.showNoNewTabAlert() + return + } + // If our parent is in non-native fullscreen, then new tabs do not work. // See: https://github.com/mitchellh/ghostty/issues/392 if let controller = parent.windowController as? TerminalController, From 995959dce44b1d75cccb9fe5202902e8a67ff3c4 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sun, 23 Feb 2025 12:57:24 -0600 Subject: [PATCH 3/9] kitty images: add delete by range operations Fixes #5937 Implement [deleting Kitty image ranges](https://sw.kovidgoyal.net/kitty/graphics-protocol/#deleting-images). --- src/terminal/kitty/graphics_command.zig | 93 ++++++++++++++++++ src/terminal/kitty/graphics_storage.zig | 125 ++++++++++++++++++++++++ 2 files changed, 218 insertions(+) diff --git a/src/terminal/kitty/graphics_command.zig b/src/terminal/kitty/graphics_command.zig index d199711d3..840949d74 100644 --- a/src/terminal/kitty/graphics_command.zig +++ b/src/terminal/kitty/graphics_command.zig @@ -801,6 +801,13 @@ pub const Delete = union(enum) { z: i32 = 0, // z }, + // r/R + range: struct { + delete: bool = false, // uppercase + first: u32 = 0, // x + last: u32 = 0, // y + }, + // x/X column: struct { delete: bool = false, // uppercase @@ -885,6 +892,19 @@ pub const Delete = union(enum) { break :blk result; }, + 'r', 'R' => blk: { + const x = kv.get('x') orelse return error.InvalidFormat; + const y = kv.get('y') orelse return error.InvalidFormat; + if (x > y) return error.InvalidFormat; + break :blk .{ + .range = .{ + .delete = what == 'R', + .first = x, + .last = y, + }, + }; + }, + 'x', 'X' => blk: { var result: Delete = .{ .column = .{ .delete = what == 'X' } }; if (kv.get('x')) |v| { @@ -1197,3 +1217,76 @@ test "response: encode with image ID and number" { try r.encode(fbs.writer()); try testing.expectEqualStrings("\x1b_Gi=12,I=4;OK\x1b\\", fbs.getWritten()); } + +test "delete range command 1" { + const testing = std.testing; + const alloc = testing.allocator; + var p = Parser.init(alloc); + defer p.deinit(); + + const input = "a=d,d=r,x=3,y=4"; + for (input) |c| try p.feed(c); + const command = try p.complete(); + defer command.deinit(alloc); + + try testing.expect(command.control == .delete); + const v = command.control.delete; + try testing.expect(v == .range); + const range = v.range; + try testing.expect(!range.delete); + try testing.expectEqual(@as(u32, 3), range.first); + try testing.expectEqual(@as(u32, 4), range.last); +} + +test "delete range command 2" { + const testing = std.testing; + const alloc = testing.allocator; + var p = Parser.init(alloc); + defer p.deinit(); + + const input = "a=d,d=R,x=5,y=11"; + for (input) |c| try p.feed(c); + const command = try p.complete(); + defer command.deinit(alloc); + + try testing.expect(command.control == .delete); + const v = command.control.delete; + try testing.expect(v == .range); + const range = v.range; + try testing.expect(range.delete); + try testing.expectEqual(@as(u32, 5), range.first); + try testing.expectEqual(@as(u32, 11), range.last); +} + +test "delete range command 3" { + const testing = std.testing; + const alloc = testing.allocator; + var p = Parser.init(alloc); + defer p.deinit(); + + const input = "a=d,d=R,x=5,y=4"; + for (input) |c| try p.feed(c); + try testing.expectError(error.InvalidFormat, p.complete()); +} + +test "delete range command 4" { + const testing = std.testing; + const alloc = testing.allocator; + var p = Parser.init(alloc); + defer p.deinit(); + + const input = "a=d,d=R,x=5"; + for (input) |c| try p.feed(c); + try testing.expectError(error.InvalidFormat, p.complete()); +} + +test "delete range command 5" { + const testing = std.testing; + const alloc = testing.allocator; + var p = Parser.init(alloc); + defer p.deinit(); + + const input = "a=d,d=R,y=5"; + for (input) |c| try p.feed(c); + try testing.expectError(error.InvalidFormat, p.complete()); +} diff --git a/src/terminal/kitty/graphics_storage.zig b/src/terminal/kitty/graphics_storage.zig index ffd3aa580..474486e5b 100644 --- a/src/terminal/kitty/graphics_storage.zig +++ b/src/terminal/kitty/graphics_storage.zig @@ -397,6 +397,31 @@ pub const ImageStorage = struct { self.dirty = true; }, + .range => |v| range: { + if (v.first <= 0 or v.last <= 0) { + log.warn("delete range values must be greater than zero", .{}); + break :range; + } + if (v.first > v.last) { + log.warn("delete range 'x' ({}) must be less than or equal to 'y' ({})", .{ v.first, v.last }); + break :range; + } + + var it = self.placements.iterator(); + while (it.next()) |entry| { + if (entry.key_ptr.image_id >= v.first or entry.key_ptr.image_id <= v.last) { + const image_id = entry.key_ptr.image_id; + log.warn("delete range: {}", .{image_id}); + entry.value_ptr.deinit(&t.screen); + self.placements.removeByPtr(entry.key_ptr); + if (v.delete) self.deleteIfUnused(alloc, image_id); + } + } + + // Mark dirty to force redraw + self.dirty = true; + }, + // We don't support animation frames yet so they are successfully // deleted! .animation_frames => {}, @@ -1111,3 +1136,103 @@ test "storage: delete by row 1x1" { .placement_id = .{ .tag = .external, .id = 3 }, }) != null); } + +test "storage: delete images by range 1" { + const testing = std.testing; + const alloc = testing.allocator; + var t = try terminal.Terminal.init(alloc, .{ .rows = 3, .cols = 3 }); + defer t.deinit(alloc); + const tracked = t.screen.pages.countTrackedPins(); + + var s: ImageStorage = .{}; + defer s.deinit(alloc, &t.screen); + try s.addImage(alloc, .{ .id = 1 }); + try s.addImage(alloc, .{ .id = 2 }); + try s.addImage(alloc, .{ .id = 3 }); + try s.addPlacement(alloc, 1, 1, .{ .location = .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) } }); + try s.addPlacement(alloc, 2, 1, .{ .location = .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) } }); + try testing.expectEqual(@as(usize, 3), s.images.count()); + try testing.expectEqual(@as(usize, 2), s.placements.count()); + + s.dirty = false; + s.delete(alloc, &t, .{ .range = .{ .delete = false, .first = 1, .last = 2 } }); + try testing.expect(s.dirty); + try testing.expectEqual(@as(usize, 3), s.images.count()); + try testing.expectEqual(@as(usize, 0), s.placements.count()); + try testing.expectEqual(tracked, t.screen.pages.countTrackedPins()); +} + +test "storage: delete images by range 2" { + const testing = std.testing; + const alloc = testing.allocator; + var t = try terminal.Terminal.init(alloc, .{ .rows = 3, .cols = 3 }); + defer t.deinit(alloc); + const tracked = t.screen.pages.countTrackedPins(); + + var s: ImageStorage = .{}; + defer s.deinit(alloc, &t.screen); + try s.addImage(alloc, .{ .id = 1 }); + try s.addImage(alloc, .{ .id = 2 }); + try s.addImage(alloc, .{ .id = 3 }); + try s.addPlacement(alloc, 1, 1, .{ .location = .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) } }); + try s.addPlacement(alloc, 2, 1, .{ .location = .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) } }); + try testing.expectEqual(@as(usize, 3), s.images.count()); + try testing.expectEqual(@as(usize, 2), s.placements.count()); + + s.dirty = false; + s.delete(alloc, &t, .{ .range = .{ .delete = true, .first = 1, .last = 2 } }); + try testing.expect(s.dirty); + try testing.expectEqual(@as(usize, 1), s.images.count()); + try testing.expectEqual(@as(usize, 0), s.placements.count()); + try testing.expectEqual(tracked, t.screen.pages.countTrackedPins()); +} + +test "storage: delete images by range 3" { + const testing = std.testing; + const alloc = testing.allocator; + var t = try terminal.Terminal.init(alloc, .{ .rows = 3, .cols = 3 }); + defer t.deinit(alloc); + const tracked = t.screen.pages.countTrackedPins(); + + var s: ImageStorage = .{}; + defer s.deinit(alloc, &t.screen); + try s.addImage(alloc, .{ .id = 1 }); + try s.addImage(alloc, .{ .id = 2 }); + try s.addImage(alloc, .{ .id = 3 }); + try s.addPlacement(alloc, 1, 1, .{ .location = .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) } }); + try s.addPlacement(alloc, 2, 1, .{ .location = .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) } }); + try testing.expectEqual(@as(usize, 3), s.images.count()); + try testing.expectEqual(@as(usize, 2), s.placements.count()); + + s.dirty = false; + s.delete(alloc, &t, .{ .range = .{ .delete = false, .first = 1, .last = 1 } }); + try testing.expect(s.dirty); + try testing.expectEqual(@as(usize, 3), s.images.count()); + try testing.expectEqual(@as(usize, 0), s.placements.count()); + try testing.expectEqual(tracked, t.screen.pages.countTrackedPins()); +} + +test "storage: delete images by range 4" { + const testing = std.testing; + const alloc = testing.allocator; + var t = try terminal.Terminal.init(alloc, .{ .rows = 3, .cols = 3 }); + defer t.deinit(alloc); + const tracked = t.screen.pages.countTrackedPins(); + + var s: ImageStorage = .{}; + defer s.deinit(alloc, &t.screen); + try s.addImage(alloc, .{ .id = 1 }); + try s.addImage(alloc, .{ .id = 2 }); + try s.addImage(alloc, .{ .id = 3 }); + try s.addPlacement(alloc, 1, 1, .{ .location = .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) } }); + try s.addPlacement(alloc, 2, 1, .{ .location = .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) } }); + try testing.expectEqual(@as(usize, 3), s.images.count()); + try testing.expectEqual(@as(usize, 2), s.placements.count()); + + s.dirty = false; + s.delete(alloc, &t, .{ .range = .{ .delete = true, .first = 1, .last = 1 } }); + try testing.expect(s.dirty); + try testing.expectEqual(@as(usize, 1), s.images.count()); + try testing.expectEqual(@as(usize, 0), s.placements.count()); + try testing.expectEqual(tracked, t.screen.pages.countTrackedPins()); +} From f4f36a9a9844870325cd138bf043b63735c64bf8 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Mon, 24 Feb 2025 11:05:37 -0600 Subject: [PATCH 4/9] kitty: delete stray log line --- src/terminal/kitty/graphics_storage.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/terminal/kitty/graphics_storage.zig b/src/terminal/kitty/graphics_storage.zig index 474486e5b..e55dc45c3 100644 --- a/src/terminal/kitty/graphics_storage.zig +++ b/src/terminal/kitty/graphics_storage.zig @@ -411,7 +411,6 @@ pub const ImageStorage = struct { while (it.next()) |entry| { if (entry.key_ptr.image_id >= v.first or entry.key_ptr.image_id <= v.last) { const image_id = entry.key_ptr.image_id; - log.warn("delete range: {}", .{image_id}); entry.value_ptr.deinit(&t.screen); self.placements.removeByPtr(entry.key_ptr); if (v.delete) self.deleteIfUnused(alloc, image_id); From 1dc375dd0e2d21c2c570394d9ed2b6eeb9585879 Mon Sep 17 00:00:00 2001 From: Leah Amelia Chen Date: Mon, 24 Feb 2025 18:05:26 +0100 Subject: [PATCH 5/9] gtk: instruct users to install blueprint-compiler There's been *far* too many people who aren't aware of the new dependency, and that is partly our fault: a "FileNotFound" error is quite obtuse, unless you religiously follow every PR and every commit made to the repository. Instead of shepherding everyone who runs into this manually, we should offer better signposting. --- src/apprt/gtk/blueprint_compiler.zig | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/apprt/gtk/blueprint_compiler.zig b/src/apprt/gtk/blueprint_compiler.zig index f1d42c43d..15dd574e5 100644 --- a/src/apprt/gtk/blueprint_compiler.zig +++ b/src/apprt/gtk/blueprint_compiler.zig @@ -47,11 +47,23 @@ pub fn main() !void { alloc, ); - const term = try compiler.spawnAndWait(); + const term = compiler.spawnAndWait() catch |err| switch (err) { + error.FileNotFound => { + std.log.err( + \\`blueprint-compiler` not found. + \\ + \\Ghostty requires `blueprint-compiler` as a build-time dependency starting from version 1.2. + \\Please install it, ensure that it is available on your PATH, and then retry building Ghostty. + , .{}); + std.posix.exit(1); + }, + else => return err, + }; + switch (term) { .Exited => |rc| { - if (rc != 0) std.posix.exit(1); + if (rc != 0) std.process.exit(1); }, - else => std.posix.exit(1), + else => std.process.exit(1), } } From 1254c6b981ac25ee5db037206649f98e1a0c4454 Mon Sep 17 00:00:00 2001 From: McNight Date: Tue, 25 Feb 2025 00:17:01 +0100 Subject: [PATCH 6/9] fix(macos): address MR feedback #5939 --- .../QuickTerminal/QuickTerminalController.swift | 13 +++++++++++++ .../Sources/Features/Terminal/TerminalManager.swift | 7 ++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift index 7681a00fa..0ffbbc49e 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift @@ -59,6 +59,11 @@ class QuickTerminalController: BaseTerminalController { selector: #selector(ghosttyConfigDidChange(_:)), name: .ghosttyConfigDidChange, object: nil) + center.addObserver( + self, + selector: #selector(onNewTab), + name: Ghostty.Notification.ghosttyNewTab, + object: nil) } required init?(coder: NSCoder) { @@ -496,6 +501,14 @@ class QuickTerminalController: BaseTerminalController { syncAppearance() } + @objc private func onNewTab(notification: SwiftUI.Notification) { + guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return } + guard let window = surfaceView.window else { return } + guard window.windowController is QuickTerminalController else { return } + // Tabs aren't supported with Quick Terminals or derivatives + showNoNewTabAlert() + } + private struct DerivedConfig { let quickTerminalScreen: QuickTerminalScreen let quickTerminalAnimationDuration: Double diff --git a/macos/Sources/Features/Terminal/TerminalManager.swift b/macos/Sources/Features/Terminal/TerminalManager.swift index 1647d066a..07735cb58 100644 --- a/macos/Sources/Features/Terminal/TerminalManager.swift +++ b/macos/Sources/Features/Terminal/TerminalManager.swift @@ -125,11 +125,8 @@ class TerminalManager { } private func newTab(to parent: NSWindow, withBaseConfig base: Ghostty.SurfaceConfiguration?) { - // If the parent window is a QuickTerminalWindow, we early return with an alert. - if let controller = parent.windowController as? QuickTerminalController { - controller.showNoNewTabAlert() - return - } + // Making sure that we're dealing with a TerminalController + guard parent.windowController is TerminalController else { return } // If our parent is in non-native fullscreen, then new tabs do not work. // See: https://github.com/mitchellh/ghostty/issues/392 From b4349d322679bd8423b9780d1c63fd129cf97ba2 Mon Sep 17 00:00:00 2001 From: McNight Date: Tue, 25 Feb 2025 00:18:02 +0100 Subject: [PATCH 7/9] fix(macos): make showNoNewTabAlert method private #5939 --- .../Features/QuickTerminal/QuickTerminalController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift index 0ffbbc49e..4e4d3167f 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift @@ -442,7 +442,7 @@ class QuickTerminalController: BaseTerminalController { } } - func showNoNewTabAlert() { + private func showNoNewTabAlert() { guard let window else { return } let alert = NSAlert() alert.messageText = "Cannot Create New Tab" From d284146621dab2c3f48476934a62ab51a5509d9b Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Mon, 24 Feb 2025 22:07:16 -0600 Subject: [PATCH 8/9] gtk: convert window actions to use zig-gobject --- src/apprt/gtk/Window.zig | 163 +++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 85 deletions(-) diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 5d1efd3ca..89bde5170 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -7,9 +7,15 @@ const Window = @This(); const std = @import("std"); const builtin = @import("builtin"); -const build_config = @import("../../build_config.zig"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; + +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const build_config = @import("../../build_config.zig"); const configpkg = @import("../../config.zig"); const font = @import("../../font/main.zig"); const input = @import("../../input.zig"); @@ -475,36 +481,38 @@ fn toggleCssClass( /// menus and such. The menu is defined in App.zig but the action is defined /// here. The string name binds them. fn initActions(self: *Window) void { + // FIXME: when rest of file is converted to gobject + const window: *gtk.Window = @ptrCast(@alignCast(self.window)); + const action_map = gobject.ext.cast(gio.ActionMap, window).?; const actions = .{ - .{ "about", >kActionAbout }, - .{ "close", >kActionClose }, - .{ "new-window", >kActionNewWindow }, - .{ "new-tab", >kActionNewTab }, - .{ "close-tab", >kActionCloseTab }, - .{ "split-right", >kActionSplitRight }, - .{ "split-down", >kActionSplitDown }, - .{ "split-left", >kActionSplitLeft }, - .{ "split-up", >kActionSplitUp }, - .{ "toggle-inspector", >kActionToggleInspector }, - .{ "copy", >kActionCopy }, - .{ "paste", >kActionPaste }, - .{ "reset", >kActionReset }, - .{ "clear", >kActionClear }, - .{ "prompt-title", >kActionPromptTitle }, + .{ "about", gtkActionAbout }, + .{ "close", gtkActionClose }, + .{ "new-window", gtkActionNewWindow }, + .{ "new-tab", gtkActionNewTab }, + .{ "close-tab", gtkActionCloseTab }, + .{ "split-right", gtkActionSplitRight }, + .{ "split-down", gtkActionSplitDown }, + .{ "split-left", gtkActionSplitLeft }, + .{ "split-up", gtkActionSplitUp }, + .{ "toggle-inspector", gtkActionToggleInspector }, + .{ "copy", gtkActionCopy }, + .{ "paste", gtkActionPaste }, + .{ "reset", gtkActionReset }, + .{ "clear", gtkActionClear }, + .{ "prompt-title", gtkActionPromptTitle }, }; inline for (actions) |entry| { - const action = c.g_simple_action_new(entry[0], null); - defer c.g_object_unref(action); - _ = c.g_signal_connect_data( + const action = gio.SimpleAction.new(entry[0], null); + defer action.unref(); + _ = gio.SimpleAction.signals.activate.connect( action, - "activate", - c.G_CALLBACK(entry[1]), + *Window, + entry[1], self, - null, - c.G_CONNECT_DEFAULT, + .{}, ); - c.g_action_map_add_action(@ptrCast(self.window), @ptrCast(action)); + action_map.addAction(action.as(gio.Action)); } } @@ -878,12 +886,10 @@ fn gtkKeyPressed( } fn gtkActionAbout( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); - const name = "Ghostty"; const icon = "com.mitchellh.ghostty"; const website = "https://ghostty.org"; @@ -924,20 +930,18 @@ fn gtkActionAbout( } fn gtkActionClose( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); c.gtk_window_destroy(self.window); } fn gtkActionNewWindow( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .new_window = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -946,20 +950,19 @@ fn gtkActionNewWindow( } fn gtkActionNewTab( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { // We can use undefined because the button is not used. - gtkTabNewClick(undefined, ud); + gtkTabNewClick(undefined, self); } fn gtkActionCloseTab( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .close_tab = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -968,11 +971,10 @@ fn gtkActionCloseTab( } fn gtkActionSplitRight( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .new_split = .right }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -981,11 +983,10 @@ fn gtkActionSplitRight( } fn gtkActionSplitDown( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .new_split = .down }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -994,11 +995,10 @@ fn gtkActionSplitDown( } fn gtkActionSplitLeft( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .new_split = .left }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -1007,11 +1007,10 @@ fn gtkActionSplitLeft( } fn gtkActionSplitUp( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .new_split = .up }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -1020,11 +1019,10 @@ fn gtkActionSplitUp( } fn gtkActionToggleInspector( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .inspector = .toggle }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -1033,11 +1031,10 @@ fn gtkActionToggleInspector( } fn gtkActionCopy( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .copy_to_clipboard = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -1046,11 +1043,10 @@ fn gtkActionCopy( } fn gtkActionPaste( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .paste_from_clipboard = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -1059,11 +1055,10 @@ fn gtkActionPaste( } fn gtkActionReset( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .reset = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -1072,11 +1067,10 @@ fn gtkActionReset( } fn gtkActionClear( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .clear_screen = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); @@ -1085,11 +1079,10 @@ fn gtkActionClear( } fn gtkActionPromptTitle( - _: *c.GSimpleAction, - _: *c.GVariant, - ud: ?*anyopaque, + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, ) callconv(.C) void { - const self: *Window = @ptrCast(@alignCast(ud orelse return)); const surface = self.actionSurface() orelse return; _ = surface.performBindingAction(.{ .prompt_surface_title = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); From 3d08b1c4aa6626724f8c142967bfa3e507d461d3 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Tue, 25 Feb 2025 08:19:52 -0600 Subject: [PATCH 9/9] gtk: cast to application window --- src/apprt/gtk/Window.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 89bde5170..2fe76fa6d 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -482,8 +482,8 @@ fn toggleCssClass( /// here. The string name binds them. fn initActions(self: *Window) void { // FIXME: when rest of file is converted to gobject - const window: *gtk.Window = @ptrCast(@alignCast(self.window)); - const action_map = gobject.ext.cast(gio.ActionMap, window).?; + const window: *gtk.ApplicationWindow = @ptrCast(@alignCast(self.window)); + const action_map = window.as(gio.ActionMap); const actions = .{ .{ "about", gtkActionAbout }, .{ "close", gtkActionClose },