From 7f5f6d01cfa8b4881c82f868212898431f705000 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 23 Oct 2023 12:26:30 -0700 Subject: [PATCH] inspector: keep track of keyboard events (not rendered yet) --- src/Inspector.zig | 53 ++++++++++++++++++++++++++++++++++++++++++++--- src/Surface.zig | 31 +++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/Inspector.zig b/src/Inspector.zig index 277c5a223..618a8d610 100644 --- a/src/Inspector.zig +++ b/src/Inspector.zig @@ -4,8 +4,10 @@ const Inspector = @This(); const std = @import("std"); +const Allocator = std.mem.Allocator; const builtin = @import("builtin"); const cimgui = @import("cimgui"); +const CircBuf = @import("circ_buf.zig").CircBuf; const Surface = @import("Surface.zig"); const input = @import("input.zig"); const terminal = @import("terminal/main.zig"); @@ -38,6 +40,9 @@ mouse: struct { /// A selected cell. cell: CellInspect = .{ .idle = {} }, +/// The list of keyboard events +key_events: CircBuf(KeyEvent, undefined), + const CellInspect = union(enum) { /// Idle, no cell inspection is requested idle: void, @@ -63,6 +68,23 @@ const CellInspect = union(enum) { } }; +pub const KeyEvent = struct { + /// The input event. + event: input.KeyEvent, + + /// The binding that was triggered as a result of this event. + binding: ?input.Binding.Action = null, + + /// The data sent to the pty as a result of this keyboard event. + /// This is allocated using the inspector allocator. + pty: []const u8 = "", + + pub fn deinit(self: *const KeyEvent, alloc: Allocator) void { + if (self.event.utf8.len > 0) alloc.free(self.event.utf8); + if (self.pty.len > 0) alloc.free(self.pty); + } +}; + /// Setup the ImGui state. This requires an ImGui context to be set. pub fn setup() void { const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO(); @@ -97,12 +119,37 @@ pub fn setup() void { } } -pub fn init(surface: *Surface) Inspector { - return .{ .surface = surface }; +pub fn init(surface: *Surface) !Inspector { + var key_buf = try CircBuf(KeyEvent, undefined).init(surface.alloc, 2); + errdefer key_buf.deinit(surface.alloc); + + return .{ + .surface = surface, + .key_events = key_buf, + }; } pub fn deinit(self: *Inspector) void { - _ = self; + { + var it = self.key_events.iterator(.forward); + while (it.next()) |v| v.deinit(self.surface.alloc); + self.key_events.deinit(self.surface.alloc); + } +} + +/// Record a keyboard event. +pub fn recordKeyEvent(self: *Inspector, ev: KeyEvent) !void { + const max_capacity = 1024; + self.key_events.append(ev) catch |err| switch (err) { + error.OutOfMemory => if (self.key_events.capacity() < max_capacity) { + // We're out of memory, but we can allocate to our capacity. + const new_capacity = @min(self.key_events.capacity() * 2, max_capacity); + try self.key_events.resize(self.surface.alloc, new_capacity); + try self.key_events.append(ev); + } else return err, + + else => return err, + }; } /// Render the frame. diff --git a/src/Surface.zig b/src/Surface.zig index a9b5bcf65..f92b34663 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -580,7 +580,7 @@ pub fn activateInspector(self: *Surface) !void { // Setup the inspector var ptr = try self.alloc.create(Inspector); errdefer self.alloc.destroy(ptr); - ptr.* = Inspector.init(self); + ptr.* = try Inspector.init(self); self.inspector = ptr; // Put the inspector onto the render state @@ -1004,6 +1004,24 @@ pub fn keyCallback( ) !bool { // log.debug("keyCallback event={}", .{event}); + // Setup our inspector event if we have an inspector. + var insp_ev: ?Inspector.KeyEvent = if (self.inspector != null) ev: { + var copy = event; + copy.utf8 = ""; + if (event.utf8.len > 0) copy.utf8 = try self.alloc.dupe(u8, event.utf8); + break :ev .{ .event = copy }; + } else null; + + // When we're done processing, we always want to add the event to + // the inspector. + defer if (insp_ev) |ev| { + if (self.inspector.?.recordKeyEvent(ev)) { + self.queueRender() catch {}; + } else |err| { + log.warn("error adding key event to inspector err={}", .{err}); + } + }; + // Before encoding, we see if we have any keybindings for this // key. Those always intercept before any encoding tasks. binding: { @@ -1041,7 +1059,10 @@ pub fn keyCallback( // 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) return true; + if (consumed and performed) { + if (insp_ev) |*ev| ev.binding = binding_action; + return true; + } } // If we allow KAM and KAM is enabled then we do nothing. @@ -1087,6 +1108,12 @@ pub fn keyCallback( .len = @intCast(seq.len), }, }, .{ .forever = {} }); + if (insp_ev) |*ev| { + ev.pty = self.alloc.dupe(u8, seq) catch |err| err: { + log.warn("error copying pty data for inspector err={}", .{err}); + break :err ""; + }; + } try self.io_thread.wakeup.notify(); // If our event is any keypress that isn't a modifier and we generated