this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

vaxis: add mode 2027 query parsing, wcwidth measurement

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>

+109 -7
+51 -7
src/Parser.zig
··· 281 281 'u' => blk: { 282 282 if (seq.private_indicator) |priv| { 283 283 // response to our kitty query 284 - // TODO: kitty query handling 285 284 if (priv == '?') { 286 285 return .{ 287 286 .event = .cap_kitty_keyboard, 287 + .n = i + 1, 288 + }; 289 + } else { 290 + log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]}); 291 + return .{ 292 + .event = null, 288 293 .n = i + 1, 289 294 }; 290 295 } 291 - 292 - log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]}); 293 - return .{ 294 - .event = null, 295 - .n = i + 1, 296 - }; 297 296 } 298 297 if (seq.param_idx == 0) { 299 298 log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]}); ··· 312 311 }, 313 312 'O' => { // focus out 314 313 return .{ .event = .focus_out, .n = i + 1 }; 314 + }, 315 + 'y' => { // DECRQM response 316 + const priv = seq.private_indicator orelse { 317 + log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]}); 318 + return .{ .event = null, .n = i + 1 }; 319 + }; 320 + if (priv != '?') { 321 + log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]}); 322 + return .{ .event = null, .n = i + 1 }; 323 + } 324 + const intm = seq.intermediate orelse { 325 + log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]}); 326 + return .{ .event = null, .n = i + 1 }; 327 + }; 328 + if (intm != '$') { 329 + log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]}); 330 + return .{ .event = null, .n = i + 1 }; 331 + } 332 + if (seq.param_idx != 2) { 333 + log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]}); 334 + return .{ .event = null, .n = i + 1 }; 335 + } 336 + // We'll get two fields, the first is the mode 337 + // we requested, the second is the status of the 338 + // mode 339 + // 0: not recognize 340 + // 1: set 341 + // 2: reset 342 + // 3: permanently set 343 + // 4: permanently reset 344 + switch (seq.params[0]) { 345 + 2027 => { 346 + switch (seq.params[1]) { 347 + 0, 4 => return .{ .event = null, .n = i + 1 }, 348 + else => return .{ .event = .cap_unicode, .n = i + 1 }, 349 + } 350 + }, 351 + 2031 => {}, 352 + else => { 353 + log.warn("unhandled DECRPM: CSI {s}", .{input[start + 1 .. i + 1]}); 354 + return .{ .event = null, .n = i + 1 }; 355 + }, 356 + } 357 + log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]}); 358 + return .{ .event = null, .n = i + 1 }; 315 359 }, 316 360 else => { 317 361 log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
+5
src/Tty.zig
··· 187 187 vx.postEvent(.cap_rgb); 188 188 } 189 189 }, 190 + .cap_unicode => { 191 + if (@hasField(EventType, "cap_unicode")) { 192 + vx.postEvent(.cap_unicode); 193 + } 194 + }, 190 195 } 191 196 } 192 197 }
+1
src/event.zig
··· 12 12 // these are delivered as discovered terminal capabilities 13 13 cap_kitty_keyboard, 14 14 cap_rgb, 15 + cap_unicode, 15 16 };
+51
src/gwidth.zig
··· 1 + const std = @import("std"); 2 + const unicode = std.unicode; 3 + const testing = std.testing; 4 + const ziglyph = @import("ziglyph"); 5 + 6 + /// the method to use when calculating the width of a grapheme 7 + pub const Method = enum { 8 + unicode, 9 + wcwidth, 10 + }; 11 + 12 + /// returns the width of the provided string, as measured by the method chosen 13 + pub fn gwidth(str: []const u8, method: Method) !usize { 14 + switch (method) { 15 + .unicode => { 16 + return try ziglyph.display_width.strWidth(str, .half); 17 + }, 18 + .wcwidth => { 19 + var total: usize = 0; 20 + const utf8 = try unicode.Utf8View.init(str); 21 + var iter = utf8.iterator(); 22 + 23 + while (iter.nextCodepoint()) |cp| { 24 + const w = ziglyph.display_width.codePointWidth(cp, .half); 25 + if (w < 0) continue; 26 + total += @intCast(w); 27 + } 28 + return total; 29 + }, 30 + } 31 + } 32 + 33 + test "gwidth: a" { 34 + try testing.expectEqual(1, try gwidth("a", .unicode)); 35 + try testing.expectEqual(1, try gwidth("a", .wcwidth)); 36 + } 37 + 38 + test "gwidth: emoji with ZWJ" { 39 + try testing.expectEqual(2, try gwidth("๐Ÿ‘ฉโ€๐Ÿš€", .unicode)); 40 + try testing.expectEqual(4, try gwidth("๐Ÿ‘ฉโ€๐Ÿš€", .wcwidth)); 41 + } 42 + 43 + test "gwidth: emoji with skin tone selector" { 44 + try testing.expectEqual(2, try gwidth("๐Ÿ‘‹๐Ÿฟ", .unicode)); 45 + try testing.expectEqual(4, try gwidth("๐Ÿ‘‹๐Ÿฟ", .wcwidth)); 46 + } 47 + 48 + test "gwidth: invalid string" { 49 + try testing.expectError(error.InvalidUtf8, gwidth("\xc3\x28", .unicode)); 50 + try testing.expectError(error.InvalidUtf8, gwidth("\xc3\x28", .wcwidth)); 51 + }
+1
src/main.zig
··· 27 27 _ = @import("cell.zig"); 28 28 _ = @import("ctlseqs.zig"); 29 29 _ = @import("event.zig"); 30 + _ = @import("gwidth.zig"); 30 31 _ = @import("queue.zig"); 31 32 _ = @import("vaxis.zig"); 32 33 }