From 7a30d1080eedea742f83441ba61a1c6ac2b906d6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 20 Oct 2023 11:25:04 -0700 Subject: [PATCH] core: hook up all the inspector activation state and such --- src/Inspector.zig | 13 +++++++++ src/Surface.zig | 55 +++++++++++++++++++++++++++++++++++ src/apprt/gtk/ImguiWidget.zig | 2 -- src/apprt/gtk/inspector.zig | 15 +++++++++- src/renderer/State.zig | 5 ++++ 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/Inspector.zig b/src/Inspector.zig index d801ad441..19cd6d53c 100644 --- a/src/Inspector.zig +++ b/src/Inspector.zig @@ -3,6 +3,19 @@ //! debugging issues in Ghostty itself. const Inspector = @This(); +const cimgui = @import("cimgui"); + pub fn init() Inspector { return .{}; } + +pub fn deinit(self: *Inspector) void { + _ = self; +} + +pub fn render(self: *Inspector) void { + _ = self; + + var show: bool = true; + cimgui.c.igShowDemoWindow(&show); +} diff --git a/src/Surface.zig b/src/Surface.zig index cc3a4a734..f90aaaf65 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -20,6 +20,7 @@ const builtin = @import("builtin"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; +const Inspector = @import("Inspector.zig"); const renderer = @import("renderer.zig"); const termio = @import("termio.zig"); const objc = @import("objc"); @@ -74,6 +75,9 @@ io: termio.Impl, io_thread: termio.Thread, io_thr: std.Thread, +/// Terminal inspector +inspector: ?*Inspector = null, + /// All the cached sizes since we need them at various times. screen_size: renderer.ScreenSize, grid_size: renderer.GridSize, @@ -550,8 +554,14 @@ pub fn deinit(self: *Surface) void { self.font_lib.deinit(); self.alloc.destroy(self.font_group); + if (self.inspector) |v| { + v.deinit(); + self.alloc.destroy(v); + } + self.alloc.destroy(self.renderer_state.mutex); self.config.deinit(); + log.info("surface closed addr={x}", .{@intFromPtr(self)}); } @@ -561,6 +571,51 @@ pub fn close(self: *Surface) void { self.rt_surface.close(self.needsConfirmQuit()); } +/// Activate the inspector. This will begin collecting inspection data. +/// This will not affect the GUI. The GUI must use performAction to +/// show/hide the inspector UI. +pub fn activateInspector(self: *Surface) !void { + if (self.inspector != null) return; + + // Setup the inspector + var ptr = try self.alloc.create(Inspector); + errdefer self.alloc.destroy(ptr); + ptr.* = Inspector.init(); + self.inspector = ptr; + + // Put the inspector onto the render state + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + assert(self.renderer_state.inspector == null); + self.renderer_state.inspector = self.inspector; +} + +/// Deactivate the inspector and stop collecting any information. +pub fn deactivateInspector(self: *Surface) void { + const inspector = self.inspector orelse return; + + // Remove the inspector from the render state + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + assert(self.renderer_state.inspector != null); + self.renderer_state.inspector = null; + } + + // Deinit the inspector + inspector.deinit(); + self.alloc.destroy(inspector); + self.inspector = null; +} + +/// Render the inspector. This requires an active ImGui context. +pub fn renderInspector(self: *Surface) void { + const inspector = self.inspector orelse return; + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + inspector.render(); +} + /// True if the surface requires confirmation to quit. This should be called /// by apprt to determine if the surface should confirm before quitting. pub fn needsConfirmQuit(self: *Surface) bool { diff --git a/src/apprt/gtk/ImguiWidget.zig b/src/apprt/gtk/ImguiWidget.zig index d8baf578b..696eee739 100644 --- a/src/apprt/gtk/ImguiWidget.zig +++ b/src/apprt/gtk/ImguiWidget.zig @@ -209,8 +209,6 @@ fn gtkRender(area: *c.GtkGLArea, ctx: *c.GdkGLContext, ud: ?*anyopaque) callconv cimgui.c.igNewFrame(); // Build our UI - var show: bool = true; - cimgui.c.igShowDemoWindow(&show); if (self.render_callback) |cb| cb(self.render_userdata); // Render diff --git a/src/apprt/gtk/inspector.zig b/src/apprt/gtk/inspector.zig index aed2afa48..f2e891a44 100644 --- a/src/apprt/gtk/inspector.zig +++ b/src/apprt/gtk/inspector.zig @@ -74,6 +74,12 @@ pub const Inspector = struct { .location = undefined, }; + // Activate the inspector. If it doesn't work we ignore the error + // because we can just show an error in the inspector window. + self.surface.core_surface.activateInspector() catch |err| { + log.err("failed to activate inspector err={}", .{err}); + }; + switch (location) { .hidden => self.location = .{ .hidden = {} }, .window => try self.initWindow(), @@ -81,7 +87,7 @@ pub const Inspector = struct { } fn deinit(self: *Inspector) void { - _ = self; + self.surface.core_surface.deactivateInspector(); } /// Request the inspector is closed. @@ -136,6 +142,8 @@ const Window = struct { // Initialize our imgui widget try self.imgui_widget.init(); errdefer self.imgui_widget.deinit(); + self.imgui_widget.render_callback = &imguiRender; + self.imgui_widget.render_userdata = self; // Signals _ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(>kDestroy), self, null, c.G_CONNECT_DEFAULT); @@ -154,6 +162,11 @@ const Window = struct { c.gtk_window_destroy(self.window); } + fn imguiRender(ud: ?*anyopaque) void { + const self: *Window = @ptrCast(@alignCast(ud orelse return)); + self.inspector.surface.core_surface.renderInspector(); + } + /// "destroy" signal for the window fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void { _ = v; diff --git a/src/renderer/State.zig b/src/renderer/State.zig index e791cfda4..e5128b10c 100644 --- a/src/renderer/State.zig +++ b/src/renderer/State.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; +const Inspector = @import("../Inspector.zig"); const terminal = @import("../terminal/main.zig"); const renderer = @import("../renderer.zig"); @@ -14,6 +15,10 @@ mutex: *std.Thread.Mutex, /// The terminal data. terminal: *terminal.Terminal, +/// The terminal inspector, if any. This will be null while the inspector +/// is not active and will be set when it is active. +inspector: ?*Inspector = null, + /// Dead key state. This will render the current dead key preedit text /// over the cursor. This currently only ever renders a single codepoint. /// Preedit can in theory be multiple codepoints long but that is left as