From a60f9eb9e6e139b352d347e0d8f598d0b050c566 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 18 Oct 2023 08:41:33 -0700 Subject: [PATCH] apprt/gtk: empty inspector window --- src/apprt/gtk.zig | 1 + src/apprt/gtk/App.zig | 7 ++++ src/apprt/gtk/Window.zig | 30 ++++------------ src/apprt/gtk/icon.zig | 52 ++++++++++++++++++++++++++++ src/apprt/gtk/inspector.zig | 68 +++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 src/apprt/gtk/icon.zig create mode 100644 src/apprt/gtk/inspector.zig diff --git a/src/apprt/gtk.zig b/src/apprt/gtk.zig index 268da2ee4..882448ed7 100644 --- a/src/apprt/gtk.zig +++ b/src/apprt/gtk.zig @@ -6,5 +6,6 @@ pub const Surface = @import("gtk/Surface.zig"); test { @import("std").testing.refAllDecls(@This()); + _ = @import("gtk/inspector.zig"); _ = @import("gtk/key.zig"); } diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 9a9c2fb41..7f89bdc83 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -25,6 +25,7 @@ const Surface = @import("Surface.zig"); const Window = @import("Window.zig"); const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig"); const c = @import("c.zig"); +const inspector = @import("inspector.zig"); const key = @import("key.zig"); const log = std.log.scoped(.gtk); @@ -262,6 +263,12 @@ pub fn run(self: *App) !void { log.warn("error handling configuration changes err={}", .{err}); }; + // TODO: temporary, remove: show our inspector window + { + const win = try inspector.Window.create(self.core_app.alloc, self); + _ = win; + } + while (self.running) { _ = c.g_main_context_iteration(self.ctx, 1); diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 3b5e8a17f..25d1c34a2 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -12,6 +12,7 @@ const CoreSurface = @import("../../Surface.zig"); const App = @import("App.zig"); const Surface = @import("Surface.zig"); +const icon = @import("icon.zig"); const c = @import("c.zig"); const log = std.log.scoped(.gtk); @@ -28,7 +29,7 @@ notebook: *c.GtkNotebook, /// The resources directory for the icon (if any). We need to retain a /// pointer to this because GTK can use it at any time. -icon_search_dir: ?[:0]const u8 = null, +icon: icon.Icon, pub fn create(alloc: Allocator, app: *App) !*Window { // Allocate a fixed pointer for our window. We try to minimize @@ -48,6 +49,7 @@ pub fn init(self: *Window, app: *App) !void { // Set up our own state self.* = .{ .app = app, + .icon = undefined, .window = undefined, .notebook = undefined, }; @@ -62,28 +64,8 @@ pub fn init(self: *Window, app: *App) !void { // If we don't have the icon then we'll try to add our resources dir // to the search path and see if we can find it there. - const icon_name = "com.mitchellh.ghostty"; - const icon_theme = c.gtk_icon_theme_get_for_display(c.gtk_widget_get_display(window)); - if (c.gtk_icon_theme_has_icon(icon_theme, icon_name) == 0) icon: { - const base = self.app.core_app.resources_dir orelse { - log.info("gtk app missing Ghostty icon and no resources dir detected", .{}); - log.info("gtk app will not have Ghostty icon", .{}); - break :icon; - }; - - // Note that this method for adding the icon search path is - // a fallback mechanism. The recommended mechanism is the - // Freedesktop Icon Theme Specification. We distribute a ".desktop" - // file in zig-out/share that should be installed to the proper - // place. - const dir = try std.fmt.allocPrintZ(app.core_app.alloc, "{s}/icons", .{base}); - self.icon_search_dir = dir; - c.gtk_icon_theme_add_search_path(icon_theme, dir.ptr); - if (c.gtk_icon_theme_has_icon(icon_theme, icon_name) == 0) { - log.warn("Ghostty icon for gtk app not found", .{}); - } - } - c.gtk_window_set_icon_name(gtk_window, icon_name); + self.icon = try icon.appIcon(self.app, window); + c.gtk_window_set_icon_name(gtk_window, self.icon.name); // Apply background opacity if we have it if (app.config.@"background-opacity" < 1) { @@ -185,7 +167,7 @@ fn initActions(self: *Window) void { } pub fn deinit(self: *Window) void { - if (self.icon_search_dir) |ptr| self.app.core_app.alloc.free(ptr); + self.icon.deinit(self.app); } /// Add a new tab to this window. diff --git a/src/apprt/gtk/icon.zig b/src/apprt/gtk/icon.zig new file mode 100644 index 000000000..30416d07b --- /dev/null +++ b/src/apprt/gtk/icon.zig @@ -0,0 +1,52 @@ +const std = @import("std"); + +const App = @import("App.zig"); +const c = @import("c.zig"); + +const log = std.log.scoped(.gtk_icon); + +/// An icon. The icon may be associated with some allocated state so when +/// the icon is no longer in use it should be deinitialized. +pub const Icon = struct { + name: [:0]const u8, + state: ?[:0]const u8 = null, + + pub fn deinit(self: *const Icon, app: *App) void { + if (self.state) |v| app.core_app.alloc.free(v); + } +}; + +/// Returns the application icon that can be used anywhere. This attempts to +/// find the icon in the theme and if it can't be found, it is loaded from +/// the resources dir. If the resources dir can't be found, we'll log a warning +/// and let GTK choose a fallback. +pub fn appIcon(app: *App, widget: *c.GtkWidget) !Icon { + const icon_name = "com.mitchellh.ghostty"; + var result: Icon = .{ .name = icon_name }; + + // If we don't have the icon then we'll try to add our resources dir + // to the search path and see if we can find it there. + const icon_theme = c.gtk_icon_theme_get_for_display(c.gtk_widget_get_display(widget)); + if (c.gtk_icon_theme_has_icon(icon_theme, icon_name) == 0) icon: { + const base = app.core_app.resources_dir orelse { + log.info("gtk app missing Ghostty icon and no resources dir detected", .{}); + log.info("gtk app will not have Ghostty icon", .{}); + break :icon; + }; + + // Note that this method for adding the icon search path is + // a fallback mechanism. The recommended mechanism is the + // Freedesktop Icon Theme Specification. We distribute a ".desktop" + // file in zig-out/share that should be installed to the proper + // place. + const dir = try std.fmt.allocPrintZ(app.core_app.alloc, "{s}/icons", .{base}); + errdefer app.core_app.alloc.free(dir); + result.state = dir; + c.gtk_icon_theme_add_search_path(icon_theme, dir.ptr); + if (c.gtk_icon_theme_has_icon(icon_theme, icon_name) == 0) { + log.warn("Ghostty icon for gtk app not found", .{}); + } + } + + return result; +} diff --git a/src/apprt/gtk/inspector.zig b/src/apprt/gtk/inspector.zig new file mode 100644 index 000000000..38323d618 --- /dev/null +++ b/src/apprt/gtk/inspector.zig @@ -0,0 +1,68 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const App = @import("App.zig"); +const TerminalWindow = @import("Window.zig"); +const c = @import("c.zig"); +const icon = @import("icon.zig"); + +const log = std.log.scoped(.inspector); + +/// A window to hold a dedicated inspector instance. +pub const Window = struct { + /// Our app + app: *App, + + /// Our window + window: *c.GtkWindow, + + /// The window icon + icon: icon.Icon, + + pub fn create(alloc: Allocator, app: *App) !*Window { + var window = try alloc.create(Window); + errdefer alloc.destroy(window); + try window.init(app); + return window; + } + + pub fn init(self: *Window, app: *App) !void { + // Initialize to undefined + self.* = .{ + .app = app, + .icon = undefined, + .window = undefined, + }; + + // Create the window + const window = c.gtk_application_window_new(app.app); + const gtk_window: *c.GtkWindow = @ptrCast(window); + errdefer c.gtk_window_destroy(gtk_window); + self.window = gtk_window; + c.gtk_window_set_title(gtk_window, "Ghostty"); + c.gtk_window_set_default_size(gtk_window, 1000, 600); + self.icon = try icon.appIcon(self.app, window); + c.gtk_window_set_icon_name(gtk_window, self.icon.name); + + // Signals + _ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(>kDestroy), self, null, c.G_CONNECT_DEFAULT); + + // Show the window + c.gtk_widget_show(window); + } + + pub fn deinit(self: *Window) void { + self.icon.deinit(self.app); + } + + /// "destroy" signal for the window + fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void { + _ = v; + log.debug("window destroy", .{}); + + const self: *Window = @ptrCast(@alignCast(ud.?)); + const alloc = self.app.core_app.alloc; + self.deinit(); + alloc.destroy(self); + } +};