From 9b90692fc7fae58c58bd18bae3f0b3051e3c78d1 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 16 Aug 2023 10:12:40 -0700 Subject: [PATCH] core: start on key2Callback for the new callback that uses KeyEncoder --- src/Surface.zig | 95 ++++++++++++++++++++++++++++------------ src/input/KeyEncoder.zig | 2 +- 2 files changed, 68 insertions(+), 29 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 687faad00..0f7ad2d61 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1139,39 +1139,78 @@ pub fn charCallback( try self.io_thread.wakeup.notify(); } -/// A key input event. -pub const KeyEvent = struct { - /// The action: press, release, etc. - action: input.Action, +pub fn key2Callback( + self: *Surface, + event: input.KeyEvent, +) !bool { + // Before encoding, we see if we have any keybindings for this + // key. Those always intercept before any encoding tasks. + if (event.action == .press or event.action == .repeat) { + const binding_mods = event.mods.effectiveMods().binding(); + const binding_action_: ?input.Binding.Action = action: { + var trigger: input.Binding.Trigger = .{ + .mods = binding_mods, + .key = event.key, + }; - /// "key" is the logical key that was pressed. For example, if - /// a Dvorak keyboard layout is being used on a US keyboard, - /// the "i" physical key will be reported as "c". The physical - /// key is the key that was physically pressed on the keyboard. - key: input.Key, - physical_key: input.Key, + const set = self.config.keybind.set; + if (set.get(trigger)) |v| break :action v; - /// Mods are the modifiers that are pressed. - mods: input.Mods, + trigger.key = event.physical_key; + trigger.physical = true; + if (set.get(trigger)) |v| break :action v; - /// The mods that were consumed in order to generate the text - /// in utf8. This has the mods set that were consumed, so to - /// get the set of mods that are effective you must negate - /// mods with this. - /// - /// This field is meaningless if utf8 is empty. - consumed_mods: input.Mods, + break :action null; + }; - /// Composing is true when this key event is part of a dead key - /// composition sequence and we're in the middle of it. - composing: bool, + if (binding_action_) |binding_action| { + //log.warn("BINDING ACTION={}", .{binding_action}); + try self.performBindingAction(binding_action); + return true; + } + } - /// The utf8 sequence that was generated by this key event. - /// This will be an empty string if there is no text generated. - /// If composing is true and this is non-empty, this is preedit - /// text. - utf8: []const u8, -}; + // No binding, so we have to perform an encoding task. This + // may still result in no encoding. Under different modes and + // inputs there are many keybindings that result in no encoding + // whatsoever. + const enc: input.KeyEncoder = enc: { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const t = &self.io.terminal; + break :enc .{ + .event = event, + .alt_esc_prefix = t.modes.get(.alt_esc_prefix), + .cursor_key_application = t.modes.get(.cursor_keys), + .keypad_key_application = t.modes.get(.keypad_keys), + .modify_other_keys_state_2 = t.flags.modify_other_keys_2, + }; + }; + + var data: termio.Message.WriteReq.Small.Array = undefined; + const seq = try enc.legacy(&data); + if (seq.len == 0) return false; + + _ = self.io_thread.mailbox.push(.{ + .write_small = .{ + .data = data, + .len = seq.len, + }, + }, .{ .forever = {} }); + try self.io_thread.wakeup.notify(); + + // If we have a sequence to emit then we always want to clear the + // selection and scroll to the bottom. + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + self.setSelection(null); + try self.io.terminal.scrollViewport(.{ .bottom = {} }); + try self.queueRender(); + } + + return true; +} /// Called for a single key event. /// diff --git a/src/input/KeyEncoder.zig b/src/input/KeyEncoder.zig index a49e9f791..48b015779 100644 --- a/src/input/KeyEncoder.zig +++ b/src/input/KeyEncoder.zig @@ -25,7 +25,7 @@ modify_other_keys_state_2: bool = false, /// These together combine the legacy protocol because they're all /// meant to be extensions that do not change any existing behavior /// and therefore safe to combine. -fn legacy( +pub fn legacy( self: *const KeyEncoder, buf: []u8, ) ![]const u8 {