summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/esp32p4.zig34
-rw-r--r--src/gui/gui.zig71
-rw-r--r--src/main.zig1
-rw-r--r--src/tty/tty.zig66
-rw-r--r--src/vaxis_input.zig77
5 files changed, 123 insertions, 126 deletions
diff --git a/src/esp32p4.zig b/src/esp32p4.zig
index b7725b81..4e1fe036 100644
--- a/src/esp32p4.zig
+++ b/src/esp32p4.zig
@@ -32,6 +32,7 @@ const std = @import("std");
const builtin = @import("builtin");
const pardes = @import("pardes.zig");
const vaxis = @import("vaxis");
+const vaxis_input = @import("vaxis_input.zig");
// ------------------------------------------------------------------ what a freestanding root owes
//
@@ -394,7 +395,7 @@ fn apply(c: *pardes.Pardes, ev: vaxis.Event) void {
// text at all, so a line break inside a paste arrives as a bare CR (Key.enter) or, from
// a terminal that does not translate them, as ctrl+j.
const text = key.text orelse "";
- const cp = mapKey(effCp(key));
+ const cp = vaxis_input.mapKey(vaxis_input.effCp(key));
const bytes: []const u8 = if (text.len > 0)
text
else if (cp == pardes.Key.tab)
@@ -406,7 +407,7 @@ fn apply(c: *pardes.Pardes, ev: vaxis.Event) void {
if (bytes.len > 0) paste_buf.appendSlice(gpa(), bytes) catch {};
} else {
c.update(.{ .key = .{
- .cp = mapKey(effCp(key)),
+ .cp = vaxis_input.mapKey(vaxis_input.effCp(key)),
.text = key.text orelse "",
.ctrl = key.mods.ctrl,
.alt = key.mods.alt,
@@ -529,35 +530,6 @@ fn apply(c: *pardes.Pardes, ev: vaxis.Event) void {
}
}
-/// The effective codepoint the way vaxis's own `Key.matches` sees it: a single-character `text`
-/// wins, because the terminal has already resolved shift; otherwise the shifted codepoint.
-fn effCp(key: vaxis.Key) u21 {
- if (key.text) |t| {
- const view = std.unicode.Utf8View.init(t) catch return key.codepoint;
- var it = view.iterator();
- if (it.nextCodepoint()) |cp| {
- if (it.nextCodepoint() == null) return cp;
- }
- }
- return key.shifted_codepoint orelse key.codepoint;
-}
-
-/// vaxis functional-key codepoints -> core constants. The ASCII ones already coincide, so
-/// enter/tab/escape/backspace pass straight through.
-fn mapKey(cp: u21) u21 {
- return switch (cp) {
- vaxis.Key.up => pardes.Key.up,
- vaxis.Key.down => pardes.Key.down,
- vaxis.Key.left => pardes.Key.left,
- vaxis.Key.right => pardes.Key.right,
- vaxis.Key.home => pardes.Key.home,
- vaxis.Key.end => pardes.Key.end,
- vaxis.Key.page_up => pardes.Key.page_up,
- vaxis.Key.page_down => pardes.Key.page_down,
- vaxis.Key.delete => pardes.Key.delete,
- else => cp,
- };
-}
export fn pardes_esp32p4_tick(now_ms: u64) callconv(.c) void {
const c = core orelse return;
last_now_ms = now_ms;
diff --git a/src/gui/gui.zig b/src/gui/gui.zig
index df565b94..8b100cd8 100644
--- a/src/gui/gui.zig
+++ b/src/gui/gui.zig
@@ -6,6 +6,7 @@ const libc = std.c;
const vaxis = @import("vaxis"); // test modes only: the stdin escape-seq parser
const ghostty_vt = @import("ghostty-vt"); // 256-color palette for .index cells
const pardes = @import("../pardes.zig");
+const vaxis_input = @import("../vaxis_input.zig");
const dump = @import("../dump.zig");
const config = @import("../config.zig");
const look = @import("../look.zig");
@@ -3225,15 +3226,7 @@ const StdinFeed = struct {
const event = result.event orelse continue;
switch (event) {
.key_press => |key| {
- var text: []const u8 = "";
- if (key.text) |t| text = f.cache.put(t);
- in.post(.{ .key = .{
- .cp = mapKey(effCp(key)),
- .text = text,
- .ctrl = key.mods.ctrl,
- .alt = key.mods.alt,
- .shift = key.mods.shift,
- } });
+ postKey(in, key, if (key.text) |t| f.cache.put(t) else "");
out.n_events += 1;
},
.mouse => |m| {
@@ -3382,6 +3375,40 @@ fn dumpGrid(gpa: std.mem.Allocator, surface: *pardes.Surface) !void {
_ = host_io.writeFd(1, frame);
}
+/// A key parsed from the stdin a test window reads, posted as the tty
+/// posts one (vaxis_input): F-keys, Insert and the keypad included. `text`
+/// is the key's text as the feed keeps it.
+fn postKey(in: *Input, key: vaxis.Key, text: []const u8) void {
+ var ev = vaxis_input.keyEvent(key);
+ ev.key.text = text;
+ in.post(ev);
+}
+
+test "the GUI's stdin keys reach a terminal pane as the tty's do: F5, Insert and the keypad's Up" {
+ const gpa = std.testing.allocator;
+ const core = try pardes.Pardes.init(gpa, .{ .tty_only = true });
+ defer core.deinit();
+ core.panes[0].?.body.mode = .tty;
+ while (core.nextEffect()) |_| {}
+ var in: Input = .{ .core = core };
+ var parser: vaxis.Parser = .{};
+ var bytes: []const u8 = "\x1b[15~\x1b[2~\x1b[57419u";
+ while (bytes.len > 0) {
+ const result = try parser.parse(bytes, gpa);
+ bytes = bytes[result.n..];
+ if (result.event) |event| switch (event) {
+ .key_press => |key| postKey(&in, key, key.text orelse ""),
+ else => {},
+ };
+ }
+ var written: std.Io.Writer.Allocating = .init(gpa);
+ defer written.deinit();
+ while (core.nextEffect()) |effect| {
+ if (effect == .write) try written.writer.writeAll(effect.write.bytes.slice());
+ }
+ try std.testing.expectEqualStrings("\x1b[15~\x1b[2~\x1b[A", written.written());
+}
+
const Input = struct {
core: ?*pardes.Pardes = null,
client: ?*detached_client.Client = null,
@@ -10439,32 +10466,6 @@ fn miniGlyph(ch: u8) [5]u8 {
};
}
-fn effCp(key: vaxis.Key) u21 {
- if (key.text) |t| {
- const view = std.unicode.Utf8View.init(t) catch return key.codepoint;
- var it = view.iterator();
- if (it.nextCodepoint()) |cp| {
- if (it.nextCodepoint() == null) return cp;
- }
- }
- return key.shifted_codepoint orelse key.codepoint;
-}
-
-fn mapKey(cp: u21) u21 {
- return switch (cp) {
- vaxis.Key.up => pardes.Key.up,
- vaxis.Key.down => pardes.Key.down,
- vaxis.Key.left => pardes.Key.left,
- vaxis.Key.right => pardes.Key.right,
- vaxis.Key.home => pardes.Key.home,
- vaxis.Key.end => pardes.Key.end,
- vaxis.Key.page_up => pardes.Key.page_up,
- vaxis.Key.page_down => pardes.Key.page_down,
- vaxis.Key.delete => pardes.Key.delete,
- else => cp,
- };
-}
-
fn envU16(env: *std.process.Environ.Map, name: []const u8) ?u16 {
const raw = env.get(name) orelse return null;
return std.fmt.parseInt(u16, raw, 10) catch null;
diff --git a/src/main.zig b/src/main.zig
index 6ea05479..17d0fb5f 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -525,6 +525,7 @@ test {
_ = @import("fs.zig");
_ = @import("detached/wire.zig");
_ = @import("ShaderBuild.zig");
+ _ = @import("vaxis_input.zig");
_ = @import("detached/server.zig");
_ = @import("detached/client.zig");
_ = @import("9p_io.zig");
diff --git a/src/tty/tty.zig b/src/tty/tty.zig
index 3902271c..3441bc29 100644
--- a/src/tty/tty.zig
+++ b/src/tty/tty.zig
@@ -5,6 +5,7 @@ const posix = std.posix;
const libc = std.c;
const vaxis = @import("vaxis");
const pardes = @import("../pardes.zig");
+const vaxis_input = @import("../vaxis_input.zig");
const dump = @import("../dump.zig");
const tracy = @import("../tracy.zig");
const look = @import("../look.zig");
@@ -1510,7 +1511,7 @@ const Shell = struct {
const bytes = pasteBytes(key);
const room = max_paste_bytes -| s.paste_buf.written().len;
s.paste_buf.writer.writeAll(bytes[0..@min(bytes.len, room)]) catch {};
- } else core.update(keyEvent(key)),
+ } else core.update(vaxis_input.keyEvent(key)),
.mouse => |m| {
const pdf_before = nativePdfWheelTarget(core, m);
if (mouseEvent(m)) |ev| core.update(ev);
@@ -2153,72 +2154,17 @@ fn readPty(io: std.Io, gpa: std.mem.Allocator, pty: std.Io.File, id: usize, gen:
_ = loop.tryPostEvent(.{ .pty_eof = .{ .id = id, .gen = gen } }) catch {};
}
-fn effCp(key: vaxis.Key) u21 {
- if (key.text) |t| {
- const view = std.unicode.Utf8View.init(t) catch return key.codepoint;
- var it = view.iterator();
- if (it.nextCodepoint()) |c| {
- if (it.nextCodepoint() == null) return c;
- }
- }
- return key.shifted_codepoint orelse key.codepoint;
-}
-
-fn mapKey(cp: u21) u21 {
- return switch (cp) {
- vaxis.Key.kp_enter => pardes.Key.enter,
- vaxis.Key.up => pardes.Key.up,
- vaxis.Key.down => pardes.Key.down,
- vaxis.Key.left => pardes.Key.left,
- vaxis.Key.right => pardes.Key.right,
- vaxis.Key.home => pardes.Key.home,
- vaxis.Key.end => pardes.Key.end,
- vaxis.Key.page_up => pardes.Key.page_up,
- vaxis.Key.page_down => pardes.Key.page_down,
- vaxis.Key.delete => pardes.Key.delete,
- vaxis.Key.insert => pardes.Key.insert,
- vaxis.Key.print_screen => pardes.Key.print_screen,
- vaxis.Key.scroll_lock => pardes.Key.scroll_lock,
- vaxis.Key.pause => pardes.Key.pause,
- vaxis.Key.menu => pardes.Key.menu,
- vaxis.Key.f1...vaxis.Key.f25 => pardes.Key.f1 + (cp - vaxis.Key.f1),
- // The keypad's own keys, which the kitty keyboard protocol tells
- // apart, do what the keys they are named for do.
- vaxis.Key.kp_left => pardes.Key.left,
- vaxis.Key.kp_right => pardes.Key.right,
- vaxis.Key.kp_up => pardes.Key.up,
- vaxis.Key.kp_down => pardes.Key.down,
- vaxis.Key.kp_page_up => pardes.Key.page_up,
- vaxis.Key.kp_page_down => pardes.Key.page_down,
- vaxis.Key.kp_home => pardes.Key.home,
- vaxis.Key.kp_end => pardes.Key.end,
- vaxis.Key.kp_insert => pardes.Key.insert,
- vaxis.Key.kp_delete => pardes.Key.delete,
- else => cp,
- };
-}
-
const max_paste_bytes: usize = 4 << 20;
fn pasteBytes(key: vaxis.Key) []const u8 {
const text = key.text orelse "";
if (text.len > 0) return text;
- const cp = mapKey(effCp(key));
+ const cp = vaxis_input.mapKey(vaxis_input.effCp(key));
if (cp == pardes.Key.tab) return "\t";
if (cp == pardes.Key.enter or (key.mods.ctrl and cp == 'j')) return "\n";
return "";
}
-fn keyEvent(key: vaxis.Key) pardes.Event {
- return .{ .key = .{
- .cp = mapKey(effCp(key)),
- .text = key.text orelse "",
- .ctrl = key.mods.ctrl,
- .alt = key.mods.alt,
- .shift = key.mods.shift,
- } };
-}
-
test "a terminal pane gets function keys as their sequences and lone modifiers not at all" {
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;
const gpa = std.testing.allocator;
@@ -2271,7 +2217,7 @@ test "a terminal pane gets function keys as their sequences and lone modifiers n
core.panes[0].?.body.mode = .tty;
while (core.nextEffect()) |_| {}
while (try loop.tryEvent()) |event| switch (event) {
- .key_press => |key| core.update(keyEvent(key)),
+ .key_press => |key| core.update(vaxis_input.keyEvent(key)),
else => {},
};
var written: std.Io.Writer.Allocating = .init(gpa);
@@ -2285,7 +2231,7 @@ test "a terminal pane gets function keys as their sequences and lone modifiers n
test "keypad Enter matches Return for editor input terminal writes and paste" {
for ([_]u21{ vaxis.Key.enter, vaxis.Key.kp_enter }) |codepoint| {
const key: vaxis.Key = .{ .codepoint = codepoint };
- const event = keyEvent(key);
+ const event = vaxis_input.keyEvent(key);
try std.testing.expectEqual(pardes.Key.enter, event.key.cp);
try std.testing.expectEqualStrings("\n", pasteBytes(key));
{
@@ -2757,7 +2703,7 @@ const Attach = struct {
const bytes = pasteBytes(key);
const room = max_paste_bytes -| a.paste_buf.written().len;
a.paste_buf.writer.writeAll(bytes[0..@min(bytes.len, room)]) catch {};
- } else return a.send(keyEvent(key)),
+ } else return a.send(vaxis_input.keyEvent(key)),
.mouse => |m| if (mouseEvent(m)) |ev| return a.send(ev),
.paste => |bytes| {
defer a.gpa.free(@constCast(bytes));
diff --git a/src/vaxis_input.zig b/src/vaxis_input.zig
new file mode 100644
index 00000000..0e25dea1
--- /dev/null
+++ b/src/vaxis_input.zig
@@ -0,0 +1,77 @@
+//! A key as vaxis parses it from a terminal's bytes, as the core's Event:
+//! the one map for every shell that reads a terminal (tty, gui's stdin,
+//! esp32p4). Three copies had drifted; the gui's had lost the F-keys,
+//! Insert and the keypad.
+const std = @import("std");
+const vaxis = @import("vaxis");
+const pardes = @import("pardes.zig");
+
+/// The effective codepoint the way vaxis's own `Key.matches` sees it: a
+/// single-character `text` wins, the terminal having resolved shift;
+/// otherwise the shifted codepoint.
+pub fn effCp(key: vaxis.Key) u21 {
+ if (key.text) |t| {
+ const view = std.unicode.Utf8View.init(t) catch return key.codepoint;
+ var it = view.iterator();
+ if (it.nextCodepoint()) |c| {
+ if (it.nextCodepoint() == null) return c;
+ }
+ }
+ return key.shifted_codepoint orelse key.codepoint;
+}
+
+/// vaxis's functional-key codepoints as the core's; the ASCII ones
+/// (enter, tab, escape, backspace) already coincide.
+pub fn mapKey(cp: u21) u21 {
+ return switch (cp) {
+ vaxis.Key.kp_enter => pardes.Key.enter,
+ vaxis.Key.up => pardes.Key.up,
+ vaxis.Key.down => pardes.Key.down,
+ vaxis.Key.left => pardes.Key.left,
+ vaxis.Key.right => pardes.Key.right,
+ vaxis.Key.home => pardes.Key.home,
+ vaxis.Key.end => pardes.Key.end,
+ vaxis.Key.page_up => pardes.Key.page_up,
+ vaxis.Key.page_down => pardes.Key.page_down,
+ vaxis.Key.delete => pardes.Key.delete,
+ vaxis.Key.insert => pardes.Key.insert,
+ vaxis.Key.print_screen => pardes.Key.print_screen,
+ vaxis.Key.scroll_lock => pardes.Key.scroll_lock,
+ vaxis.Key.pause => pardes.Key.pause,
+ vaxis.Key.menu => pardes.Key.menu,
+ vaxis.Key.f1...vaxis.Key.f25 => pardes.Key.f1 + (cp - vaxis.Key.f1),
+ // The keypad's own keys, which the kitty keyboard protocol tells
+ // apart, do what the keys they are named for do.
+ vaxis.Key.kp_left => pardes.Key.left,
+ vaxis.Key.kp_right => pardes.Key.right,
+ vaxis.Key.kp_up => pardes.Key.up,
+ vaxis.Key.kp_down => pardes.Key.down,
+ vaxis.Key.kp_page_up => pardes.Key.page_up,
+ vaxis.Key.kp_page_down => pardes.Key.page_down,
+ vaxis.Key.kp_home => pardes.Key.home,
+ vaxis.Key.kp_end => pardes.Key.end,
+ vaxis.Key.kp_insert => pardes.Key.insert,
+ vaxis.Key.kp_delete => pardes.Key.delete,
+ else => cp,
+ };
+}
+
+/// The key as the core's event, its text the key's own.
+pub fn keyEvent(key: vaxis.Key) pardes.Event {
+ return .{ .key = .{
+ .cp = mapKey(effCp(key)),
+ .text = key.text orelse "",
+ .ctrl = key.mods.ctrl,
+ .alt = key.mods.alt,
+ .shift = key.mods.shift,
+ } };
+}
+
+test "F-keys, Insert and the keypad map to the core's keys" {
+ try std.testing.expectEqual(pardes.Key.f1 + 4, mapKey(vaxis.Key.f5));
+ try std.testing.expectEqual(pardes.Key.f1 + 24, mapKey(vaxis.Key.f25));
+ try std.testing.expectEqual(pardes.Key.insert, mapKey(vaxis.Key.insert));
+ try std.testing.expectEqual(pardes.Key.up, mapKey(vaxis.Key.kp_up));
+ try std.testing.expectEqual(pardes.Key.enter, keyEvent(.{ .codepoint = vaxis.Key.kp_enter }).key.cp);
+ try std.testing.expectEqual(@as(u21, 'a'), mapKey('a'));
+}