this repo has no description
13
fork

Configure Feed

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

parser: more kitty key parsing

Add some more implementation details to kitty key parsing

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

+77 -25
+77 -25
src/Tty.zig
··· 139 139 param_buf: [8]u8 = undefined, 140 140 param_buf_idx: usize = 0, 141 141 sub_state: std.StaticBitSet(16) = std.StaticBitSet(16).initEmpty(), 142 + empty_state: std.StaticBitSet(16) = std.StaticBitSet(16).initEmpty(), 142 143 }; 143 144 144 145 var seq: Sequence = .{}; ··· 151 152 152 153 // initialize the read buffer 153 154 var buf: [1024]u8 = undefined; 155 + // read loop 154 156 while (true) { 155 157 _ = try std.os.poll(&pollfds, -1); 156 158 if (pollfds[1].revents & std.os.POLL.IN != 0) { ··· 161 163 const n = try os.read(self.fd, &buf); 162 164 var i: usize = 0; 163 165 var start: usize = 0; 166 + // parse the read into events. This parser is bespoke for input parsing 167 + // and is not suitable for reuse as a generic vt parser 164 168 while (i < n) : (i += 1) { 165 169 const b = buf[i]; 166 170 switch (state) { ··· 169 173 // generally get ascii characters, but anything less than 170 174 // 0x20 is a Ctrl+<c> keypress. We map these to lowercase 171 175 // ascii characters when we can 172 - const key: ?Key = switch (b) { 173 - 0x00 => Key{ .codepoint = '@', .mods = .{ .ctrl = true } }, 174 - 0x01...0x1A => Key{ .codepoint = b + 0x60, .mods = .{ .ctrl = true } }, 176 + const key: Key = switch (b) { 177 + 0x00 => .{ .codepoint = '@', .mods = .{ .ctrl = true } }, 178 + 0x01...0x1A => .{ .codepoint = b + 0x60, .mods = .{ .ctrl = true } }, 175 179 0x1B => escape: { 176 180 // NOTE: This could be an errant escape at the end 177 181 // of a large read. That is _incredibly_ unlikely ··· 183 187 break :escape event; 184 188 } 185 189 state = .escape; 186 - break :escape null; 190 + continue; 187 191 }, 188 - 0x20...0x7E => Key{ .codepoint = b }, 189 - 0x7F => Key{ .codepoint = Key.backspace }, 190 - else => Key{ .codepoint = b }, 192 + 0x20...0x7E => .{ .codepoint = b }, 193 + 0x7F => .{ .codepoint = Key.backspace }, 194 + // TODO: graphemes 195 + else => .{ .codepoint = b }, 191 196 }; 192 - if (key) |k| { 193 - if (@hasField(EventType, "key_press")) { 194 - vx.postEvent(.{ .key_press = k }); 195 - } 197 + if (@hasField(EventType, "key_press")) { 198 + vx.postEvent(.{ .key_press = key }); 196 199 } 197 200 }, 198 201 .escape => { ··· 221 224 } 222 225 }, 223 226 .ss3 => { 224 - const key: ?Key = switch (b) { 227 + state = .ground; 228 + const key: Key = switch (b) { 225 229 'A' => .{ .codepoint = Key.up }, 226 230 'B' => .{ .codepoint = Key.down }, 227 231 'C' => .{ .codepoint = Key.right }, ··· 232 236 'Q' => .{ .codepoint = Key.f2 }, 233 237 'R' => .{ .codepoint = Key.f3 }, 234 238 'S' => .{ .codepoint = Key.f4 }, 235 - else => blk: { 239 + else => { 236 240 log.warn("unhandled ss3: {x}", .{b}); 237 - break :blk null; 241 + continue; 238 242 }, 239 243 }; 240 - if (key) |k| { 241 - if (@hasField(EventType, "key_press")) { 242 - vx.postEvent(.{ .key_press = k }); 243 - } 244 + 245 + if (@hasField(EventType, "key_press")) { 246 + vx.postEvent(.{ .key_press = key }); 244 247 } 245 - state = .ground; 246 248 }, 247 249 .csi => { 248 250 switch (b) { ··· 260 262 0x3C...0x3F => seq.private_indicator = b, 261 263 ';' => { 262 264 if (seq.param_buf_idx == 0) { 263 - // empty param. default it to 1 264 - seq.params[seq.param_idx] = 1; 265 + // empty param. default it to 0 and set the 266 + // empty state 267 + seq.params[seq.param_idx] = 0; 268 + seq.empty_state.set(seq.param_idx); 265 269 seq.param_idx += 1; 266 270 } else { 267 271 const p = try std.fmt.parseUnsigned(u16, seq.param_buf[0..seq.param_buf_idx], 10); ··· 272 276 }, 273 277 ':' => { 274 278 if (seq.param_buf_idx == 0) { 275 - // empty param. default it to 1 276 - seq.params[seq.param_idx] = 1; 279 + // empty param. default it to 0 and set the 280 + // empty state 281 + seq.params[seq.param_idx] = 0; 282 + seq.empty_state.set(seq.param_idx); 277 283 seq.param_idx += 1; 278 284 // Set the *next* param as a subparam 279 285 seq.sub_state.set(seq.param_idx); ··· 287 293 } 288 294 }, 289 295 0x40...0xFF => { 290 - // dispatch our sequence 296 + if (seq.param_buf_idx > 0) { 297 + const p = try std.fmt.parseUnsigned(u16, seq.param_buf[0..seq.param_buf_idx], 10); 298 + seq.param_buf_idx = 0; 299 + seq.params[seq.param_idx] = p; 300 + seq.param_idx += 1; 301 + } 302 + // dispatch the sequence 291 303 state = .ground; 292 304 const codepoint: u21 = switch (b) { 293 305 'A' => Key.up, ··· 376 388 }, 377 389 }; 378 390 379 - const key: Key = .{ .codepoint = codepoint }; 391 + var key: Key = .{ .codepoint = codepoint }; 392 + 393 + var idx: usize = 0; 394 + var field: u8 = 0; 395 + // parse the parameters 396 + while (idx < seq.param_idx) : (idx += 1) { 397 + switch (field) { 398 + 0 => { 399 + defer field += 1; 400 + // field 0 contains our codepoint. Any 401 + // subparameters shifted key code and 402 + // alternate keycode (csi u encoding) 403 + 404 + // We already handled our codepoint so 405 + // we just need to check for subs 406 + if (!seq.sub_state.isSet(idx + 1)) { 407 + continue; 408 + } 409 + idx += 1; 410 + // The first one is a shifted code if it 411 + // isn't empty 412 + if (!seq.empty_state.isSet(idx)) { 413 + key.shifted_codepoint = seq.params[idx]; 414 + } 415 + // check the next one for base layout 416 + // code 417 + if (!seq.sub_state.isSet(idx + 1)) { 418 + continue; 419 + } 420 + idx += 1; 421 + key.base_layout_codepoint = seq.params[idx]; 422 + }, 423 + 1 => { 424 + // field 1 is modifiers and optionally 425 + // the event type (csiu) 426 + const mod_mask: u8 = @truncate(seq.params[idx] - 1); 427 + key.mods = @bitCast(mod_mask); 428 + }, 429 + else => {}, 430 + } 431 + } 380 432 if (@hasField(EventType, "key_press")) { 381 433 vx.postEvent(.{ .key_press = key }); 382 434 }