From 17caeb5fac1a13aeb0d261556ceacef583d7c328 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 23 Sep 2024 19:20:34 -0700 Subject: [PATCH] core: "all" bindings work --- src/Surface.zig | 35 +++++++++++++++++--- src/input/Binding.zig | 75 ++++++++++++++++++++++++++++++------------- 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 9e25ef0ad..49017883a 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1590,7 +1590,7 @@ fn maybeHandleBinding( }; // Determine if this entry has an action or if its a leader key. - const action: input.Binding.Action, const consumed: bool = switch (entry) { + const leaf: input.Binding.Set.Leaf = switch (entry) { .leader => |set| { // Setup the next set we'll look at. self.keyboard.bindings = set; @@ -1605,7 +1605,20 @@ fn maybeHandleBinding( return .consumed; }, - .leaf => |leaf| .{ leaf.action, leaf.flags.consumed }, + .leaf => |leaf| leaf, + }; + const action = leaf.action; + + // consumed determines if the input is consumed or if we continue + // encoding the key (if we have a key to encode). + const consumed = consumed: { + // If the consumed flag is explicitly set, then we are consumed. + if (leaf.flags.consumed) break :consumed true; + + // If the global or all flag is set, we always consume. + if (leaf.flags.global or leaf.flags.all) break :consumed true; + + break :consumed false; }; // We have an action, so at this point we're handling SOMETHING so @@ -1617,8 +1630,22 @@ fn maybeHandleBinding( self.keyboard.bindings = null; // Attempt to perform the action - log.debug("key event binding consumed={} action={}", .{ consumed, action }); - const performed = try self.performBindingAction(action); + log.debug("key event binding flags={} action={}", .{ + leaf.flags, + action, + }); + const performed = performed: { + // If this is a global or all action, then we perform it on + // the app and it applies to every surface. + if (leaf.flags.global or leaf.flags.all) { + try self.app.performAllAction(self.rt_app, action); + + // "All" actions are always performed since they are global. + break :performed true; + } + + break :performed try self.performBindingAction(action); + }; // If we performed an action and it was a closing action, // our "self" pointer is not safe to use anymore so we need to diff --git a/src/input/Binding.zig b/src/input/Binding.zig index ba7b62af2..57c98f351 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -1160,7 +1160,7 @@ pub const Set = struct { set.remove(alloc, t); if (old) |entry| switch (entry) { .leader => unreachable, // Handled above - .leaf => |leaf| set.put_( + .leaf => |leaf| set.putFlags( alloc, t, leaf.action, @@ -1179,11 +1179,12 @@ pub const Set = struct { return error.SequenceUnbind; }, - else => if (b.flags.consumed) { - try set.put(alloc, b.trigger, b.action); - } else { - try set.putUnconsumed(alloc, b.trigger, b.action); - }, + else => try set.putFlags( + alloc, + b.trigger, + b.action, + b.flags, + ), }, } } @@ -1196,24 +1197,11 @@ pub const Set = struct { t: Trigger, action: Action, ) Allocator.Error!void { - try self.put_(alloc, t, action, .{}); + try self.putFlags(alloc, t, action, .{}); } - /// Same as put but marks the trigger as unconsumed. An unconsumed - /// trigger will evaluate the action and continue to encode for the - /// terminal. - /// - /// This is a separate function because this case is rare. - pub fn putUnconsumed( - self: *Set, - alloc: Allocator, - t: Trigger, - action: Action, - ) Allocator.Error!void { - try self.put_(alloc, t, action, .{ .consumed = false }); - } - - fn put_( + /// Add a binding to the set with explicit flags. + pub fn putFlags( self: *Set, alloc: Allocator, t: Trigger, @@ -1486,6 +1474,49 @@ test "parse: global triggers" { } } +test "parse: all triggers" { + const testing = std.testing; + + // all keys + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .shift = true }, + .key = .{ .translated = .a }, + }, + .action = .{ .ignore = {} }, + .flags = .{ .all = true }, + }, try parseSingle("all:shift+a=ignore")); + + // all physical keys + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .shift = true }, + .key = .{ .physical = .a }, + }, + .action = .{ .ignore = {} }, + .flags = .{ .all = true }, + }, try parseSingle("all:physical:a+shift=ignore")); + + // all unconsumed keys + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .shift = true }, + .key = .{ .translated = .a }, + }, + .action = .{ .ignore = {} }, + .flags = .{ + .all = true, + .consumed = false, + }, + }, try parseSingle("unconsumed:all:a+shift=ignore")); + + // all sequences not allowed + { + var p = try Parser.init("all:a>b=ignore"); + try testing.expectError(Error.InvalidFormat, p.next()); + } +} + test "parse: modifier aliases" { const testing = std.testing;