diff --git a/src/apprt/gtk-ng/class/application.zig b/src/apprt/gtk-ng/class/application.zig index 4f4cc77d4..d653d3e99 100644 --- a/src/apprt/gtk-ng/class/application.zig +++ b/src/apprt/gtk-ng/class/application.zig @@ -15,6 +15,7 @@ const apprt = @import("../../../apprt.zig"); const cgroup = @import("../cgroup.zig"); const CoreApp = @import("../../../App.zig"); const configpkg = @import("../../../config.zig"); +const input = @import("../../../input.zig"); const internal_os = @import("../../../os/main.zig"); const systemd = @import("../../../os/systemd.zig"); const terminal = @import("../../../terminal/main.zig"); @@ -24,6 +25,7 @@ const CoreConfig = configpkg.Config; const CoreSurface = @import("../../../Surface.zig"); const ext = @import("../ext.zig"); +const key = @import("../key.zig"); const adw_version = @import("../adw_version.zig"); const gtk_version = @import("../gtk_version.zig"); const winprotopkg = @import("../winproto.zig"); @@ -861,6 +863,61 @@ pub const Application = extern struct { } } + fn syncActionAccelerators(self: *Self) void { + self.syncActionAccelerator("app.quit", .{ .quit = {} }); + self.syncActionAccelerator("app.open-config", .{ .open_config = {} }); + self.syncActionAccelerator("app.reload-config", .{ .reload_config = {} }); + self.syncActionAccelerator("win.toggle-inspector", .{ .inspector = .toggle }); + self.syncActionAccelerator("app.show-gtk-inspector", .show_gtk_inspector); + self.syncActionAccelerator("win.toggle-command-palette", .toggle_command_palette); + self.syncActionAccelerator("win.close", .{ .close_window = {} }); + self.syncActionAccelerator("win.new-window", .{ .new_window = {} }); + self.syncActionAccelerator("win.new-tab", .{ .new_tab = {} }); + self.syncActionAccelerator("win.close-tab", .{ .close_tab = {} }); + self.syncActionAccelerator("win.split-right", .{ .new_split = .right }); + self.syncActionAccelerator("win.split-down", .{ .new_split = .down }); + self.syncActionAccelerator("win.split-left", .{ .new_split = .left }); + self.syncActionAccelerator("win.split-up", .{ .new_split = .up }); + self.syncActionAccelerator("win.copy", .{ .copy_to_clipboard = {} }); + self.syncActionAccelerator("win.paste", .{ .paste_from_clipboard = {} }); + self.syncActionAccelerator("win.reset", .{ .reset = {} }); + self.syncActionAccelerator("win.clear", .{ .clear_screen = {} }); + self.syncActionAccelerator("win.prompt-title", .{ .prompt_surface_title = {} }); + } + + fn syncActionAccelerator( + self: *Self, + gtk_action: [:0]const u8, + action: input.Binding.Action, + ) void { + const gtk_app = self.as(gtk.Application); + + // Reset it initially + const zero = [_:null]?[*:0]const u8{}; + gtk_app.setAccelsForAction(gtk_action, &zero); + + const config = self.private().config.get(); + const trigger = config.keybind.set.getTrigger(action) orelse return; + var buf: [1024]u8 = undefined; + const accel = if (key.accelFromTrigger( + &buf, + trigger, + )) |accel_| + accel_ orelse return + else |err| switch (err) { + // This should really never, never happen. Its not critical enough + // to actually crash, but this is a bug somewhere. An accelerator + // for a trigger can't possibly be more than 1024 bytes. + error.NoSpaceLeft => { + log.warn("accelerator somehow longer than 1024 bytes: {}", .{trigger}); + return; + }, + }; + const accels = [_:null]?[*:0]const u8{accel}; + + gtk_app.setAccelsForAction(gtk_action, &accels); + } + //--------------------------------------------------------------- // Properties @@ -891,6 +948,9 @@ pub const Application = extern struct { _: *gobject.ParamSpec, self: *Self, ) callconv(.c) void { + // Sync our accelerators for menu items. + self.syncActionAccelerators(); + // Load our runtime and custom CSS. If this fails then our window is // just stuck with the old CSS but we don't want to fail the entire // config change operation. diff --git a/src/apprt/gtk-ng/class/global_shortcuts.zig b/src/apprt/gtk-ng/class/global_shortcuts.zig index b15a0d5a9..512dac57e 100644 --- a/src/apprt/gtk-ng/class/global_shortcuts.zig +++ b/src/apprt/gtk-ng/class/global_shortcuts.zig @@ -152,7 +152,7 @@ pub const GlobalShortcuts = extern struct { } } - fn refresh(self: *Self) !void { + fn refresh(self: *Self) Allocator.Error!void { // Always close our previous state first. self.close(); @@ -173,7 +173,7 @@ pub const GlobalShortcuts = extern struct { errdefer priv.map = .{}; // Update map - var trigger_buf: [256]u8 = undefined; + var trigger_buf: [1024]u8 = undefined; var it = config.keybind.set.bindings.iterator(); while (it.next()) |entry| { const leaf = switch (entry.value_ptr.*) { @@ -183,10 +183,23 @@ pub const GlobalShortcuts = extern struct { }; if (!leaf.flags.global) continue; - const trigger = try key.xdgShortcutFromTrigger( + const trigger = if (key.xdgShortcutFromTrigger( &trigger_buf, entry.key_ptr.*, - ) orelse continue; + )) |shortcut_| + shortcut_ orelse continue + else |err| switch (err) { + // If there isn't space to translate the trigger, then our + // buffer might be too small (but 1024 is insane!). In any case + // we don't want to stop registering globals. + error.NoSpaceLeft => { + log.warn( + "buffer too small to translate trigger, ignoring={}", + .{entry.key_ptr.*}, + ); + continue; + }, + }; try priv.map.put( alloc, diff --git a/src/apprt/gtk-ng/key.zig b/src/apprt/gtk-ng/key.zig index fc3296366..344d7de43 100644 --- a/src/apprt/gtk-ng/key.zig +++ b/src/apprt/gtk-ng/key.zig @@ -9,7 +9,10 @@ const input = @import("../../input.zig"); const winproto = @import("winproto.zig"); /// Returns a GTK accelerator string from a trigger. -pub fn accelFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u8 { +pub fn accelFromTrigger( + buf: []u8, + trigger: input.Binding.Trigger, +) error{NoSpaceLeft}!?[:0]const u8 { var buf_stream = std.io.fixedBufferStream(buf); const writer = buf_stream.writer(); @@ -30,7 +33,10 @@ pub fn accelFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u /// Returns a XDG-compliant shortcuts string from a trigger. /// Spec: https://specifications.freedesktop.org/shortcuts-spec/latest/ -pub fn xdgShortcutFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u8 { +pub fn xdgShortcutFromTrigger( + buf: []u8, + trigger: input.Binding.Trigger, +) error{NoSpaceLeft}!?[:0]const u8 { var buf_stream = std.io.fixedBufferStream(buf); const writer = buf_stream.writer();