From fe0e1f5ee80692ce7521298f733f257840bb4727 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 17 Aug 2023 09:24:44 -0700 Subject: [PATCH] input: key must provide unshifted codepoint --- src/apprt/embedded.zig | 37 +++++++++++++++++++++++++------------ src/apprt/gtk.zig | 26 ++++++++++++++++++++++++++ src/input/key.zig | 4 ++++ 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 68a3c805a..e1ef1612b 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -458,6 +458,26 @@ pub const Surface = struct { // then we've consumed our translate mods. const consumed_mods: input.Mods = if (result.text.len > 0) translate_mods else .{}; + // We need to always do a translation with no modifiers at all in + // order to get the "unshifted_codepoint" for the key event. + const unshifted_codepoint: u21 = unshifted: { + var nomod_buf: [128]u8 = undefined; + var nomod_state: input.Keymap.State = undefined; + const nomod = try self.app.keymap.translate( + &nomod_buf, + &nomod_state, + @intCast(keycode), + .{}, + ); + + const view = std.unicode.Utf8View.init(nomod.text) catch |err| { + log.warn("cannot build utf8 view over text: {}", .{err}); + break :unshifted 0; + }; + var it = view.iterator(); + break :unshifted it.nextCodepoint() orelse 0; + }; + // log.warn("TRANSLATE: action={} keycode={x} dead={} key_len={} key={any} key_str={s} mods={}", .{ // action, // keycode, @@ -489,18 +509,9 @@ pub const Surface = struct { } } - // If that doesn't work then we try to translate without - // any modifiers and convert that. - var nomod_buf: [128]u8 = undefined; - var nomod_state: input.Keymap.State = undefined; - const nomod = try self.app.keymap.translate( - &nomod_buf, - &nomod_state, - @intCast(keycode), - .{}, - ); - if (nomod.text.len == 1) { - if (input.Key.fromASCII(nomod.text[0])) |key| { + // If the above doesn't work, we use the unmodified value. + if (std.math.cast(u8, unshifted_codepoint)) |ascii| { + if (input.Key.fromASCII(ascii)) |key| { break :key key; } } @@ -517,6 +528,7 @@ pub const Surface = struct { .consumed_mods = consumed_mods, .composing = result.composing, .utf8 = result.text, + .unshifted_codepoint = unshifted_codepoint, }) catch |err| { log.err("error in key callback err={}", .{err}); return; @@ -549,6 +561,7 @@ pub const Surface = struct { .consumed_mods = .{}, .composing = false, .utf8 = buf[0..len], + .unshifted_codepoint = 0, }) catch |err| { log.err("error in key callback err={}", .{err}); return; diff --git a/src/apprt/gtk.zig b/src/apprt/gtk.zig index 1a8d497db..d2aeaac87 100644 --- a/src/apprt/gtk.zig +++ b/src/apprt/gtk.zig @@ -1212,6 +1212,31 @@ pub const Surface = struct { const keyval_unicode = c.gdk_keyval_to_unicode(keyval); const event = c.gtk_event_controller_get_current_event(@ptrCast(ec_key)); + // Get the unshifted unicode value of the keyval. This is used + // by the Kitty keyboard protocol. + const keyval_unicode_unshifted: u21 = unshifted: { + var n: c_int = undefined; + var keys: [*c]c.GdkKeymapKey = undefined; + var keyvals: [*c]c.guint = undefined; + if (c.gdk_display_map_keycode( + c.gdk_event_get_display(event), + keycode, + &keys, + &keyvals, + &n, + ) == 0) break :unshifted 0; + + defer c.g_free(keys); + defer c.g_free(keyvals); + for (keys[0..@intCast(n)], 0..) |key, i| { + if (key.group == 0 and key.level == 0) { + break :unshifted @intCast(c.gdk_keyval_to_unicode(keyvals[i])); + } + } + + break :unshifted 0; + }; + // We always reset our committed text when ending a keypress so that // future keypresses don't think we have a commit event. defer self.im_len = 0; @@ -1317,6 +1342,7 @@ pub const Surface = struct { .consumed_mods = consumed_mods, .composing = self.im_composing, .utf8 = self.im_buf[0..self.im_len], + .unshifted_codepoint = keyval_unicode_unshifted, }) catch |err| { log.err("error in key callback err={}", .{err}); return false; diff --git a/src/input/key.zig b/src/input/key.zig index 622d50556..46263bf30 100644 --- a/src/input/key.zig +++ b/src/input/key.zig @@ -41,6 +41,10 @@ pub const KeyEvent = struct { /// text. utf8: []const u8 = "", + /// The codepoint for this key when it is unshifted. For example, + /// shift+a is "A" in UTF-8 but unshifted would provide 'a'. + unshifted_codepoint: u21 = 0, + /// Returns the effective modifiers for this event. The effective /// modifiers are the mods that should be considered for keybindings. pub fn effectiveMods(self: KeyEvent) Mods {