From 4fe739cae0071c442308416c1dc35668ffe66e84 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 10 Aug 2023 15:29:46 -0700 Subject: [PATCH] core: note when keyCallback processed the input --- src/Surface.zig | 12 ++++++++++-- src/apprt/embedded.zig | 39 ++++++++++++++++++++++++++------------- src/apprt/glfw.zig | 2 +- src/apprt/gtk.zig | 18 +++--------------- 4 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 29455592e..ef1d394a0 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1022,13 +1022,19 @@ pub fn charCallback(self: *Surface, codepoint: u21) !void { try self.io_thread.wakeup.notify(); } +/// Called for a single key event. +/// +/// This will return true if the key was handled/consumed. In that case, +/// the caller doesn't need to call a subsequent `charCallback` for the +/// same event. However, the caller can call `charCallback` if they want, +/// the surface will retain state to ensure the event is ignored. pub fn keyCallback( self: *Surface, action: input.Action, key: input.Key, unmapped_key: input.Key, mods: input.Mods, -) !void { +) !bool { const tracy = trace(@src()); defer tracy.end(); @@ -1079,7 +1085,7 @@ pub fn keyCallback( self.ignore_char = true; // No matter what, if there is a binding then we are done. - return; + return self.ignore_char; } // Handle non-printables @@ -1172,6 +1178,8 @@ pub fn keyCallback( } } } + + return self.ignore_char; } pub fn focusCallback(self: *Surface, focused: bool) !void { diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index b800f2690..e2e315605 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -408,17 +408,6 @@ pub const Surface = struct { // mods, // }); - // If this is a dead key, then we're composing a character and - // we end processing here. We don't process keybinds for dead keys. - if (result.composing) { - // TODO: we ultimately want to update some surface state so that - // we can show the user that we're in dead key mode and the - // precomposed character. For now, we can just ignore and that - // is not incorrect behavior. - log.warn("dead key mode, currently composing", .{}); - return; - } - // We want to get the physical unmapped key to process keybinds. const physical_key = keycode: for (input.keycodes.entries) |entry| { if (entry.native == keycode) break :keycode entry.key; @@ -428,7 +417,9 @@ pub const Surface = struct { // and attempt to translate it to a key enum and call the key callback. // If the length is greater than 1 then we're going to call the // charCallback. - const key = if (result.text.len == 1) key: { + // + // 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; @@ -437,10 +428,32 @@ pub const Surface = struct { // If both keys are invalid then we won't call the key callback. But // if either one is valid, we want to give it a chance. if (key != .invalid or physical_key != .invalid) { - self.core_surface.keyCallback(action, key, physical_key, mods) catch |err| { + const consumed = self.core_surface.keyCallback( + action, + key, + physical_key, + mods, + ) catch |err| { log.err("error in key callback err={}", .{err}); return; }; + + // If we consume the key then we want to reset the dead key state. + if (consumed) { + self.keymap_state = .{}; + return; + } + } + + // If this is a dead key, then we're composing a character and + // we end processing here. We don't process keybinds for dead keys. + if (result.composing) { + // TODO: we ultimately want to update some surface state so that + // we can show the user that we're in dead key mode and the + // precomposed character. For now, we can just ignore and that + // is not incorrect behavior. + log.warn("dead key mode, currently composing", .{}); + return; } // Next, we want to call the char callback with each codepoint. diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 83de490d2..6b0edc22a 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -740,7 +740,7 @@ pub const Surface = struct { // TODO: we need to do mapped keybindings const core_win = window.getUserPointer(CoreSurface) orelse return; - core_win.keyCallback(action, key, key, mods) catch |err| { + _ = core_win.keyCallback(action, key, key, mods) catch |err| { log.err("error in key callback err={}", .{err}); return; }; diff --git a/src/apprt/gtk.zig b/src/apprt/gtk.zig index e1f7be92f..fe9570b5f 100644 --- a/src/apprt/gtk.zig +++ b/src/apprt/gtk.zig @@ -1167,25 +1167,13 @@ pub const Surface = struct { const key = translateKey(keyval); const mods = translateMods(state); log.debug("key-press code={} key={} mods={}", .{ keycode, key, mods }); - self.core_surface.keyCallback(.press, key, key, mods) catch |err| { + const processed = self.core_surface.keyCallback(.press, key, key, mods) catch |err| { log.err("error in key callback err={}", .{err}); return 0; }; - // We generally just say we didn't handle it. We control our - // GTK environment so for any keys that matter we'll grab them. - // One of the reasons we say we didn't handle it is so that the - // IME can still work. - return switch (keyval) { - // If the key is tab, we say we handled it because we don't want - // tab to move focus from our surface. - c.GDK_KEY_Tab => 1, - // We do the same for up, because that steals focus from the surface, - // in case we have multiple tabs open. - c.GDK_KEY_Up => 1, - - else => 0, - }; + // If we processed the key, we say we handled it. + return if (processed) 1 else 0; } fn gtkKeyReleased(