this repo has no description
13
fork

Configure Feed

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

widgets(terminal): implement legacy key encoding

+178 -19
+1 -1
examples/vt.zig
··· 82 82 } 83 83 84 84 const win = vx.window(); 85 - win.clear(); 86 85 win.hideCursor(); 86 + win.clear(); 87 87 const child = win.child(.{ 88 88 .x_off = 4, 89 89 .y_off = 2,
+2
src/widgets/terminal/Screen.zig
··· 90 90 91 91 cursor: Cursor = .{}, 92 92 93 + csi_u_flags: vaxis.Key.KittyFlags = @bitCast(@as(u5, 0)), 94 + 93 95 /// sets each cell to the default cell 94 96 pub fn init(alloc: std.mem.Allocator, w: usize, h: usize) !Screen { 95 97 var screen = Screen{
+3 -18
src/widgets/terminal/Terminal.zig
··· 14 14 const Key = vaxis.Key; 15 15 const Queue = vaxis.Queue(Event, 16); 16 16 const code_point = @import("code_point"); 17 + const key = @import("key.zig"); 17 18 18 19 pub const Event = union(enum) { 19 20 exited, ··· 207 208 if (self.mode.cursor) { 208 209 win.setCursorShape(self.front_screen.cursor.shape); 209 210 win.showCursor(self.front_screen.cursor.col, self.front_screen.cursor.row); 210 - } else win.hideCursor(); 211 + } 211 212 } 212 213 213 214 pub fn tryEvent(self: *Terminal) ?Event { ··· 216 217 217 218 pub fn update(self: *Terminal, event: InputEvent) !void { 218 219 switch (event) { 219 - .key_press => |key| try self.encodeKey(key, true), 220 + .key_press => |k| try key.encode(self.anyWriter(), k, true, self.back_screen.csi_u_flags), 220 221 } 221 222 } 222 223 ··· 695 696 }, 696 697 2026 => self.mode.sync = val, 697 698 else => return, 698 - } 699 - } 700 - 701 - pub fn encodeKey(self: *Terminal, key: vaxis.Key, press: bool) !void { 702 - switch (press) { 703 - true => { 704 - if (key.text) |text| { 705 - try self.anyWriter().writeAll(text); 706 - return; 707 - } 708 - switch (key.codepoint) { 709 - 0x00...0x7F => try self.anyWriter().writeByte(@intCast(key.codepoint)), 710 - else => {}, 711 - } 712 - }, 713 - false => {}, 714 699 } 715 700 } 716 701
+172
src/widgets/terminal/key.zig
··· 1 + const std = @import("std"); 2 + const vaxis = @import("../../main.zig"); 3 + 4 + pub fn encode( 5 + writer: std.io.AnyWriter, 6 + key: vaxis.Key, 7 + press: bool, 8 + kitty_flags: vaxis.Key.KittyFlags, 9 + ) !void { 10 + const flags: u5 = @bitCast(kitty_flags); 11 + switch (press) { 12 + true => { 13 + switch (flags) { 14 + 0 => try legacy(writer, key), 15 + else => unreachable, // TODO: kitty encodings 16 + } 17 + }, 18 + false => {}, 19 + } 20 + } 21 + 22 + fn legacy(writer: std.io.AnyWriter, key: vaxis.Key) !void { 23 + // If we have text, we always write it directly 24 + if (key.text) |text| { 25 + try writer.writeAll(text); 26 + return; 27 + } 28 + 29 + const shift = 0b00000001; 30 + const alt = 0b00000010; 31 + const ctrl = 0b00000100; 32 + 33 + const effective_mods: u8 = blk: { 34 + const mods: u8 = @bitCast(key.mods); 35 + break :blk mods & (shift | alt | ctrl); 36 + }; 37 + 38 + // If we have no mods and an ascii byte, write it directly 39 + if (effective_mods == 0 and key.codepoint <= 0x7F) { 40 + const b: u8 = @truncate(key.codepoint); 41 + try writer.writeByte(b); 42 + return; 43 + } 44 + 45 + // If we are lowercase ascii and ctrl, we map to a control byte 46 + if (effective_mods == ctrl and key.codepoint >= 'a' and key.codepoint <= 'z') { 47 + const b: u8 = @truncate(key.codepoint); 48 + try writer.writeByte(b -| 0x60); 49 + return; 50 + } 51 + 52 + // If we are printable ascii + alt 53 + if (effective_mods == alt and key.codepoint >= ' ' and key.codepoint < 0x7F) { 54 + const b: u8 = @truncate(key.codepoint); 55 + try writer.print("\x1b{c}", .{b}); 56 + return; 57 + } 58 + 59 + // If we are ctrl + alt + lowercase ascii 60 + if (effective_mods == (ctrl | alt) and key.codepoint >= 'a' and key.codepoint <= 'z') { 61 + // convert to control sequence 62 + try writer.print("\x1b{d}", .{key.codepoint - 0x60}); 63 + } 64 + 65 + const def = switch (key.codepoint) { 66 + vaxis.Key.escape => escape, 67 + vaxis.Key.enter, 68 + vaxis.Key.kp_enter, 69 + => enter, 70 + vaxis.Key.tab => tab, 71 + vaxis.Key.backspace => backspace, 72 + vaxis.Key.insert, 73 + vaxis.Key.kp_insert, 74 + => insert, 75 + vaxis.Key.delete, 76 + vaxis.Key.kp_delete, 77 + => delete, 78 + vaxis.Key.left, 79 + vaxis.Key.kp_left, 80 + => left, 81 + vaxis.Key.right, 82 + vaxis.Key.kp_right, 83 + => right, 84 + vaxis.Key.up, 85 + vaxis.Key.kp_up, 86 + => up, 87 + vaxis.Key.down, 88 + vaxis.Key.kp_down, 89 + => down, 90 + vaxis.Key.page_up, 91 + vaxis.Key.kp_page_up, 92 + => page_up, 93 + vaxis.Key.page_down, 94 + vaxis.Key.kp_page_down, 95 + => page_down, 96 + vaxis.Key.home, 97 + vaxis.Key.kp_home, 98 + => home, 99 + vaxis.Key.end, 100 + vaxis.Key.kp_end, 101 + => end, 102 + vaxis.Key.f1 => f1, 103 + vaxis.Key.f2 => f2, 104 + vaxis.Key.f3 => f3_legacy, 105 + vaxis.Key.f4 => f4, 106 + vaxis.Key.f5 => f5, 107 + vaxis.Key.f6 => f6, 108 + vaxis.Key.f7 => f7, 109 + vaxis.Key.f8 => f8, 110 + vaxis.Key.f9 => f9, 111 + vaxis.Key.f10 => f10, 112 + vaxis.Key.f11 => f11, 113 + vaxis.Key.f12 => f12, 114 + else => return, // TODO: more keys 115 + }; 116 + 117 + switch (effective_mods) { 118 + 0 => { 119 + if (def.number == 1) 120 + switch (key.codepoint) { 121 + vaxis.Key.f1, 122 + vaxis.Key.f2, 123 + vaxis.Key.f3, 124 + vaxis.Key.f4, 125 + => try writer.print("\x1bO{c}", .{def.suffix}), 126 + else => try writer.print("\x1b[{c}", .{def.suffix}), 127 + } 128 + else 129 + try writer.print("\x1b[{d}{c}", .{ def.number, def.suffix }); 130 + }, 131 + else => try writer.print("\x1b[{d};{d}{c}", .{ def.number, effective_mods + 1, def.suffix }), 132 + } 133 + } 134 + 135 + const Definition = struct { 136 + number: u21, 137 + suffix: u8, 138 + }; 139 + 140 + const escape: Definition = .{ .number = 27, .suffix = 'u' }; 141 + const enter: Definition = .{ .number = 13, .suffix = 'u' }; 142 + const tab: Definition = .{ .number = 9, .suffix = 'u' }; 143 + const backspace: Definition = .{ .number = 127, .suffix = 'u' }; 144 + const insert: Definition = .{ .number = 2, .suffix = '~' }; 145 + const delete: Definition = .{ .number = 3, .suffix = '~' }; 146 + const left: Definition = .{ .number = 1, .suffix = 'D' }; 147 + const right: Definition = .{ .number = 1, .suffix = 'C' }; 148 + const up: Definition = .{ .number = 1, .suffix = 'A' }; 149 + const down: Definition = .{ .number = 1, .suffix = 'B' }; 150 + const page_up: Definition = .{ .number = 5, .suffix = '~' }; 151 + const page_down: Definition = .{ .number = 6, .suffix = '~' }; 152 + const home: Definition = .{ .number = 1, .suffix = 'H' }; 153 + const end: Definition = .{ .number = 1, .suffix = 'F' }; 154 + const caps_lock: Definition = .{ .number = 57358, .suffix = 'u' }; 155 + const scroll_lock: Definition = .{ .number = 57359, .suffix = 'u' }; 156 + const num_lock: Definition = .{ .number = 57360, .suffix = 'u' }; 157 + const print_screen: Definition = .{ .number = 57361, .suffix = 'u' }; 158 + const pause: Definition = .{ .number = 57362, .suffix = 'u' }; 159 + const menu: Definition = .{ .number = 57363, .suffix = 'u' }; 160 + const f1: Definition = .{ .number = 1, .suffix = 'P' }; 161 + const f2: Definition = .{ .number = 1, .suffix = 'Q' }; 162 + const f3: Definition = .{ .number = 13, .suffix = '~' }; 163 + const f3_legacy: Definition = .{ .number = 1, .suffix = 'R' }; 164 + const f4: Definition = .{ .number = 1, .suffix = 'S' }; 165 + const f5: Definition = .{ .number = 15, .suffix = '~' }; 166 + const f6: Definition = .{ .number = 17, .suffix = '~' }; 167 + const f7: Definition = .{ .number = 18, .suffix = '~' }; 168 + const f8: Definition = .{ .number = 19, .suffix = '~' }; 169 + const f9: Definition = .{ .number = 20, .suffix = '~' }; 170 + const f10: Definition = .{ .number = 21, .suffix = '~' }; 171 + const f11: Definition = .{ .number = 23, .suffix = '~' }; 172 + const f12: Definition = .{ .number = 24, .suffix = '~' };