vaxis: add mode 2027 query parsing, wcwidth measurement
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
parent
ef7b30dbee
commit
a2dc97c039
5 changed files with 109 additions and 7 deletions
|
@ -281,19 +281,18 @@ pub fn parse(self: *Parser, input: []const u8) !Result {
|
|||
'u' => blk: {
|
||||
if (seq.private_indicator) |priv| {
|
||||
// response to our kitty query
|
||||
// TODO: kitty query handling
|
||||
if (priv == '?') {
|
||||
return .{
|
||||
.event = .cap_kitty_keyboard,
|
||||
.n = i + 1,
|
||||
};
|
||||
} else {
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{
|
||||
.event = null,
|
||||
.n = i + 1,
|
||||
};
|
||||
}
|
||||
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{
|
||||
.event = null,
|
||||
.n = i + 1,
|
||||
};
|
||||
}
|
||||
if (seq.param_idx == 0) {
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
|
@ -313,6 +312,51 @@ pub fn parse(self: *Parser, input: []const u8) !Result {
|
|||
'O' => { // focus out
|
||||
return .{ .event = .focus_out, .n = i + 1 };
|
||||
},
|
||||
'y' => { // DECRQM response
|
||||
const priv = seq.private_indicator orelse {
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{ .event = null, .n = i + 1 };
|
||||
};
|
||||
if (priv != '?') {
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{ .event = null, .n = i + 1 };
|
||||
}
|
||||
const intm = seq.intermediate orelse {
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{ .event = null, .n = i + 1 };
|
||||
};
|
||||
if (intm != '$') {
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{ .event = null, .n = i + 1 };
|
||||
}
|
||||
if (seq.param_idx != 2) {
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{ .event = null, .n = i + 1 };
|
||||
}
|
||||
// We'll get two fields, the first is the mode
|
||||
// we requested, the second is the status of the
|
||||
// mode
|
||||
// 0: not recognize
|
||||
// 1: set
|
||||
// 2: reset
|
||||
// 3: permanently set
|
||||
// 4: permanently reset
|
||||
switch (seq.params[0]) {
|
||||
2027 => {
|
||||
switch (seq.params[1]) {
|
||||
0, 4 => return .{ .event = null, .n = i + 1 },
|
||||
else => return .{ .event = .cap_unicode, .n = i + 1 },
|
||||
}
|
||||
},
|
||||
2031 => {},
|
||||
else => {
|
||||
log.warn("unhandled DECRPM: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{ .event = null, .n = i + 1 };
|
||||
},
|
||||
}
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{ .event = null, .n = i + 1 };
|
||||
},
|
||||
else => {
|
||||
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
|
||||
return .{
|
||||
|
|
|
@ -187,6 +187,11 @@ pub fn run(
|
|||
vx.postEvent(.cap_rgb);
|
||||
}
|
||||
},
|
||||
.cap_unicode => {
|
||||
if (@hasField(EventType, "cap_unicode")) {
|
||||
vx.postEvent(.cap_unicode);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,4 +12,5 @@ pub const Event = union(enum) {
|
|||
// these are delivered as discovered terminal capabilities
|
||||
cap_kitty_keyboard,
|
||||
cap_rgb,
|
||||
cap_unicode,
|
||||
};
|
||||
|
|
51
src/gwidth.zig
Normal file
51
src/gwidth.zig
Normal file
|
@ -0,0 +1,51 @@
|
|||
const std = @import("std");
|
||||
const unicode = std.unicode;
|
||||
const testing = std.testing;
|
||||
const ziglyph = @import("ziglyph");
|
||||
|
||||
/// the method to use when calculating the width of a grapheme
|
||||
pub const Method = enum {
|
||||
unicode,
|
||||
wcwidth,
|
||||
};
|
||||
|
||||
/// returns the width of the provided string, as measured by the method chosen
|
||||
pub fn gwidth(str: []const u8, method: Method) !usize {
|
||||
switch (method) {
|
||||
.unicode => {
|
||||
return try ziglyph.display_width.strWidth(str, .half);
|
||||
},
|
||||
.wcwidth => {
|
||||
var total: usize = 0;
|
||||
const utf8 = try unicode.Utf8View.init(str);
|
||||
var iter = utf8.iterator();
|
||||
|
||||
while (iter.nextCodepoint()) |cp| {
|
||||
const w = ziglyph.display_width.codePointWidth(cp, .half);
|
||||
if (w < 0) continue;
|
||||
total += @intCast(w);
|
||||
}
|
||||
return total;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test "gwidth: a" {
|
||||
try testing.expectEqual(1, try gwidth("a", .unicode));
|
||||
try testing.expectEqual(1, try gwidth("a", .wcwidth));
|
||||
}
|
||||
|
||||
test "gwidth: emoji with ZWJ" {
|
||||
try testing.expectEqual(2, try gwidth("👩🚀", .unicode));
|
||||
try testing.expectEqual(4, try gwidth("👩🚀", .wcwidth));
|
||||
}
|
||||
|
||||
test "gwidth: emoji with skin tone selector" {
|
||||
try testing.expectEqual(2, try gwidth("👋🏿", .unicode));
|
||||
try testing.expectEqual(4, try gwidth("👋🏿", .wcwidth));
|
||||
}
|
||||
|
||||
test "gwidth: invalid string" {
|
||||
try testing.expectError(error.InvalidUtf8, gwidth("\xc3\x28", .unicode));
|
||||
try testing.expectError(error.InvalidUtf8, gwidth("\xc3\x28", .wcwidth));
|
||||
}
|
|
@ -27,6 +27,7 @@ test {
|
|||
_ = @import("cell.zig");
|
||||
_ = @import("ctlseqs.zig");
|
||||
_ = @import("event.zig");
|
||||
_ = @import("gwidth.zig");
|
||||
_ = @import("queue.zig");
|
||||
_ = @import("vaxis.zig");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue