From 4ce7a88dee35790b16d773cb98d0efc9f8432d1d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 19 Aug 2024 12:10:31 -0700 Subject: [PATCH] core: enable key sequence handling, including dumping invalid --- src/Surface.zig | 49 +++++++++++++++++++++++++++++++++++++----- src/termio/message.zig | 2 +- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 18f47f2b8..c85084ed7 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -209,6 +209,14 @@ pub const Keyboard = struct { /// a combination to be handled by different bindings before the release /// of the prior (namely since you can't bind modifier-only). last_trigger: ?u64 = null, + + /// The queued keys when we're in the middle of a sequenced binding. + /// These are flushed when the sequence is completed and unconsumed or + /// invalid. + /// + /// This is naturally bounded due to the configuration maximum + /// length of a sequence. + queued: std.ArrayListUnmanaged(termio.Message.WriteReq) = .{}, }; /// The configuration that a surface has, this is copied from the main @@ -625,6 +633,10 @@ pub fn deinit(self: *Surface) void { self.alloc.destroy(v); } + // Clean up our keyboard state + for (self.keyboard.queued.items) |req| req.deinit(); + self.keyboard.queued.deinit(self.alloc); + // Clean up our font grid self.app.font_grid_set.deref(self.font_grid_key); @@ -1521,17 +1533,36 @@ fn maybeHandleBinding( // No entry found. If we're not looking at the root set of the // bindings we need to encode everything up to this point and // send to the pty. - if (self.keyboard.bindings != null) @panic("TODO"); + if (self.keyboard.bindings != null) { + // Reset to the root set + self.keyboard.bindings = null; + + // Encode everything up to this point + for (self.keyboard.queued.items) |write_req| { + self.io.queueMessage(switch (write_req) { + .small => |v| .{ .write_small = v }, + .stable => |v| .{ .write_stable = v }, + .alloc => |v| .{ .write_alloc = v }, + }, .unlocked); + } + self.keyboard.queued.clearRetainingCapacity(); + } return null; }; // Determine if this entry has an action or if its a leader key. const action: input.Binding.Action, const consumed: bool = switch (entry) { - .leader => { - // TODO - log.warn("sequenced keybinds are not supported yet", .{}); - return null; + .leader => |set| { + // Setup the next set we'll look at. + self.keyboard.bindings = set; + + // Store this event so that we can drain and encode on invalid + if (try self.encodeKey(event, insp_ev)) |req| { + try self.keyboard.queued.append(self.alloc, req); + } + + return .consumed; }, .action => |v| .{ v, true }, @@ -1543,6 +1574,14 @@ fn maybeHandleBinding( // perform an action (below) self.keyboard.last_trigger = null; + // An action also always resets the binding set. + self.keyboard.bindings = null; + if (self.keyboard.queued.items.len > 0) { + // TODO: unconsumed + for (self.keyboard.queued.items) |req| req.deinit(); + self.keyboard.queued.clearRetainingCapacity(); + } + // Attempt to perform the action log.debug("key event binding consumed={} action={}", .{ consumed, action }); const performed = try self.performBindingAction(action); diff --git a/src/termio/message.zig b/src/termio/message.zig index a5307498e..20d47fd32 100644 --- a/src/termio/message.zig +++ b/src/termio/message.zig @@ -179,7 +179,7 @@ pub fn MessageData(comptime Elem: type, comptime small_size: comptime_int) type pub fn deinit(self: Self) void { switch (self) { .small, .stable => {}, - .alloc => |v| v.alloc.free(v.alloc.data), + .alloc => |v| v.alloc.free(v.data), } }