From 2b2b23dcf66f0bf9d5c3b7ccdf48df988df4fde6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 15 Aug 2023 11:13:01 -0700 Subject: [PATCH] apprt/embedded: keycallback should use nomod text to determine key To determine the logical key that was pressed, we previously just trusted that the translated text would have the right value. But if modifiers are pressed, the text may not translate. For example on macOS, Ctrl+C does not produce any text. As a result, we would fall back to the physical key. On layouts like Dvorak, the physical key for "C" is "I". This means "Ctrl+C" sequences weren't working. Instead, if there is no text or the text doesn't map to a key, we translate again using no modifiers to try to get the raw text of the input and then base the key on that. --- src/Surface.zig | 7 +++++++ src/apprt/embedded.zig | 28 +++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 4af32922e..5aeecf2d4 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1134,6 +1134,13 @@ pub fn keyCallback( const tracy = trace(@src()); defer tracy.end(); + // log.warn("KEY CALLBACK action={} key={} physical_key={} mods={}", .{ + // action, + // key, + // physical_key, + // mods, + // }); + // Dev Mode if (DevMode.enabled and DevMode.instance.visible) { // If the event was handled by imgui, ignore it. diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index e9f2a403c..2612022f5 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -422,10 +422,11 @@ pub const Surface = struct { self.core_surface.preeditCallback(null) catch {}; } - // log.warn("TRANSLATE: action={} keycode={x} dead={} key={any} key_str={s} mods={}", .{ + // log.warn("TRANSLATE: action={} keycode={x} dead={} key_len={} key={any} key_str={s} mods={}", .{ // action, // keycode, // result.composing, + // result.text.len, // result.text, // result.text, // mods, @@ -444,8 +445,29 @@ pub const Surface = struct { // We also only do key translation if this is not a dead key. const key = if (!result.composing and result.text.len == 1) key: { // A completed key. If the length of the key is one then we can - // attempt to translate it to a key enum and call the key callback. - break :key input.Key.fromASCII(result.text[0]) orelse physical_key; + // attempt to translate it to a key enum and call the key + // callback. First try plain ASCII. + if (input.Key.fromASCII(result.text[0])) |key| { + break :key key; + } + + // 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| { + break :key key; + } + } + + break :key physical_key; } else .invalid; // If both keys are invalid then we won't call the key callback. But