From 47ee1e735576685d5129562da4cd37f4cc10d7ad Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 29 Sep 2023 21:34:23 -0700 Subject: [PATCH] input: Binding string can be unconsumed with "unconsumed:" prefix --- src/config/Config.zig | 6 +++++- src/input/Binding.zig | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/config/Config.zig b/src/config/Config.zig index 79a22c3d3..db23d0ee3 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1524,7 +1524,11 @@ pub const Keybinds = struct { const binding = try inputpkg.Binding.parse(value); switch (binding.action) { .unbind => self.set.remove(binding.trigger), - else => try self.set.put(alloc, binding.trigger, binding.action), + else => if (binding.consumed) { + try self.set.put(alloc, binding.trigger, binding.action); + } else { + try self.set.putUnconsumed(alloc, binding.trigger, binding.action); + }, } } diff --git a/src/input/Binding.zig b/src/input/Binding.zig index ccaee06b6..9b11a640a 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -13,6 +13,10 @@ trigger: Trigger, /// The action to take if this binding matches action: Action, +/// True if this binding should consume the input when the +/// action is triggered. +consumed: bool = true, + pub const Error = error{ InvalidFormat, InvalidAction, @@ -22,10 +26,17 @@ pub const Error = error{ /// specifically "trigger=action". Trigger is a "+"-delimited series of /// modifiers and keys. Action is the action name and optionally a /// parameter after a colon, i.e. "csi:A" or "ignore". -pub fn parse(input: []const u8) !Binding { +pub fn parse(raw_input: []const u8) !Binding { // NOTE(mitchellh): This is not the most efficient way to do any // of this, I welcome any improvements here! + // If our entire input is prefixed with "unconsumed:" then we are + // not consuming this keybind when the action is triggered. + const unconsumed_prefix = "unconsumed:"; + const unconsumed = std.mem.startsWith(u8, raw_input, unconsumed_prefix); + const start_idx = if (unconsumed) unconsumed_prefix.len else 0; + const input = raw_input[start_idx..]; + // Find the first = which splits are mapping into the trigger // and action, respectively. const eqlIdx = std.mem.indexOf(u8, input, "=") orelse return Error.InvalidFormat; @@ -84,7 +95,11 @@ pub fn parse(input: []const u8) !Binding { // Find a matching action const action = try Action.parse(input[eqlIdx + 1 ..]); - return Binding{ .trigger = trigger, .action = action }; + return Binding{ + .trigger = trigger, + .action = action, + .consumed = !unconsumed, + }; } /// The set of actions that a keybinding can take. @@ -581,6 +596,27 @@ test "parse: triggers" { .action = .{ .ignore = {} }, }, try parse("shift+physical:a=ignore")); + // unconsumed keys + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .shift = true }, + .key = .a, + }, + .action = .{ .ignore = {} }, + .consumed = false, + }, try parse("unconsumed:shift+a=ignore")); + + // unconsumed physical keys + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .shift = true }, + .key = .a, + .physical = true, + }, + .action = .{ .ignore = {} }, + .consumed = false, + }, try parse("unconsumed:physical:a+shift=ignore")); + // invalid key try testing.expectError(Error.InvalidFormat, parse("foo=ignore"));