this repo has no description
13
fork

Configure Feed

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

key: enable kitty keyboard

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

+114 -29
+4
examples/text_input.zig
··· 61 61 .winsize => |ws| { 62 62 try vx.resize(alloc, ws); 63 63 }, 64 + .cap_rgb => continue, 65 + .cap_kitty_keyboard => try vx.enableKittyKeyboard(.{}), 64 66 else => {}, 65 67 } 66 68 ··· 96 98 key_press: vaxis.Key, 97 99 winsize: vaxis.Winsize, 98 100 focus_in, 101 + cap_rgb, 102 + cap_kitty_keyboard, 99 103 foo: u8, 100 104 };
+8
src/Key.zig
··· 15 15 num_lock: bool = false, 16 16 }; 17 17 18 + pub const KittyFlags = packed struct(u5) { 19 + disambiguate: bool = true, 20 + report_events: bool = false, 21 + report_alternate_keys: bool = true, 22 + report_all_as_ctl_seqs: bool = true, 23 + report_text: bool = true, 24 + }; 25 + 18 26 /// the unicode codepoint of the key event. 19 27 codepoint: u21, 20 28
+8 -1
src/Tty.zig
··· 4 4 const vaxis = @import("main.zig"); 5 5 const Vaxis = vaxis.Vaxis; 6 6 const Event = @import("event.zig").Event; 7 - const parser = @import("parser.zig"); 7 + const Parser = @import("Parser.zig"); 8 8 const Key = vaxis.Key; 9 9 const GraphemeCache = @import("GraphemeCache.zig"); 10 10 ··· 122 122 .{ .fd = pipe[0], .events = std.os.POLL.IN, .revents = undefined }, 123 123 }; 124 124 125 + var parser: Parser = .{}; 126 + 125 127 // initialize the read buffer 126 128 var buf: [1024]u8 = undefined; 127 129 // read loop ··· 178 180 .cap_kitty_keyboard => { 179 181 if (@hasField(EventType, "cap_kitty_keyboard")) { 180 182 vx.postEvent(.cap_kitty_keyboard); 183 + } 184 + }, 185 + .cap_rgb => { 186 + if (@hasField(EventType, "cap_rgb")) { 187 + vx.postEvent(.cap_rgb); 181 188 } 182 189 }, 183 190 }
+3
src/event.zig
··· 8 8 focus_out, 9 9 paste_start, 10 10 paste_end, 11 + 12 + // these are delivered as discovered terminal capabilities 11 13 cap_kitty_keyboard, 14 + cap_rgb, 12 15 };
+1 -1
src/main.zig
··· 20 20 _ = @import("GraphemeCache.zig"); 21 21 _ = @import("Key.zig"); 22 22 _ = @import("Options.zig"); 23 + _ = @import("Parser.zig"); 23 24 _ = @import("Screen.zig"); 24 25 _ = @import("Tty.zig"); 25 26 _ = @import("Window.zig"); ··· 27 28 _ = @import("ctlseqs.zig"); 28 29 _ = @import("event.zig"); 29 30 _ = @import("queue.zig"); 30 - _ = @import("parser.zig"); 31 31 _ = @import("vaxis.zig"); 32 32 }
+68 -25
src/parser.zig src/Parser.zig
··· 7 7 8 8 const log = std.log.scoped(.parser); 9 9 10 + const Parser = @This(); 11 + 10 12 /// The return type of our parse method. Contains an Event and the number of 11 13 /// bytes read from the buffer. 12 14 pub const Result = struct { ··· 44 46 ss3, 45 47 }; 46 48 47 - pub fn parse(input: []const u8) !Result { 49 + // a buffer to temporarily store text in. We need this to encode 50 + // text-as-codepoints 51 + buf: [128]u8 = undefined, 52 + 53 + pub fn parse(self: *Parser, input: []const u8) !Result { 48 54 const n = input.len; 49 55 50 56 var seq: Sequence = .{}; ··· 349 355 key.base_layout_codepoint = seq.params[idx]; 350 356 }, 351 357 1 => { 358 + defer field += 1; 352 359 // field 1 is modifiers and optionally 353 - // the event type (csiu) 354 - const mod_mask: u8 = @truncate(seq.params[idx] - 1); 355 - key.mods = @bitCast(mod_mask); 360 + // the event type (csiu). It can be empty 361 + if (seq.empty_state.isSet(idx)) { 362 + continue; 363 + } 364 + // default of 1 365 + const ps: u8 = blk: { 366 + if (seq.params[idx] == 0) break :blk 1; 367 + break :blk @truncate(seq.params[idx]); 368 + }; 369 + key.mods = @bitCast(ps - 1); 370 + }, 371 + 2 => { 372 + // field 2 is text, as codepoints 373 + var total: usize = 0; 374 + while (idx < seq.param_idx) : (idx += 1) { 375 + total += try std.unicode.utf8Encode(seq.params[idx], self.buf[total..]); 376 + } 377 + key.text = self.buf[0..total]; 356 378 }, 357 379 else => {}, 358 380 } ··· 377 399 378 400 test "parse: single xterm keypress" { 379 401 const input = "a"; 380 - const result = try parse(input); 402 + var parser: Parser = .{}; 403 + const result = try parser.parse(input); 381 404 const expected_key: Key = .{ 382 405 .codepoint = 'a', 383 406 .text = "a", ··· 390 413 391 414 test "parse: single xterm keypress with more buffer" { 392 415 const input = "ab"; 393 - const result = try parse(input); 416 + var parser: Parser = .{}; 417 + const result = try parser.parse(input); 394 418 const expected_key: Key = .{ 395 419 .codepoint = 'a', 396 420 .text = "a", ··· 404 428 405 429 test "parse: xterm escape keypress" { 406 430 const input = "\x1b"; 407 - const result = try parse(input); 431 + var parser: Parser = .{}; 432 + const result = try parser.parse(input); 408 433 const expected_key: Key = .{ .codepoint = Key.escape }; 409 434 const expected_event: Event = .{ .key_press = expected_key }; 410 435 ··· 414 439 415 440 test "parse: xterm ctrl+a" { 416 441 const input = "\x01"; 417 - const result = try parse(input); 442 + var parser: Parser = .{}; 443 + const result = try parser.parse(input); 418 444 const expected_key: Key = .{ .codepoint = 'a', .mods = .{ .ctrl = true } }; 419 445 const expected_event: Event = .{ .key_press = expected_key }; 420 446 ··· 424 450 425 451 test "parse: xterm alt+a" { 426 452 const input = "\x1ba"; 427 - const result = try parse(input); 453 + var parser: Parser = .{}; 454 + const result = try parser.parse(input); 428 455 const expected_key: Key = .{ .codepoint = 'a', .mods = .{ .alt = true } }; 429 456 const expected_event: Event = .{ .key_press = expected_key }; 430 457 ··· 434 461 435 462 test "parse: xterm invalid ss3" { 436 463 const input = "\x1bOZ"; 437 - const result = try parse(input); 464 + var parser: Parser = .{}; 465 + const result = try parser.parse(input); 438 466 439 467 try testing.expectEqual(3, result.n); 440 468 try testing.expectEqual(null, result.event); ··· 444 472 { 445 473 // normal version 446 474 const input = "\x1bOA"; 447 - const result = try parse(input); 475 + var parser: Parser = .{}; 476 + const result = try parser.parse(input); 448 477 const expected_key: Key = .{ .codepoint = Key.up }; 449 478 const expected_event: Event = .{ .key_press = expected_key }; 450 479 ··· 455 484 { 456 485 // application keys version 457 486 const input = "\x1b[2~"; 458 - const result = try parse(input); 487 + var parser: Parser = .{}; 488 + const result = try parser.parse(input); 459 489 const expected_key: Key = .{ .codepoint = Key.insert }; 460 490 const expected_event: Event = .{ .key_press = expected_key }; 461 491 ··· 466 496 467 497 test "parse: xterm shift+up" { 468 498 const input = "\x1b[1;2A"; 469 - const result = try parse(input); 499 + var parser: Parser = .{}; 500 + const result = try parser.parse(input); 470 501 const expected_key: Key = .{ .codepoint = Key.up, .mods = .{ .shift = true } }; 471 502 const expected_event: Event = .{ .key_press = expected_key }; 472 503 ··· 476 507 477 508 test "parse: xterm insert" { 478 509 const input = "\x1b[1;2A"; 479 - const result = try parse(input); 510 + var parser: Parser = .{}; 511 + const result = try parser.parse(input); 480 512 const expected_key: Key = .{ .codepoint = Key.up, .mods = .{ .shift = true } }; 481 513 const expected_event: Event = .{ .key_press = expected_key }; 482 514 ··· 486 518 487 519 test "parse: paste_start" { 488 520 const input = "\x1b[200~"; 489 - const result = try parse(input); 521 + var parser: Parser = .{}; 522 + const result = try parser.parse(input); 490 523 const expected_event: Event = .paste_start; 491 524 492 525 try testing.expectEqual(6, result.n); ··· 495 528 496 529 test "parse: paste_end" { 497 530 const input = "\x1b[201~"; 498 - const result = try parse(input); 531 + var parser: Parser = .{}; 532 + const result = try parser.parse(input); 499 533 const expected_event: Event = .paste_end; 500 534 501 535 try testing.expectEqual(6, result.n); ··· 504 538 505 539 test "parse: focus_in" { 506 540 const input = "\x1b[I"; 507 - const result = try parse(input); 541 + var parser: Parser = .{}; 542 + const result = try parser.parse(input); 508 543 const expected_event: Event = .focus_in; 509 544 510 545 try testing.expectEqual(3, result.n); ··· 513 548 514 549 test "parse: focus_out" { 515 550 const input = "\x1b[O"; 516 - const result = try parse(input); 551 + var parser: Parser = .{}; 552 + const result = try parser.parse(input); 517 553 const expected_event: Event = .focus_out; 518 554 519 555 try testing.expectEqual(3, result.n); ··· 522 558 523 559 test "parse: kitty: shift+a without text reporting" { 524 560 const input = "\x1b[97:65;2u"; 525 - const result = try parse(input); 561 + var parser: Parser = .{}; 562 + const result = try parser.parse(input); 526 563 const expected_key: Key = .{ 527 564 .codepoint = 'a', 528 565 .shifted_codepoint = 'A', ··· 536 573 537 574 test "parse: kitty: alt+shift+a without text reporting" { 538 575 const input = "\x1b[97:65;4u"; 539 - const result = try parse(input); 576 + var parser: Parser = .{}; 577 + const result = try parser.parse(input); 540 578 const expected_key: Key = .{ 541 579 .codepoint = 'a', 542 580 .shifted_codepoint = 'A', ··· 550 588 551 589 test "parse: kitty: a without text reporting" { 552 590 const input = "\x1b[97u"; 553 - const result = try parse(input); 591 + var parser: Parser = .{}; 592 + const result = try parser.parse(input); 554 593 const expected_key: Key = .{ 555 594 .codepoint = 'a', 556 595 }; ··· 562 601 563 602 test "parse: single codepoint" { 564 603 const input = "🙂"; 565 - const result = try parse(input); 604 + var parser: Parser = .{}; 605 + const result = try parser.parse(input); 566 606 const expected_key: Key = .{ 567 607 .codepoint = 0x1F642, 568 608 .text = input, ··· 575 615 576 616 test "parse: single codepoint with more in buffer" { 577 617 const input = "🙂a"; 578 - const result = try parse(input); 618 + var parser: Parser = .{}; 619 + const result = try parser.parse(input); 579 620 const expected_key: Key = .{ 580 621 .codepoint = 0x1F642, 581 622 .text = "🙂", ··· 590 631 // TODO: this test is passing but throws a warning. Not sure how we'll 591 632 // handle graphemes yet 592 633 const input = "👩‍🚀"; 593 - const result = try parse(input); 634 + var parser: Parser = .{}; 635 + const result = try parser.parse(input); 594 636 const expected_key: Key = .{ 595 637 .codepoint = Key.multicodepoint, 596 638 .text = input, ··· 605 647 // TODO: this test is passing but throws a warning. Not sure how we'll 606 648 // handle graphemes yet 607 649 const input = "👩‍🚀abc"; 608 - const result = try parse(input); 650 + var parser: Parser = .{}; 651 + const result = try parser.parse(input); 609 652 const expected_key: Key = .{ 610 653 .codepoint = Key.multicodepoint, 611 654 .text = "👩‍🚀",
+21 -1
src/vaxis.zig
··· 45 45 /// alt_screen state. We track so we can exit on deinit 46 46 alt_screen: bool, 47 47 48 + /// if we have entered kitty keyboard 49 + kitty_keyboard: bool = false, 50 + 48 51 /// if we should redraw the entire screen on the next render 49 52 refresh: bool = false, 50 53 ··· 72 75 var tty = &self.tty.?; 73 76 if (self.alt_screen) { 74 77 _ = tty.write(ctlseqs.rmcup) catch {}; 78 + tty.flush() catch {}; 79 + } 80 + if (self.kitty_keyboard) { 81 + _ = tty.write(ctlseqs.csi_u_pop) catch {}; 75 82 tty.flush() catch {}; 76 83 } 77 84 tty.deinit(); ··· 169 176 if (std.mem.eql(u8, colorterm, "truecolor") or 170 177 std.mem.eql(u8, colorterm, "24bit")) 171 178 { 172 - // TODO: Notify rgb support 179 + if (@hasField(EventType, "cap_rgb")) { 180 + self.postEvent(.cap_rgb); 181 + } 173 182 } 174 183 175 184 // TODO: decide if we actually want to query for focus and sync. It ··· 410 419 ); 411 420 _ = try tty.write(ctlseqs.show_cursor); 412 421 } 422 + } 423 + 424 + pub fn enableKittyKeyboard(self: *Self, flags: Key.KittyFlags) !void { 425 + const flag_int: u5 = @bitCast(flags); 426 + try std.fmt.format( 427 + self.tty.?.buffered_writer.writer(), 428 + ctlseqs.csi_u_push, 429 + .{ 430 + flag_int, 431 + }, 432 + ); 413 433 } 414 434 }; 415 435 }
+1 -1
src/widgets/TextInput.zig
··· 67 67 var cursor_idx: usize = 0; 68 68 while (iter.next()) |grapheme| { 69 69 const g = grapheme.slice(self.buf.items); 70 - const w = strWidth(g, .full) catch 1; 70 + const w = strWidth(g, .half) catch 1; 71 71 win.writeCell(col, 0, .{ 72 72 .char = .{ 73 73 .grapheme = g,