From b34e0f6ec73ac1183286f78caba2a2b74b26a62d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 23 Nov 2023 08:40:52 -0800 Subject: [PATCH] core: do not send repeat/release events if the press consumed a binding Fixes #943 --- src/Surface.zig | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 308e6e878..173ace394 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -71,6 +71,12 @@ renderer_thr: std.Thread, /// Mouse state. mouse: Mouse, +/// The hash value of the last keybinding trigger that we performed. This +/// is only set if the last key input matched a keybinding, consumed it, +/// and performed it. This is used to prevent sending release/repeat events +/// for handled bindings. +last_binding_trigger: u64 = 0, + /// The terminal IO handler. io: termio.Impl, io_thread: termio.Thread, @@ -1123,7 +1129,7 @@ pub fn keyCallback( // Before encoding, we see if we have any keybindings for this // key. Those always intercept before any encoding tasks. binding: { - const binding_action: input.Binding.Action, const consumed = action: { + const binding_action: input.Binding.Action, const binding_trigger: input.Binding.Trigger, const consumed = action: { const binding_mods = event.mods.binding(); var trigger: input.Binding.Trigger = .{ .mods = binding_mods, @@ -1133,6 +1139,7 @@ pub fn keyCallback( const set = self.config.keybind.set; if (set.get(trigger)) |v| break :action .{ v, + trigger, set.getConsumed(trigger), }; @@ -1140,6 +1147,7 @@ pub fn keyCallback( trigger.physical = true; if (set.get(trigger)) |v| break :action .{ v, + trigger, set.getConsumed(trigger), }; @@ -1149,18 +1157,28 @@ pub fn keyCallback( // We only execute the binding on press/repeat but we still consume // the key on release so that we don't send any release events. log.debug("key event binding consumed={} action={}", .{ consumed, binding_action }); - const performed = if (event.action == .press or event.action == .repeat) - try self.performBindingAction(binding_action) - else - false; + const performed = if (event.action == .press or event.action == .repeat) press: { + self.last_binding_trigger = 0; + break :press try self.performBindingAction(binding_action); + } else false; // If we consume this event, then we are done. If we don't consume // it, we processed the action but we still want to process our // encodings, too. if (consumed and performed) { + self.last_binding_trigger = binding_trigger.hash(); if (insp_ev) |*ev| ev.binding = binding_action; return true; } + + // If we have a previous binding trigger and it matches this one, + // then we handled the down event so we don't want to send any further + // events. + if (self.last_binding_trigger > 0 and + self.last_binding_trigger == binding_trigger.hash()) + { + return true; + } } // If we allow KAM and KAM is enabled then we do nothing.