From 4606d56dc0c3a7154ae95d01bc7f6ad777a4835a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 11:24:19 -0700 Subject: [PATCH 01/11] apprt/gtk: config errors is not an application window --- src/apprt/gtk/ConfigErrorsWindow.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apprt/gtk/ConfigErrorsWindow.zig b/src/apprt/gtk/ConfigErrorsWindow.zig index f582c459a..5fe371c1d 100644 --- a/src/apprt/gtk/ConfigErrorsWindow.zig +++ b/src/apprt/gtk/ConfigErrorsWindow.zig @@ -47,7 +47,7 @@ fn destroy(self: *ConfigErrors) void { fn init(self: *ConfigErrors, app: *App) !void { // Create the window - const window = c.gtk_application_window_new(app.app); + const window = c.gtk_window_new(); const gtk_window: *c.GtkWindow = @ptrCast(window); errdefer c.gtk_window_destroy(gtk_window); c.gtk_window_set_title(gtk_window, "Configuration Errors"); From 394ab3017f14a80afe47051438f7f074e0f4ea4a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 12:54:19 -0700 Subject: [PATCH 02/11] apprt/gtk: initial app menu --- src/apprt/gtk/App.zig | 46 ++++++++++++++++++++++++++++++++++++++++ src/apprt/gtk/Window.zig | 10 +++++++++ 2 files changed, 56 insertions(+) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 2505810ef..b5647427b 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -37,6 +37,9 @@ ctx: *c.GMainContext, /// The "none" cursor. We use one that is shared across the entire app. cursor_none: ?*c.GdkCursor, +/// The shared application menu. +menu: ?*c.GMenu = null, + /// The configuration errors window, if it is currently open. config_errors_window: ?*ConfigErrorsWindow = null, @@ -140,6 +143,7 @@ pub fn terminate(self: *App) void { c.g_object_unref(self.app); if (self.cursor_none) |cursor| c.g_object_unref(cursor); + if (self.menu) |menu| c.g_object_unref(menu); self.config.deinit(); @@ -195,6 +199,10 @@ pub fn wakeup(self: App) void { pub fn run(self: *App) !void { if (!self.running) return; + // If we're not remote, then we also setup our actions and menus. + self.initActions(); + self.initMenu(); + // On startup, we want to check for configuration errors right away // so we can show our error window. self.updateConfigErrors() catch |err| { @@ -331,3 +339,41 @@ fn gtkActivate(app: *c.GtkApplication, ud: ?*anyopaque) callconv(.C) void { .new_window = .{}, }, .{ .forever = {} }); } + +fn gtkActionQuit( + _: *c.GSimpleAction, + _: *c.GVariant, + ud: ?*anyopaque, +) callconv(.C) void { + const self: *App = @ptrCast(@alignCast(ud orelse return)); + self.core_app.setQuit() catch |err| { + log.warn("error setting quit err={}", .{err}); + return; + }; +} + +/// 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 { + const action_quit = c.g_simple_action_new("quit", null); + defer c.g_object_unref(action_quit); + c.g_action_map_add_action(@ptrCast(self.app), @ptrCast(action_quit)); + + _ = c.g_signal_connect_data(action_quit, "activate", c.G_CALLBACK(>kActionQuit), self, null, c.G_CONNECT_DEFAULT); +} + +/// This sets the self.menu property to the application menu that can be +/// shared by all application windows. +fn initMenu(self: *App) void { + const menu = c.g_menu_new(); + errdefer c.g_object_unref(menu); + c.g_menu_append(menu, "Quit", "app.quit"); + + // { + // const section = c.g_menu_new(); + // defer c.g_object_unref(section); + // c.g_menu_append_submenu(menu, "File", @ptrCast(@alignCast(section))); + // } + + self.menu = menu; +} diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index c336c2e59..86ef1cbd8 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -89,6 +89,16 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_widget_set_opacity(@ptrCast(window), app.config.@"background-opacity"); } + // Use the new GTK4 header bar + const header = c.gtk_header_bar_new(); + c.gtk_window_set_titlebar(gtk_window, header); + { + const btn = c.gtk_menu_button_new(); + c.gtk_menu_button_set_icon_name(@ptrCast(btn), "open-menu-symbolic"); + c.gtk_menu_button_set_menu_model(@ptrCast(btn), @ptrCast(@alignCast(app.menu))); + c.gtk_header_bar_pack_end(@ptrCast(header), btn); + } + // Hide window decoration if configured. This has to happen before // `gtk_widget_show`. if (!app.config.@"window-decoration") { From dcf615022ea7a9d6bee3683859cb312eccb59ec0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 14:27:05 -0700 Subject: [PATCH 03/11] apprt/gtk: key file for logic related to keys --- src/apprt.zig | 3 +- src/apprt/gtk.zig | 2 + src/apprt/gtk/App.zig | 48 +++++++-- src/apprt/gtk/Surface.zig | 130 ------------------------ src/apprt/gtk/key.zig | 201 ++++++++++++++++++++++++++++++++++++++ src/main.zig | 1 + 6 files changed, 245 insertions(+), 140 deletions(-) create mode 100644 src/apprt/gtk/key.zig diff --git a/src/apprt.zig b/src/apprt.zig index f0c4c2ffe..e979588a9 100644 --- a/src/apprt.zig +++ b/src/apprt.zig @@ -64,5 +64,6 @@ pub const Runtime = enum { }; test { - @import("std").testing.refAllDecls(@This()); + _ = Runtime; + _ = runtime; } diff --git a/src/apprt/gtk.zig b/src/apprt/gtk.zig index f4e4c74a8..268da2ee4 100644 --- a/src/apprt/gtk.zig +++ b/src/apprt/gtk.zig @@ -5,4 +5,6 @@ pub const Surface = @import("gtk/Surface.zig"); test { @import("std").testing.refAllDecls(@This()); + + _ = @import("gtk/key.zig"); } diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index b5647427b..9e1b23556 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -15,6 +15,7 @@ const assert = std.debug.assert; const builtin = @import("builtin"); const glfw = @import("glfw"); const configpkg = @import("../../config.zig"); +const input = @import("../../input.zig"); const Config = configpkg.Config; const CoreApp = @import("../../App.zig"); const CoreSurface = @import("../../Surface.zig"); @@ -23,6 +24,7 @@ const Surface = @import("Surface.zig"); const Window = @import("Window.zig"); const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig"); const c = @import("c.zig"); +const key = @import("key.zig"); const log = std.log.scoped(.gtk); @@ -163,15 +165,19 @@ pub fn reloadConfig(self: *App) !?*const Config { // Update the existing config, be sure to clean up the old one. self.config.deinit(); self.config = config; - - // If there were errors, report them - self.updateConfigErrors() catch |err| { - log.warn("error handling configuration errors err={}", .{err}); + self.syncConfigChanges() catch |err| { + log.warn("error handling configuration changes err={}", .{err}); }; return &self.config; } +/// Call this anytime the configuration changes. +fn syncConfigChanges(self: *App) !void { + try self.updateConfigErrors(); + try self.syncActionAccelerators(); +} + /// This should be called whenever the configuration changes to update /// the state of our config errors window. This will show the window if /// there are new configuration errors and hide the window if the errors @@ -189,6 +195,29 @@ fn updateConfigErrors(self: *App) !void { } } +fn syncActionAccelerators(self: *App) !void { + try self.syncActionAccelerator("app.quit", .{ .quit = {} }); +} + +fn syncActionAccelerator( + self: *App, + gtk_action: [:0]const u8, + action: input.Binding.Action, +) !void { + const trigger = self.config.keybind.set.getTrigger(action) orelse return; + + // Build our accelerator string. + var buf: [256]u8 = undefined; + const accel = try key.accelFromTrigger(&buf, trigger) orelse return; + const accels = [_]?[*:0]const u8{ accel, null }; + + c.gtk_application_set_accels_for_action( + @ptrCast(self.app), + gtk_action.ptr, + &accels, + ); +} + /// Called by CoreApp to wake up the event loop. pub fn wakeup(self: App) void { _ = self; @@ -204,9 +233,10 @@ pub fn run(self: *App) !void { self.initMenu(); // On startup, we want to check for configuration errors right away - // so we can show our error window. - self.updateConfigErrors() catch |err| { - log.warn("error handling configuration errors err={}", .{err}); + // so we can show our error window. We also need to setup other initial + // state. + self.syncConfigChanges() catch |err| { + log.warn("error handling configuration changes err={}", .{err}); }; while (self.running) { @@ -357,9 +387,9 @@ fn gtkActionQuit( fn initActions(self: *App) void { const action_quit = c.g_simple_action_new("quit", null); defer c.g_object_unref(action_quit); - c.g_action_map_add_action(@ptrCast(self.app), @ptrCast(action_quit)); - _ = c.g_signal_connect_data(action_quit, "activate", c.G_CALLBACK(>kActionQuit), self, null, c.G_CONNECT_DEFAULT); + + c.g_action_map_add_action(@ptrCast(self.app), @ptrCast(action_quit)); } /// This sets the self.menu property to the application menu that can be diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 398f2bf85..ad8369c7c 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1008,133 +1008,3 @@ fn translateMods(state: c.GdkModifierType) input.Mods { if (state & c.GDK_LOCK_MASK != 0) mods.caps_lock = true; return mods; } - -fn translateKey(keyval: c.guint) input.Key { - return switch (keyval) { - c.GDK_KEY_a => .a, - c.GDK_KEY_b => .b, - c.GDK_KEY_c => .c, - c.GDK_KEY_d => .d, - c.GDK_KEY_e => .e, - c.GDK_KEY_f => .f, - c.GDK_KEY_g => .g, - c.GDK_KEY_h => .h, - c.GDK_KEY_i => .i, - c.GDK_KEY_j => .j, - c.GDK_KEY_k => .k, - c.GDK_KEY_l => .l, - c.GDK_KEY_m => .m, - c.GDK_KEY_n => .n, - c.GDK_KEY_o => .o, - c.GDK_KEY_p => .p, - c.GDK_KEY_q => .q, - c.GDK_KEY_r => .r, - c.GDK_KEY_s => .s, - c.GDK_KEY_t => .t, - c.GDK_KEY_u => .u, - c.GDK_KEY_v => .v, - c.GDK_KEY_w => .w, - c.GDK_KEY_x => .x, - c.GDK_KEY_y => .y, - c.GDK_KEY_z => .z, - - c.GDK_KEY_0 => .zero, - c.GDK_KEY_1 => .one, - c.GDK_KEY_2 => .two, - c.GDK_KEY_3 => .three, - c.GDK_KEY_4 => .four, - c.GDK_KEY_5 => .five, - c.GDK_KEY_6 => .six, - c.GDK_KEY_7 => .seven, - c.GDK_KEY_8 => .eight, - c.GDK_KEY_9 => .nine, - - c.GDK_KEY_semicolon => .semicolon, - c.GDK_KEY_space => .space, - c.GDK_KEY_apostrophe => .apostrophe, - c.GDK_KEY_comma => .comma, - c.GDK_KEY_grave => .grave_accent, // ` - c.GDK_KEY_period => .period, - c.GDK_KEY_slash => .slash, - c.GDK_KEY_minus => .minus, - c.GDK_KEY_equal => .equal, - c.GDK_KEY_bracketleft => .left_bracket, // [ - c.GDK_KEY_bracketright => .right_bracket, // ] - c.GDK_KEY_backslash => .backslash, // / - - c.GDK_KEY_Up => .up, - c.GDK_KEY_Down => .down, - c.GDK_KEY_Right => .right, - c.GDK_KEY_Left => .left, - c.GDK_KEY_Home => .home, - c.GDK_KEY_End => .end, - c.GDK_KEY_Insert => .insert, - c.GDK_KEY_Delete => .delete, - c.GDK_KEY_Caps_Lock => .caps_lock, - c.GDK_KEY_Scroll_Lock => .scroll_lock, - c.GDK_KEY_Num_Lock => .num_lock, - c.GDK_KEY_Page_Up => .page_up, - c.GDK_KEY_Page_Down => .page_down, - c.GDK_KEY_Escape => .escape, - c.GDK_KEY_Return => .enter, - c.GDK_KEY_Tab => .tab, - c.GDK_KEY_BackSpace => .backspace, - c.GDK_KEY_Print => .print_screen, - c.GDK_KEY_Pause => .pause, - - c.GDK_KEY_F1 => .f1, - c.GDK_KEY_F2 => .f2, - c.GDK_KEY_F3 => .f3, - c.GDK_KEY_F4 => .f4, - c.GDK_KEY_F5 => .f5, - c.GDK_KEY_F6 => .f6, - c.GDK_KEY_F7 => .f7, - c.GDK_KEY_F8 => .f8, - c.GDK_KEY_F9 => .f9, - c.GDK_KEY_F10 => .f10, - c.GDK_KEY_F11 => .f11, - c.GDK_KEY_F12 => .f12, - c.GDK_KEY_F13 => .f13, - c.GDK_KEY_F14 => .f14, - c.GDK_KEY_F15 => .f15, - c.GDK_KEY_F16 => .f16, - c.GDK_KEY_F17 => .f17, - c.GDK_KEY_F18 => .f18, - c.GDK_KEY_F19 => .f19, - c.GDK_KEY_F20 => .f20, - c.GDK_KEY_F21 => .f21, - c.GDK_KEY_F22 => .f22, - c.GDK_KEY_F23 => .f23, - c.GDK_KEY_F24 => .f24, - c.GDK_KEY_F25 => .f25, - - c.GDK_KEY_KP_0 => .kp_0, - c.GDK_KEY_KP_1 => .kp_1, - c.GDK_KEY_KP_2 => .kp_2, - c.GDK_KEY_KP_3 => .kp_3, - c.GDK_KEY_KP_4 => .kp_4, - c.GDK_KEY_KP_5 => .kp_5, - c.GDK_KEY_KP_6 => .kp_6, - c.GDK_KEY_KP_7 => .kp_7, - c.GDK_KEY_KP_8 => .kp_8, - c.GDK_KEY_KP_9 => .kp_9, - c.GDK_KEY_KP_Decimal => .kp_decimal, - c.GDK_KEY_KP_Divide => .kp_divide, - c.GDK_KEY_KP_Multiply => .kp_multiply, - c.GDK_KEY_KP_Subtract => .kp_subtract, - c.GDK_KEY_KP_Add => .kp_add, - c.GDK_KEY_KP_Enter => .kp_enter, - c.GDK_KEY_KP_Equal => .kp_equal, - - c.GDK_KEY_Shift_L => .left_shift, - c.GDK_KEY_Control_L => .left_control, - c.GDK_KEY_Alt_L => .left_alt, - c.GDK_KEY_Super_L => .left_super, - c.GDK_KEY_Shift_R => .right_shift, - c.GDK_KEY_Control_R => .right_control, - c.GDK_KEY_Alt_R => .right_alt, - c.GDK_KEY_Super_R => .right_super, - - else => .invalid, - }; -} diff --git a/src/apprt/gtk/key.zig b/src/apprt/gtk/key.zig new file mode 100644 index 000000000..6f3a5cc81 --- /dev/null +++ b/src/apprt/gtk/key.zig @@ -0,0 +1,201 @@ +const std = @import("std"); +const input = @import("../../input.zig"); +const c = @import("c.zig"); + +/// Returns a GTK accelerator string from a trigger. +pub fn accelFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u8 { + var buf_stream = std.io.fixedBufferStream(buf); + const writer = buf_stream.writer(); + + // Modifiers + if (trigger.mods.shift) try writer.writeAll(""); + if (trigger.mods.ctrl) try writer.writeAll(""); + if (trigger.mods.alt) try writer.writeAll(""); + if (trigger.mods.super) try writer.writeAll(""); + + // Write our key + const keyval = keyvalFromKey(trigger.key) orelse return null; + try writer.writeAll(std.mem.sliceTo(c.gdk_keyval_name(keyval), 0)); + + // We need to make the string null terminated. + try writer.writeByte(0); + const slice = buf_stream.getWritten(); + return slice[0 .. slice.len - 1 :0]; +} + +pub fn translateMods(state: c.GdkModifierType) input.Mods { + var mods: input.Mods = .{}; + if (state & c.GDK_SHIFT_MASK != 0) mods.shift = true; + if (state & c.GDK_CONTROL_MASK != 0) mods.ctrl = true; + if (state & c.GDK_ALT_MASK != 0) mods.alt = true; + if (state & c.GDK_SUPER_MASK != 0) mods.super = true; + + // Lock is dependent on the X settings but we just assume caps lock. + if (state & c.GDK_LOCK_MASK != 0) mods.caps_lock = true; + return mods; +} + +/// Returns an input key from a keyval or null if we don't have a mapping. +pub fn keyFromKeyval(keyval: c.guint) ?input.Key { + for (keymap) |entry| { + if (entry[0] == keyval) return entry[1]; + } + + return null; +} + +/// Returns a keyval from an input key or null if we don't have a mapping. +pub fn keyvalFromKey(key: input.Key) ?c.guint { + switch (key) { + inline else => |key_comptime| { + return comptime value: { + @setEvalBranchQuota(10_000); + for (keymap) |entry| { + if (entry[1] == key_comptime) break :value entry[0]; + } + + break :value null; + }; + }, + } +} + +test "accelFromTrigger" { + const testing = std.testing; + var buf: [256]u8 = undefined; + + try testing.expectEqualStrings("q", (try accelFromTrigger(&buf, .{ + .mods = .{ .super = true }, + .key = .q, + })).?); +} + +/// A raw entry in the keymap. Our keymap contains mappings between +/// GDK keys and our own key enum. +const RawEntry = struct { c.guint, input.Key }; + +const keymap: []const RawEntry = &.{ + .{ c.GDK_KEY_a, .a }, + .{ c.GDK_KEY_b, .b }, + .{ c.GDK_KEY_c, .c }, + .{ c.GDK_KEY_d, .d }, + .{ c.GDK_KEY_e, .e }, + .{ c.GDK_KEY_f, .f }, + .{ c.GDK_KEY_g, .g }, + .{ c.GDK_KEY_h, .h }, + .{ c.GDK_KEY_i, .i }, + .{ c.GDK_KEY_j, .j }, + .{ c.GDK_KEY_k, .k }, + .{ c.GDK_KEY_l, .l }, + .{ c.GDK_KEY_m, .m }, + .{ c.GDK_KEY_n, .n }, + .{ c.GDK_KEY_o, .o }, + .{ c.GDK_KEY_p, .p }, + .{ c.GDK_KEY_q, .q }, + .{ c.GDK_KEY_r, .r }, + .{ c.GDK_KEY_s, .s }, + .{ c.GDK_KEY_t, .t }, + .{ c.GDK_KEY_u, .u }, + .{ c.GDK_KEY_v, .v }, + .{ c.GDK_KEY_w, .w }, + .{ c.GDK_KEY_x, .x }, + .{ c.GDK_KEY_y, .y }, + .{ c.GDK_KEY_z, .z }, + + .{ c.GDK_KEY_0, .zero }, + .{ c.GDK_KEY_1, .one }, + .{ c.GDK_KEY_2, .two }, + .{ c.GDK_KEY_3, .three }, + .{ c.GDK_KEY_4, .four }, + .{ c.GDK_KEY_5, .five }, + .{ c.GDK_KEY_6, .six }, + .{ c.GDK_KEY_7, .seven }, + .{ c.GDK_KEY_8, .eight }, + .{ c.GDK_KEY_9, .nine }, + + .{ c.GDK_KEY_semicolon, .semicolon }, + .{ c.GDK_KEY_space, .space }, + .{ c.GDK_KEY_apostrophe, .apostrophe }, + .{ c.GDK_KEY_comma, .comma }, + .{ c.GDK_KEY_grave, .grave_accent }, + .{ c.GDK_KEY_period, .period }, + .{ c.GDK_KEY_slash, .slash }, + .{ c.GDK_KEY_minus, .minus }, + .{ c.GDK_KEY_equal, .equal }, + .{ c.GDK_KEY_bracketleft, .left_bracket }, + .{ c.GDK_KEY_bracketright, .right_bracket }, + .{ c.GDK_KEY_backslash, .backslash }, + + .{ c.GDK_KEY_Up, .up }, + .{ c.GDK_KEY_Down, .down }, + .{ c.GDK_KEY_Right, .right }, + .{ c.GDK_KEY_Left, .left }, + .{ c.GDK_KEY_Home, .home }, + .{ c.GDK_KEY_End, .end }, + .{ c.GDK_KEY_Insert, .insert }, + .{ c.GDK_KEY_Delete, .delete }, + .{ c.GDK_KEY_Caps_Lock, .caps_lock }, + .{ c.GDK_KEY_Scroll_Lock, .scroll_lock }, + .{ c.GDK_KEY_Num_Lock, .num_lock }, + .{ c.GDK_KEY_Page_Up, .page_up }, + .{ c.GDK_KEY_Page_Down, .page_down }, + .{ c.GDK_KEY_Escape, .escape }, + .{ c.GDK_KEY_Return, .enter }, + .{ c.GDK_KEY_Tab, .tab }, + .{ c.GDK_KEY_BackSpace, .backspace }, + .{ c.GDK_KEY_Print, .print_screen }, + .{ c.GDK_KEY_Pause, .pause }, + + .{ c.GDK_KEY_F1, .f1 }, + .{ c.GDK_KEY_F2, .f2 }, + .{ c.GDK_KEY_F3, .f3 }, + .{ c.GDK_KEY_F4, .f4 }, + .{ c.GDK_KEY_F5, .f5 }, + .{ c.GDK_KEY_F6, .f6 }, + .{ c.GDK_KEY_F7, .f7 }, + .{ c.GDK_KEY_F8, .f8 }, + .{ c.GDK_KEY_F9, .f9 }, + .{ c.GDK_KEY_F10, .f10 }, + .{ c.GDK_KEY_F11, .f11 }, + .{ c.GDK_KEY_F12, .f12 }, + .{ c.GDK_KEY_F13, .f13 }, + .{ c.GDK_KEY_F14, .f14 }, + .{ c.GDK_KEY_F15, .f15 }, + .{ c.GDK_KEY_F16, .f16 }, + .{ c.GDK_KEY_F17, .f17 }, + .{ c.GDK_KEY_F18, .f18 }, + .{ c.GDK_KEY_F19, .f19 }, + .{ c.GDK_KEY_F20, .f20 }, + .{ c.GDK_KEY_F21, .f21 }, + .{ c.GDK_KEY_F22, .f22 }, + .{ c.GDK_KEY_F23, .f23 }, + .{ c.GDK_KEY_F24, .f24 }, + .{ c.GDK_KEY_F25, .f25 }, + + .{ c.GDK_KEY_KP_0, .kp_0 }, + .{ c.GDK_KEY_KP_1, .kp_1 }, + .{ c.GDK_KEY_KP_2, .kp_2 }, + .{ c.GDK_KEY_KP_3, .kp_3 }, + .{ c.GDK_KEY_KP_4, .kp_4 }, + .{ c.GDK_KEY_KP_5, .kp_5 }, + .{ c.GDK_KEY_KP_6, .kp_6 }, + .{ c.GDK_KEY_KP_7, .kp_7 }, + .{ c.GDK_KEY_KP_8, .kp_8 }, + .{ c.GDK_KEY_KP_9, .kp_9 }, + .{ c.GDK_KEY_KP_Decimal, .kp_decimal }, + .{ c.GDK_KEY_KP_Divide, .kp_divide }, + .{ c.GDK_KEY_KP_Multiply, .kp_multiply }, + .{ c.GDK_KEY_KP_Subtract, .kp_subtract }, + .{ c.GDK_KEY_KP_Add, .kp_add }, + .{ c.GDK_KEY_KP_Enter, .kp_enter }, + .{ c.GDK_KEY_KP_Equal, .kp_equal }, + + .{ c.GDK_KEY_Shift_L, .left_shift }, + .{ c.GDK_KEY_Control_L, .left_control }, + .{ c.GDK_KEY_Alt_L, .left_alt }, + .{ c.GDK_KEY_Super_L, .left_super }, + .{ c.GDK_KEY_Shift_R, .right_shift }, + .{ c.GDK_KEY_Control_R, .right_control }, + .{ c.GDK_KEY_Alt_R, .right_alt }, + .{ c.GDK_KEY_Super_R, .right_super }, +}; diff --git a/src/main.zig b/src/main.zig index 10a9d62ae..c61f94c5e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -259,6 +259,7 @@ test { _ = @import("Pty.zig"); _ = @import("Command.zig"); _ = @import("font/main.zig"); + _ = @import("apprt.zig"); _ = @import("renderer.zig"); _ = @import("termio.zig"); _ = @import("input.zig"); From 71ca254be845b773095d5ed67684e55a57c1cf45 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 14:41:52 -0700 Subject: [PATCH 04/11] apprt/gtk: window actions --- src/apprt/gtk/App.zig | 2 ++ src/apprt/gtk/Window.zig | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 9e1b23556..a33f1ce51 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -197,6 +197,7 @@ fn updateConfigErrors(self: *App) !void { fn syncActionAccelerators(self: *App) !void { try self.syncActionAccelerator("app.quit", .{ .quit = {} }); + try self.syncActionAccelerator("win.close", .{ .close_surface = {} }); } fn syncActionAccelerator( @@ -397,6 +398,7 @@ fn initActions(self: *App) void { fn initMenu(self: *App) void { const menu = c.g_menu_new(); errdefer c.g_object_unref(menu); + c.g_menu_append(menu, "Close", "win.close"); c.g_menu_append(menu, "Quit", "app.quit"); // { diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 86ef1cbd8..cf6e109c5 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -145,6 +145,9 @@ pub fn init(self: *Window, app: *App) !void { _ = c.g_signal_connect_data(notebook, "switch-page", c.G_CALLBACK(>kSwitchPage), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(notebook, "create-window", c.G_CALLBACK(>kNotebookCreateWindow), self, null, c.G_CONNECT_DEFAULT); + // Our actions for the menu + initActions(self); + // The box is our main child c.gtk_window_set_child(gtk_window, box); @@ -152,6 +155,17 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_widget_show(window); } +/// Sets up the GTK actions for the window scope. Actions are how GTK handles +/// 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 { + const action_close = c.g_simple_action_new("close", null); + defer c.g_object_unref(action_close); + _ = c.g_signal_connect_data(action_close, "activate", c.G_CALLBACK(>kActionClose), self, null, c.G_CONNECT_DEFAULT); + + c.g_action_map_add_action(@ptrCast(self.window), @ptrCast(action_close)); +} + pub fn deinit(self: *Window) void { if (self.icon_search_dir) |ptr| self.app.core_app.alloc.free(ptr); } @@ -465,6 +479,19 @@ fn getNotebookPageIndex(page: *c.GtkNotebookPage) c_int { return c.g_value_get_int(&value); } +fn gtkActionClose( + _: *c.GSimpleAction, + _: *c.GVariant, + ud: ?*anyopaque, +) callconv(.C) void { + const self: *Window = @ptrCast(@alignCast(ud orelse return)); + const surface = self.app.core_app.focusedSurface() orelse return; + surface.performBindingAction(.{ .close_surface = {} }) catch |err| { + log.warn("error performing binding action error={}", .{err}); + return; + }; +} + fn userdataSelf(ud: *anyopaque) *Window { return @ptrCast(@alignCast(ud)); } From 7996be25a8a93145321fea7a93ea0f0055f4f50e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 14:51:19 -0700 Subject: [PATCH 05/11] apprt/gtk: new window, tab --- src/apprt/gtk/App.zig | 18 +++++++++++++-- src/apprt/gtk/Window.zig | 48 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index a33f1ce51..39a2ec62f 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -398,8 +398,22 @@ fn initActions(self: *App) void { fn initMenu(self: *App) void { const menu = c.g_menu_new(); errdefer c.g_object_unref(menu); - c.g_menu_append(menu, "Close", "win.close"); - c.g_menu_append(menu, "Quit", "app.quit"); + + { + const section = c.g_menu_new(); + defer c.g_object_unref(section); + c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); + c.g_menu_append(section, "New Window", "win.new_window"); + c.g_menu_append(section, "New Tab", "win.new_tab"); + } + + { + const section = c.g_menu_new(); + defer c.g_object_unref(section); + c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); + c.g_menu_append(section, "Close Window", "win.close"); + c.g_menu_append(section, "Quit Ghostty", "app.quit"); + } // { // const section = c.g_menu_new(); diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index cf6e109c5..20b5510fb 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -159,11 +159,25 @@ pub fn init(self: *Window, app: *App) !void { /// 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 { - const action_close = c.g_simple_action_new("close", null); - defer c.g_object_unref(action_close); - _ = c.g_signal_connect_data(action_close, "activate", c.G_CALLBACK(>kActionClose), self, null, c.G_CONNECT_DEFAULT); + const actions = .{ + .{ "close", >kActionClose }, + .{ "new_window", >kActionNewWindow }, + .{ "new_tab", >kActionNewTab }, + }; - c.g_action_map_add_action(@ptrCast(self.window), @ptrCast(action_close)); + 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( + action, + "activate", + c.G_CALLBACK(entry[1]), + self, + null, + c.G_CONNECT_DEFAULT, + ); + c.g_action_map_add_action(@ptrCast(self.window), @ptrCast(action)); + } } pub fn deinit(self: *Window) void { @@ -492,6 +506,32 @@ fn gtkActionClose( }; } +fn gtkActionNewWindow( + _: *c.GSimpleAction, + _: *c.GVariant, + ud: ?*anyopaque, +) callconv(.C) void { + const self: *Window = @ptrCast(@alignCast(ud orelse return)); + const surface = self.app.core_app.focusedSurface() orelse return; + surface.performBindingAction(.{ .new_window = {} }) catch |err| { + log.warn("error performing binding action error={}", .{err}); + return; + }; +} + +fn gtkActionNewTab( + _: *c.GSimpleAction, + _: *c.GVariant, + ud: ?*anyopaque, +) callconv(.C) void { + const self: *Window = @ptrCast(@alignCast(ud orelse return)); + const surface = self.app.core_app.focusedSurface() orelse return; + surface.performBindingAction(.{ .new_tab = {} }) catch |err| { + log.warn("error performing binding action error={}", .{err}); + return; + }; +} + fn userdataSelf(ud: *anyopaque) *Window { return @ptrCast(@alignCast(ud)); } From c3a72b91880d9ca13623307c036de93ab68b378d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 14:55:37 -0700 Subject: [PATCH 06/11] apprt/gtk: add new tab button in header --- src/apprt/gtk/Window.zig | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 20b5510fb..c8e366c3d 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -98,6 +98,11 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_menu_button_set_menu_model(@ptrCast(btn), @ptrCast(@alignCast(app.menu))); c.gtk_header_bar_pack_end(@ptrCast(header), btn); } + { + const btn = c.gtk_button_new_from_icon_name("tab-new-symbolic"); + c.gtk_header_bar_pack_end(@ptrCast(header), btn); + _ = c.g_signal_connect_data(btn, "clicked", c.G_CALLBACK(>kActionNewTab), self, null, c.G_CONNECT_DEFAULT); + } // Hide window decoration if configured. This has to happen before // `gtk_widget_show`. @@ -119,10 +124,6 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_widget_set_vexpand(notebook_widget, 1); c.gtk_widget_set_hexpand(notebook_widget, 1); - // Create our add button for new tabs - const notebook_add_btn = c.gtk_button_new_from_icon_name("list-add-symbolic"); - c.gtk_notebook_set_action_widget(notebook, notebook_add_btn, c.GTK_PACK_END); - // Create our box which will hold our widgets. const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0); @@ -139,7 +140,6 @@ pub fn init(self: *Window, app: *App) !void { // All of our events _ = c.g_signal_connect_data(window, "close-request", c.G_CALLBACK(>kCloseRequest), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(>kDestroy), self, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(notebook_add_btn, "clicked", c.G_CALLBACK(>kTabAddClick), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(notebook, "page-added", c.G_CALLBACK(>kPageAdded), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(notebook, "page-removed", c.G_CALLBACK(>kPageRemoved), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(notebook, "switch-page", c.G_CALLBACK(>kSwitchPage), self, null, c.G_CONNECT_DEFAULT); @@ -336,15 +336,6 @@ fn focusCurrentTab(self: *Window) void { _ = c.gtk_widget_grab_focus(widget); } -fn gtkTabAddClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void { - const self = userdataSelf(ud.?); - const parent = self.app.core_app.focusedSurface(); - self.newTab(parent) catch |err| { - log.warn("error adding new tab: {}", .{err}); - return; - }; -} - fn gtkTabCloseClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void { const surface: *Surface = @ptrCast(@alignCast(ud)); surface.core_surface.close(); From 844945e7f9e347b8d8570db5d3a3e9b3f1107ccc Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 15:00:54 -0700 Subject: [PATCH 07/11] apprt/gtk: add reload config --- src/apprt/gtk/App.zig | 42 ++++++++++++++++++++++++++++++++++++---- src/apprt/gtk/Window.zig | 2 ++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 39a2ec62f..284fb2b1d 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -197,7 +197,10 @@ fn updateConfigErrors(self: *App) !void { fn syncActionAccelerators(self: *App) !void { try self.syncActionAccelerator("app.quit", .{ .quit = {} }); + try self.syncActionAccelerator("app.reload_config", .{ .reload_config = {} }); try self.syncActionAccelerator("win.close", .{ .close_surface = {} }); + try self.syncActionAccelerator("win.new_window", .{ .new_window = {} }); + try self.syncActionAccelerator("win.new_tab", .{ .new_tab = {} }); } fn syncActionAccelerator( @@ -371,6 +374,17 @@ fn gtkActivate(app: *c.GtkApplication, ud: ?*anyopaque) callconv(.C) void { }, .{ .forever = {} }); } +fn gtkActionReloadConfig( + _: *c.GSimpleAction, + _: *c.GVariant, + ud: ?*anyopaque, +) callconv(.C) void { + const self: *App = @ptrCast(@alignCast(ud orelse return)); + _ = self.core_app.mailbox.push(.{ + .reload_config = {}, + }, .{ .forever = {} }); +} + fn gtkActionQuit( _: *c.GSimpleAction, _: *c.GVariant, @@ -386,11 +400,24 @@ fn gtkActionQuit( /// 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 { - const action_quit = c.g_simple_action_new("quit", null); - defer c.g_object_unref(action_quit); - _ = c.g_signal_connect_data(action_quit, "activate", c.G_CALLBACK(>kActionQuit), self, null, c.G_CONNECT_DEFAULT); + const actions = .{ + .{ "quit", >kActionQuit }, + .{ "reload_config", >kActionReloadConfig }, + }; - c.g_action_map_add_action(@ptrCast(self.app), @ptrCast(action_quit)); + 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( + action, + "activate", + c.G_CALLBACK(entry[1]), + self, + null, + c.G_CONNECT_DEFAULT, + ); + c.g_action_map_add_action(@ptrCast(self.app), @ptrCast(action)); + } } /// This sets the self.menu property to the application menu that can be @@ -407,6 +434,13 @@ fn initMenu(self: *App) void { c.g_menu_append(section, "New Tab", "win.new_tab"); } + { + const section = c.g_menu_new(); + defer c.g_object_unref(section); + c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); + c.g_menu_append(section, "Reload Configuration", "app.reload_config"); + } + { const section = c.g_menu_new(); defer c.g_object_unref(section); diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index c8e366c3d..c1e0bf155 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -94,12 +94,14 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_window_set_titlebar(gtk_window, header); { const btn = c.gtk_menu_button_new(); + c.gtk_widget_set_tooltip_text(btn, "Main Menu"); c.gtk_menu_button_set_icon_name(@ptrCast(btn), "open-menu-symbolic"); c.gtk_menu_button_set_menu_model(@ptrCast(btn), @ptrCast(@alignCast(app.menu))); c.gtk_header_bar_pack_end(@ptrCast(header), btn); } { const btn = c.gtk_button_new_from_icon_name("tab-new-symbolic"); + c.gtk_widget_set_tooltip_text(btn, "New Tab"); c.gtk_header_bar_pack_end(@ptrCast(header), btn); _ = c.g_signal_connect_data(btn, "clicked", c.G_CALLBACK(>kActionNewTab), self, null, c.G_CONNECT_DEFAULT); } From bf665b7c636a370a0c54e3d9350e87ec45c5e8c8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 15:03:36 -0700 Subject: [PATCH 08/11] apprt/gtk: reset accelerators initially --- src/apprt/gtk/App.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 284fb2b1d..9f30eaac7 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -208,9 +208,11 @@ fn syncActionAccelerator( gtk_action: [:0]const u8, action: input.Binding.Action, ) !void { - const trigger = self.config.keybind.set.getTrigger(action) orelse return; + // Reset it initially + const zero = [_]?[*:0]const u8{null}; + c.gtk_application_set_accels_for_action(@ptrCast(self.app), gtk_action.ptr, &zero); - // Build our accelerator string. + const trigger = self.config.keybind.set.getTrigger(action) orelse return; var buf: [256]u8 = undefined; const accel = try key.accelFromTrigger(&buf, trigger) orelse return; const accels = [_]?[*:0]const u8{ accel, null }; From f3662354e586427994e8e33618d62c61d6fa583f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 15:16:35 -0700 Subject: [PATCH 09/11] apprt/gtk: about window --- build.zig | 6 +++++- src/apprt/gtk/App.zig | 8 +++++++- src/apprt/gtk/Window.zig | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/build.zig b/build.zig index 7d667db0d..f002f6fd2 100644 --- a/build.zig +++ b/build.zig @@ -206,7 +206,11 @@ pub fn build(b: *std.Build) !void { const exe_options = b.addOptions(); exe_options.addOption(std.SemanticVersion, "app_version", version); - exe_options.addOption([]const u8, "app_version_string", b.fmt("{}", .{version})); + exe_options.addOption([:0]const u8, "app_version_string", try std.fmt.allocPrintZ( + b.allocator, + "{}", + .{version}, + )); exe_options.addOption(bool, "tracy_enabled", tracy); exe_options.addOption(bool, "flatpak", flatpak); exe_options.addOption(apprt.Runtime, "app_runtime", app_runtime); diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 9f30eaac7..73e8cdc29 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -448,7 +448,13 @@ fn initMenu(self: *App) void { defer c.g_object_unref(section); c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); c.g_menu_append(section, "Close Window", "win.close"); - c.g_menu_append(section, "Quit Ghostty", "app.quit"); + } + + { + const section = c.g_menu_new(); + defer c.g_object_unref(section); + c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); + c.g_menu_append(section, "About Ghostty", "win.about"); } // { diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index c1e0bf155..45d9dfb3a 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -3,6 +3,7 @@ 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 configpkg = @import("../../config.zig"); @@ -162,6 +163,7 @@ pub fn init(self: *Window, app: *App) !void { /// here. The string name binds them. fn initActions(self: *Window) void { const actions = .{ + .{ "about", >kActionAbout }, .{ "close", >kActionClose }, .{ "new_window", >kActionNewWindow }, .{ "new_tab", >kActionNewTab }, @@ -486,6 +488,29 @@ fn getNotebookPageIndex(page: *c.GtkNotebookPage) c_int { return c.g_value_get_int(&value); } +fn gtkActionAbout( + _: *c.GSimpleAction, + _: *c.GVariant, + ud: ?*anyopaque, +) callconv(.C) void { + const self: *Window = @ptrCast(@alignCast(ud orelse return)); + + c.gtk_show_about_dialog( + self.window, + "program-name", + "Ghostty", + "logo-icon-name", + "com.mitchellh.ghostty", + "title", + "About Ghostty", + "version", + build_config.version_string.ptr, + "website", + "https://github.com/mitchellh/ghostty", + @as(?*anyopaque, null), + ); +} + fn gtkActionClose( _: *c.GSimpleAction, _: *c.GVariant, From ed0a2ecfc4c259d9f57b6efbe3f4d0fbb3ff9353 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 15:33:11 -0700 Subject: [PATCH 10/11] apprt/gtk: window actions should use currently active surface --- src/apprt/gtk/App.zig | 14 +------------- src/apprt/gtk/Window.zig | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 73e8cdc29..f349f6818 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -434,19 +434,6 @@ fn initMenu(self: *App) void { c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); c.g_menu_append(section, "New Window", "win.new_window"); c.g_menu_append(section, "New Tab", "win.new_tab"); - } - - { - const section = c.g_menu_new(); - defer c.g_object_unref(section); - c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); - c.g_menu_append(section, "Reload Configuration", "app.reload_config"); - } - - { - const section = c.g_menu_new(); - defer c.g_object_unref(section); - c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); c.g_menu_append(section, "Close Window", "win.close"); } @@ -454,6 +441,7 @@ fn initMenu(self: *App) void { const section = c.g_menu_new(); defer c.g_object_unref(section); c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); + c.g_menu_append(section, "Reload Configuration", "app.reload_config"); c.g_menu_append(section, "About Ghostty", "win.about"); } diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 45d9dfb3a..947088723 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -517,7 +517,7 @@ fn gtkActionClose( ud: ?*anyopaque, ) callconv(.C) void { const self: *Window = @ptrCast(@alignCast(ud orelse return)); - const surface = self.app.core_app.focusedSurface() orelse return; + const surface = self.actionSurface() orelse return; surface.performBindingAction(.{ .close_surface = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); return; @@ -530,7 +530,7 @@ fn gtkActionNewWindow( ud: ?*anyopaque, ) callconv(.C) void { const self: *Window = @ptrCast(@alignCast(ud orelse return)); - const surface = self.app.core_app.focusedSurface() orelse return; + const surface = self.actionSurface() orelse return; surface.performBindingAction(.{ .new_window = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); return; @@ -543,13 +543,23 @@ fn gtkActionNewTab( ud: ?*anyopaque, ) callconv(.C) void { const self: *Window = @ptrCast(@alignCast(ud orelse return)); - const surface = self.app.core_app.focusedSurface() orelse return; + const surface = self.actionSurface() orelse return; surface.performBindingAction(.{ .new_tab = {} }) catch |err| { log.warn("error performing binding action error={}", .{err}); return; }; } +/// Returns the surface to use for an action. +fn actionSurface(self: *Window) ?*CoreSurface { + const page_idx = c.gtk_notebook_get_current_page(self.notebook); + const page = c.gtk_notebook_get_nth_page(self.notebook, page_idx); + const surface: *Surface = @ptrCast(@alignCast( + c.g_object_get_data(@ptrCast(page), GL_AREA_SURFACE) orelse return null, + )); + return &surface.core_surface; +} + fn userdataSelf(ud: *anyopaque) *Window { return @ptrCast(@alignCast(ud)); } From 90b5fdeede4742e19ab351aa63660e70e60e7273 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Sep 2023 15:46:57 -0700 Subject: [PATCH 11/11] ci: don't run gtk tests in CI --- .github/workflows/test.yml | 2 +- nix/devshell.nix | 6 ------ src/apprt.zig | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b3ac1492e..5bbea409b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -87,7 +87,7 @@ jobs: nix_path: nixpkgs=channel:nixos-unstable - name: test - run: nix develop -c zig build test + run: nix develop -c zig build -Dapp-runtime=none test - name: Test GTK Build run: nix develop -c zig build -Dapp-runtime=gtk diff --git a/nix/devshell.nix b/nix/devshell.nix index bda7efe55..d8995e189 100644 --- a/nix/devshell.nix +++ b/nix/devshell.nix @@ -1,8 +1,6 @@ { mkShell, lib, stdenv , bashInteractive -, debugedit -, flatpak-builder , gdb , glxinfo , ncurses @@ -92,10 +90,6 @@ in mkShell rec { # by default so we have to include this. bashInteractive - # Flatpak builds - debugedit - flatpak-builder - gdb valgrind wraptest diff --git a/src/apprt.zig b/src/apprt.zig index e979588a9..50e8a6ba7 100644 --- a/src/apprt.zig +++ b/src/apprt.zig @@ -25,7 +25,7 @@ pub const surface = @import("apprt/surface.zig"); /// Window or something. pub const runtime = switch (build_config.artifact) { .exe => switch (build_config.app_runtime) { - .none => @compileError("exe with no runtime not allowed"), + .none => struct {}, .glfw => glfw, .gtk => gtk, },