this repo has no description
13
fork

Configure Feed

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

feat: support legacy xterm-style mouse events

authored by

FalsePattern and committed by
Tim Culverhouse
c49abb48 1f41c121

+47 -11
+47 -11
src/Parser.zig
··· 467 467 468 468 'I' => return .{ .event = .focus_in, .n = sequence.len }, 469 469 'O' => return .{ .event = .focus_out, .n = sequence.len }, 470 - 'M', 'm' => return parseMouse(sequence), 470 + 'M', 'm' => return parseMouse(sequence, input), 471 471 'c' => { 472 472 // Primary DA (CSI ? Pm c) 473 473 std.debug.assert(sequence.len >= 4); // ESC [ ? c == 4 bytes ··· 656 656 } 657 657 658 658 /// Parse a mouse event 659 - inline fn parseMouse(input: []const u8) Result { 660 - std.debug.assert(input.len >= 4); // ESC [ < [Mm] 659 + inline fn parseMouse(input: []const u8, full_input: []const u8) Result { 661 660 const null_event: Result = .{ .event = null, .n = input.len }; 662 661 663 - if (input[2] != '<') return null_event; 664 - 665 - const delim1 = std.mem.indexOfScalarPos(u8, input, 3, ';') orelse return null_event; 666 - const button_mask = parseParam(u16, input[3..delim1], null) orelse return null_event; 667 - const delim2 = std.mem.indexOfScalarPos(u8, input, delim1 + 1, ';') orelse return null_event; 668 - const px = parseParam(u16, input[delim1 + 1 .. delim2], 1) orelse return null_event; 669 - const py = parseParam(u16, input[delim2 + 1 .. input.len - 1], 1) orelse return null_event; 662 + var button_mask: u16 = undefined; 663 + var px: u16 = undefined; 664 + var py: u16 = undefined; 665 + var xterm: bool = undefined; 666 + if (input.len == 3 and (input[2] == 'M') and full_input.len >= 6) { 667 + xterm = true; 668 + button_mask = full_input[3] - 32; 669 + px = full_input[4] - 32; 670 + py = full_input[5] - 32; 671 + } else if (input.len >= 4 and input[2] == '<') { 672 + xterm = false; 673 + const delim1 = std.mem.indexOfScalarPos(u8, input, 3, ';') orelse return null_event; 674 + button_mask = parseParam(u16, input[3..delim1], null) orelse return null_event; 675 + const delim2 = std.mem.indexOfScalarPos(u8, input, delim1 + 1, ';') orelse return null_event; 676 + px = parseParam(u16, input[delim1 + 1 .. delim2], 1) orelse return null_event; 677 + py = parseParam(u16, input[delim2 + 1 .. input.len - 1], 1) orelse return null_event; 678 + } else { 679 + return null_event; 680 + } 670 681 671 682 const button: Mouse.Button = @enumFromInt(button_mask & mouse_bits.buttons); 672 683 const motion = button_mask & mouse_bits.motion > 0; ··· 690 701 if (motion and button == Mouse.Button.none) { 691 702 break :blk .motion; 692 703 } 704 + if (xterm) { 705 + if (button == Mouse.Button.none) { 706 + break :blk .release; 707 + } 708 + break :blk .press; 709 + } 693 710 if (input[input.len - 1] == 'm') break :blk .release; 694 711 break :blk .press; 695 712 }, 696 713 }; 697 - return .{ .event = .{ .mouse = mouse }, .n = input.len }; 714 + return .{ .event = .{ .mouse = mouse }, .n = if (xterm) 6 else input.len }; 698 715 } 699 716 700 717 test "parse: single xterm keypress" { ··· 1142 1159 .row = 0, 1143 1160 .button = .none, 1144 1161 .type = .motion, 1162 + .mods = .{}, 1163 + } }, 1164 + .n = input.len, 1165 + }; 1166 + 1167 + try testing.expectEqual(expected.n, result.n); 1168 + try testing.expectEqual(expected.event, result.event); 1169 + } 1170 + 1171 + test "parse(csi): xterm mouse" { 1172 + var buf: [1]u8 = undefined; 1173 + const input = "\x1b[M\x20\x21\x21"; 1174 + const result = parseCsi(input, &buf); 1175 + const expected: Result = .{ 1176 + .event = .{ .mouse = .{ 1177 + .col = 0, 1178 + .row = 0, 1179 + .button = .left, 1180 + .type = .press, 1145 1181 .mods = .{}, 1146 1182 } }, 1147 1183 .n = input.len,