From d3cb6d0d41835f9e57d4dca6b927440e0f505bb4 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Thu, 29 May 2025 15:45:51 -0500 Subject: [PATCH] GTK: add action to show the GTK inspector The default keybinds for showing the GTK inspector (`ctrl+shift+i` and `ctrl+shift+d`) don't work reliably in Ghostty due to the way Ghostty handles input. You can show the GTK inspector by setting the environment variable `GTK_DEBUG` to `interactive` before starting Ghostty but that's not always convenient. This adds a keybind action that will show the GTK inspector. Due to API limitations toggling the GTK inspector using the keybind action is impractical because GTK does not provide a convenient API to determine if the GTK inspector is already showing. Thus we limit ourselves to strictly showing the GTK inspector. To close the GTK inspector the user must click the close button on the GTK inspector window. If the GTK inspector window is already visible but is hidden, calling the keybind action will not bring the GTK inspector window to the front. --- include/ghostty.h | 1 + .../TerminalCommandPalette.swift | 3 ++- src/App.zig | 1 + src/apprt/action.zig | 4 ++++ src/apprt/glfw.zig | 1 + src/apprt/gtk/App.zig | 19 +++++++++++++++++++ src/input/Binding.zig | 4 ++++ src/input/command.zig | 6 ++++++ 8 files changed, 38 insertions(+), 1 deletion(-) diff --git a/include/ghostty.h b/include/ghostty.h index 950f5ef80..6b1625a30 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -653,6 +653,7 @@ typedef enum { GHOSTTY_ACTION_INITIAL_SIZE, GHOSTTY_ACTION_CELL_SIZE, GHOSTTY_ACTION_INSPECTOR, + GHOSTTY_ACTION_SHOW_GTK_INSPECTOR, GHOSTTY_ACTION_RENDER_INSPECTOR, GHOSTTY_ACTION_DESKTOP_NOTIFICATION, GHOSTTY_ACTION_SET_TITLE, diff --git a/macos/Sources/Features/Command Palette/TerminalCommandPalette.swift b/macos/Sources/Features/Command Palette/TerminalCommandPalette.swift index 57a76dd43..47f2baf23 100644 --- a/macos/Sources/Features/Command Palette/TerminalCommandPalette.swift +++ b/macos/Sources/Features/Command Palette/TerminalCommandPalette.swift @@ -29,7 +29,8 @@ struct TerminalCommandPaletteView: View { let key = String(cString: c.action_key) switch (key) { case "toggle_tab_overview", - "toggle_window_decorations": + "toggle_window_decorations", + "show_gtk_inspector": return false default: return true diff --git a/src/App.zig b/src/App.zig index 005b745a6..39db2e2f9 100644 --- a/src/App.zig +++ b/src/App.zig @@ -445,6 +445,7 @@ pub fn performAction( .toggle_quick_terminal => _ = try rt_app.performAction(.app, .toggle_quick_terminal, {}), .toggle_visibility => _ = try rt_app.performAction(.app, .toggle_visibility, {}), .check_for_updates => _ = try rt_app.performAction(.app, .check_for_updates, {}), + .show_gtk_inspector => _ = try rt_app.performAction(.app, .show_gtk_inspector, {}), } } diff --git a/src/apprt/action.zig b/src/apprt/action.zig index 8a23bc1a4..7866db182 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -165,6 +165,9 @@ pub const Action = union(Key) { /// Control whether the inspector is shown or hidden. inspector: Inspector, + /// Show the GTK inspector. + show_gtk_inspector, + /// The inspector for the given target has changes and should be /// rendered at the next opportunity. render_inspector, @@ -284,6 +287,7 @@ pub const Action = union(Key) { initial_size, cell_size, inspector, + show_gtk_inspector, render_inspector, desktop_notification, set_title, diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 221d5344a..d67567aee 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -250,6 +250,7 @@ pub const App = struct { .reset_window_size, .ring_bell, .check_for_updates, + .show_gtk_inspector, => { log.info("unimplemented action={}", .{action}); return false; diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 55c0be5e0..d1c8f2c59 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -481,6 +481,7 @@ pub fn performAction( .config_change => self.configChange(target, value.config), .reload_config => try self.reloadConfig(target, value), .inspector => self.controlInspector(target, value), + .show_gtk_inspector => self.showGTKInspector(), .desktop_notification => self.showDesktopNotification(target, value), .set_title => try self.setTitle(target, value), .pwd => try self.setPwd(target, value), @@ -687,6 +688,12 @@ fn controlInspector( surface.controlInspector(mode); } +fn showGTKInspector( + _: *const App, +) void { + gtk.Window.setInteractiveDebugging(@intFromBool(true)); +} + fn toggleMaximize(_: *App, target: apprt.Target) void { switch (target) { .app => {}, @@ -1060,6 +1067,7 @@ fn syncActionAccelerators(self: *App) !void { try self.syncActionAccelerator("app.open-config", .{ .open_config = {} }); try self.syncActionAccelerator("app.reload-config", .{ .reload_config = {} }); try self.syncActionAccelerator("win.toggle-inspector", .{ .inspector = .toggle }); + try self.syncActionAccelerator("app.show-gtk-inspector", .show_gtk_inspector); try self.syncActionAccelerator("win.toggle-command-palette", .toggle_command_palette); try self.syncActionAccelerator("win.close", .{ .close_window = {} }); try self.syncActionAccelerator("win.new-window", .{ .new_window = {} }); @@ -1655,6 +1663,16 @@ fn gtkActionPresentSurface( ); } +fn gtkActionShowGTKInspector( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *App, +) callconv(.c) void { + self.core_app.performAction(self, .show_gtk_inspector) catch |err| { + log.err("error showing GTK inspector err={}", .{err}); + }; +} + /// This is called to setup the action map that this application supports. /// This should be called only once on startup. fn initActions(self: *App) void { @@ -1673,6 +1691,7 @@ fn initActions(self: *App) void { .{ "open-config", gtkActionOpenConfig, null }, .{ "reload-config", gtkActionReloadConfig, null }, .{ "present-surface", gtkActionPresentSurface, t }, + .{ "show-gtk-inspector", gtkActionShowGTKInspector, null }, }; inline for (actions) |entry| { const action = gio.SimpleAction.new(entry[0], entry[2]); diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 3818d99a6..4a5fb4522 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -397,6 +397,9 @@ pub const Action = union(enum) { /// keybind = cmd+i=inspector:toggle inspector: InspectorMode, + /// Show the GTK inspector. + show_gtk_inspector, + /// Open the configuration file in the default OS editor. If your default OS /// editor isn't configured then this will fail. Currently, any failures to /// open the configuration will show up only in the logs. @@ -795,6 +798,7 @@ pub const Action = union(enum) { .toggle_quick_terminal, .toggle_visibility, .check_for_updates, + .show_gtk_inspector, => .app, // These are app but can be special-cased in a surface context. diff --git a/src/input/command.zig b/src/input/command.zig index 8ef4a5f0e..53d1b6b3d 100644 --- a/src/input/command.zig +++ b/src/input/command.zig @@ -298,6 +298,12 @@ fn actionCommands(action: Action.Key) []const Command { .description = "Toggle the inspector.", }}, + .show_gtk_inspector => comptime &.{.{ + .action = .show_gtk_inspector, + .title = "Show the GTK Inspector", + .description = "Show the GTK inspector.", + }}, + .open_config => comptime &.{.{ .action = .open_config, .title = "Open Config",