mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 16:26:08 +03:00
inspector: keep track of keyboard events (not rendered yet)
This commit is contained in:
@ -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.
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user