parser: more kitty key parsing

Add some more implementation details to kitty key parsing

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
Tim Culverhouse 2024-01-21 11:53:25 -06:00
parent 5df727f77d
commit 38b41c4ecf

View file

@ -139,6 +139,7 @@ pub fn run(
param_buf: [8]u8 = undefined, param_buf: [8]u8 = undefined,
param_buf_idx: usize = 0, param_buf_idx: usize = 0,
sub_state: std.StaticBitSet(16) = std.StaticBitSet(16).initEmpty(), sub_state: std.StaticBitSet(16) = std.StaticBitSet(16).initEmpty(),
empty_state: std.StaticBitSet(16) = std.StaticBitSet(16).initEmpty(),
}; };
var seq: Sequence = .{}; var seq: Sequence = .{};
@ -151,6 +152,7 @@ pub fn run(
// initialize the read buffer // initialize the read buffer
var buf: [1024]u8 = undefined; var buf: [1024]u8 = undefined;
// read loop
while (true) { while (true) {
_ = try std.os.poll(&pollfds, -1); _ = try std.os.poll(&pollfds, -1);
if (pollfds[1].revents & std.os.POLL.IN != 0) { if (pollfds[1].revents & std.os.POLL.IN != 0) {
@ -161,6 +163,8 @@ pub fn run(
const n = try os.read(self.fd, &buf); const n = try os.read(self.fd, &buf);
var i: usize = 0; var i: usize = 0;
var start: usize = 0; var start: usize = 0;
// parse the read into events. This parser is bespoke for input parsing
// and is not suitable for reuse as a generic vt parser
while (i < n) : (i += 1) { while (i < n) : (i += 1) {
const b = buf[i]; const b = buf[i];
switch (state) { switch (state) {
@ -169,9 +173,9 @@ pub fn run(
// generally get ascii characters, but anything less than // generally get ascii characters, but anything less than
// 0x20 is a Ctrl+<c> keypress. We map these to lowercase // 0x20 is a Ctrl+<c> keypress. We map these to lowercase
// ascii characters when we can // ascii characters when we can
const key: ?Key = switch (b) { const key: Key = switch (b) {
0x00 => Key{ .codepoint = '@', .mods = .{ .ctrl = true } }, 0x00 => .{ .codepoint = '@', .mods = .{ .ctrl = true } },
0x01...0x1A => Key{ .codepoint = b + 0x60, .mods = .{ .ctrl = true } }, 0x01...0x1A => .{ .codepoint = b + 0x60, .mods = .{ .ctrl = true } },
0x1B => escape: { 0x1B => escape: {
// NOTE: This could be an errant escape at the end // NOTE: This could be an errant escape at the end
// of a large read. That is _incredibly_ unlikely // of a large read. That is _incredibly_ unlikely
@ -183,16 +187,15 @@ pub fn run(
break :escape event; break :escape event;
} }
state = .escape; state = .escape;
break :escape null; continue;
}, },
0x20...0x7E => Key{ .codepoint = b }, 0x20...0x7E => .{ .codepoint = b },
0x7F => Key{ .codepoint = Key.backspace }, 0x7F => .{ .codepoint = Key.backspace },
else => Key{ .codepoint = b }, // TODO: graphemes
else => .{ .codepoint = b },
}; };
if (key) |k| { if (@hasField(EventType, "key_press")) {
if (@hasField(EventType, "key_press")) { vx.postEvent(.{ .key_press = key });
vx.postEvent(.{ .key_press = k });
}
} }
}, },
.escape => { .escape => {
@ -221,7 +224,8 @@ pub fn run(
} }
}, },
.ss3 => { .ss3 => {
const key: ?Key = switch (b) { state = .ground;
const key: Key = switch (b) {
'A' => .{ .codepoint = Key.up }, 'A' => .{ .codepoint = Key.up },
'B' => .{ .codepoint = Key.down }, 'B' => .{ .codepoint = Key.down },
'C' => .{ .codepoint = Key.right }, 'C' => .{ .codepoint = Key.right },
@ -232,17 +236,15 @@ pub fn run(
'Q' => .{ .codepoint = Key.f2 }, 'Q' => .{ .codepoint = Key.f2 },
'R' => .{ .codepoint = Key.f3 }, 'R' => .{ .codepoint = Key.f3 },
'S' => .{ .codepoint = Key.f4 }, 'S' => .{ .codepoint = Key.f4 },
else => blk: { else => {
log.warn("unhandled ss3: {x}", .{b}); log.warn("unhandled ss3: {x}", .{b});
break :blk null; continue;
}, },
}; };
if (key) |k| {
if (@hasField(EventType, "key_press")) { if (@hasField(EventType, "key_press")) {
vx.postEvent(.{ .key_press = k }); vx.postEvent(.{ .key_press = key });
}
} }
state = .ground;
}, },
.csi => { .csi => {
switch (b) { switch (b) {
@ -260,8 +262,10 @@ pub fn run(
0x3C...0x3F => seq.private_indicator = b, 0x3C...0x3F => seq.private_indicator = b,
';' => { ';' => {
if (seq.param_buf_idx == 0) { if (seq.param_buf_idx == 0) {
// empty param. default it to 1 // empty param. default it to 0 and set the
seq.params[seq.param_idx] = 1; // empty state
seq.params[seq.param_idx] = 0;
seq.empty_state.set(seq.param_idx);
seq.param_idx += 1; seq.param_idx += 1;
} else { } else {
const p = try std.fmt.parseUnsigned(u16, seq.param_buf[0..seq.param_buf_idx], 10); const p = try std.fmt.parseUnsigned(u16, seq.param_buf[0..seq.param_buf_idx], 10);
@ -272,8 +276,10 @@ pub fn run(
}, },
':' => { ':' => {
if (seq.param_buf_idx == 0) { if (seq.param_buf_idx == 0) {
// empty param. default it to 1 // empty param. default it to 0 and set the
seq.params[seq.param_idx] = 1; // empty state
seq.params[seq.param_idx] = 0;
seq.empty_state.set(seq.param_idx);
seq.param_idx += 1; seq.param_idx += 1;
// Set the *next* param as a subparam // Set the *next* param as a subparam
seq.sub_state.set(seq.param_idx); seq.sub_state.set(seq.param_idx);
@ -287,7 +293,13 @@ pub fn run(
} }
}, },
0x40...0xFF => { 0x40...0xFF => {
// dispatch our sequence if (seq.param_buf_idx > 0) {
const p = try std.fmt.parseUnsigned(u16, seq.param_buf[0..seq.param_buf_idx], 10);
seq.param_buf_idx = 0;
seq.params[seq.param_idx] = p;
seq.param_idx += 1;
}
// dispatch the sequence
state = .ground; state = .ground;
const codepoint: u21 = switch (b) { const codepoint: u21 = switch (b) {
'A' => Key.up, 'A' => Key.up,
@ -376,7 +388,47 @@ pub fn run(
}, },
}; };
const key: Key = .{ .codepoint = codepoint }; var key: Key = .{ .codepoint = codepoint };
var idx: usize = 0;
var field: u8 = 0;
// parse the parameters
while (idx < seq.param_idx) : (idx += 1) {
switch (field) {
0 => {
defer field += 1;
// field 0 contains our codepoint. Any
// subparameters shifted key code and
// alternate keycode (csi u encoding)
// We already handled our codepoint so
// we just need to check for subs
if (!seq.sub_state.isSet(idx + 1)) {
continue;
}
idx += 1;
// The first one is a shifted code if it
// isn't empty
if (!seq.empty_state.isSet(idx)) {
key.shifted_codepoint = seq.params[idx];
}
// check the next one for base layout
// code
if (!seq.sub_state.isSet(idx + 1)) {
continue;
}
idx += 1;
key.base_layout_codepoint = seq.params[idx];
},
1 => {
// field 1 is modifiers and optionally
// the event type (csiu)
const mod_mask: u8 = @truncate(seq.params[idx] - 1);
key.mods = @bitCast(mod_mask);
},
else => {},
}
}
if (@hasField(EventType, "key_press")) { if (@hasField(EventType, "key_press")) {
vx.postEvent(.{ .key_press = key }); vx.postEvent(.{ .key_press = key });
} }