3d5b717d
Handle non-text key encoding
a73x 2026-04-08 11:36
diff --git a/build.zig b/build.zig index ffd6045..97ded6d 100644 --- a/build.zig +++ b/build.zig @@ -50,6 +50,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, .link_libc = true, }); exe_mod.addImport("vt", vt_mod); exe_mod.addImport("wayland-client", wayland_mod); @@ -98,6 +99,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, .link_libc = true, }); main_test_mod.addImport("vt", vt_mod); main_test_mod.addImport("wayland-client", wayland_mod); diff --git a/src/main.zig b/src/main.zig index 7c4ca2a..0b97c28 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,6 +5,10 @@ const wayland_client = @import("wayland-client"); const renderer = @import("renderer"); const font = @import("font"); const c = @cImport({ @cInclude("xkbcommon/xkbcommon-keysyms.h"); }); pub fn main() !void { var gpa: std.heap.DebugAllocator(.{}) = .init; defer _ = gpa.deinit(); @@ -119,6 +123,7 @@ fn runTerminal(alloc: std.mem.Allocator) !void { }; var read_buf: [8192]u8 = undefined; var key_buf: [32]u8 = undefined; while (!window.should_close and p.isChildAlive()) { // Flush any pending wayland requests @@ -154,8 +159,9 @@ fn runTerminal(alloc: std.mem.Allocator) !void { if (ev.action == .release) continue; if (ev.utf8_len > 0) { _ = try p.write(ev.utf8[0..ev.utf8_len]); } else if (try encodeKeyboardEvent(&term, ev, &key_buf)) |encoded| { _ = try p.write(encoded); } // Keys without UTF-8 (arrows, function keys) are skipped for v1 } keyboard.event_queue.clearRetainingCapacity(); @@ -210,6 +216,60 @@ fn runTerminal(alloc: std.mem.Allocator) !void { _ = try ctx.vkd.deviceWaitIdle(ctx.device); } fn encodeKeyboardEvent( term: *const vt.Terminal, ev: wayland_client.KeyboardEvent, buf: []u8, ) !?[]const u8 { const key = mapKeysymToInputKey(ev.keysym) orelse return null; const encoded = try term.encodeKey( key, .{ .shift = ev.modifiers.shift, .ctrl = ev.modifiers.ctrl, .alt = ev.modifiers.alt, .super = ev.modifiers.super, }, switch (ev.action) { .press => .press, .release => .release, .repeat => .repeat, }, buf, ); if (encoded.len == 0) return null; return encoded; } fn mapKeysymToInputKey(keysym: u32) ?vt.InputKey { return switch (keysym) { c.XKB_KEY_Up => .arrow_up, c.XKB_KEY_Down => .arrow_down, c.XKB_KEY_Left => .arrow_left, c.XKB_KEY_Right => .arrow_right, c.XKB_KEY_Home => .home, c.XKB_KEY_End => .end, c.XKB_KEY_Page_Up => .page_up, c.XKB_KEY_Page_Down => .page_down, c.XKB_KEY_Insert => .insert, c.XKB_KEY_Delete => .delete, c.XKB_KEY_F1 => .f1, c.XKB_KEY_F2 => .f2, c.XKB_KEY_F3 => .f3, c.XKB_KEY_F4 => .f4, c.XKB_KEY_F5 => .f5, c.XKB_KEY_F6 => .f6, c.XKB_KEY_F7 => .f7, c.XKB_KEY_F8 => .f8, c.XKB_KEY_F9 => .f9, c.XKB_KEY_F10 => .f10, c.XKB_KEY_F11 => .f11, c.XKB_KEY_F12 => .f12, else => null, }; } fn runDrawSmokeTest(alloc: std.mem.Allocator) !void { var conn = try wayland_client.Connection.init(); defer conn.deinit(); @@ -295,6 +355,25 @@ fn runDrawSmokeTest(alloc: std.mem.Allocator) !void { std.debug.print("done\n", .{}); } test "encodeKeyboardEvent encodes left arrow" { var term = try vt.Terminal.init(std.testing.allocator, .{ .cols = 80, .rows = 24, }); defer term.deinit(); var buf: [32]u8 = undefined; const encoded = (try encodeKeyboardEvent(&term, .{ .keysym = c.XKB_KEY_Left, .modifiers = .{}, .action = .press, .utf8 = [_]u8{0} ** 8, .utf8_len = 0, }, &buf)).?; try std.testing.expectEqualStrings("\x1b[D", encoded); } fn runRenderSmokeTest(alloc: std.mem.Allocator) !void { var conn = try wayland_client.Connection.init(); defer conn.deinit(); diff --git a/src/vt.zig b/src/vt.zig index b506046..71c55bf 100644 --- a/src/vt.zig +++ b/src/vt.zig @@ -50,6 +50,10 @@ const std = @import("std"); const ghostty_vt = @import("ghostty-vt"); pub const InputAction = ghostty_vt.input.KeyAction; pub const InputKey = ghostty_vt.input.Key; pub const InputMods = ghostty_vt.input.KeyMods; pub const Terminal = struct { alloc: std.mem.Allocator, inner: ghostty_vt.Terminal, @@ -105,6 +109,23 @@ pub const Terminal = struct { self.stream.nextSlice(bytes); } pub fn encodeKey( self: *const Terminal, key: InputKey, mods: InputMods, action: InputAction, buf: []u8, ) ![]const u8 { var writer: std.Io.Writer = .fixed(buf); try ghostty_vt.input.encodeKey(&writer, .{ .action = action, .key = key, .mods = mods, .consumed_mods = .{}, }, .fromTerminal(&self.inner)); return writer.buffered(); } pub fn resize(self: *Terminal, new_cols: u16, new_rows: u16) !void { try self.inner.resize(self.alloc, @intCast(new_cols), @intCast(new_rows)); } @@ -158,3 +179,15 @@ test "Terminal.resize" { try std.testing.expectEqual(@as(u16, 120), term.cols()); try std.testing.expectEqual(@as(u16, 40), term.rows()); } test "Terminal encodes non-text arrow keys" { var term = try Terminal.init(std.testing.allocator, .{ .cols = 80, .rows = 24, }); defer term.deinit(); var buf: [32]u8 = undefined; const encoded = try term.encodeKey(.arrow_left, .{}, .press, &buf); try std.testing.expectEqualStrings("\x1b[D", encoded); }