···33const os = std.os;
44const vaxis = @import("main.zig");
55const Vaxis = vaxis.Vaxis;
66+const Event = @import("event.zig").Event;
77+const parser = @import("parser.zig");
68const Key = vaxis.Key;
79810const log = std.log.scoped(.tty);
···110112 };
111113 try WinchHandler.init(vx, self.fd);
112114113113- // the state of the parser
114114- const State = enum {
115115- ground,
116116- escape,
117117- csi,
118118- osc,
119119- dcs,
120120- sos,
121121- pm,
122122- apc,
123123- ss2,
124124- ss3,
125125- };
126126-127127- var state: State = .ground;
128128-129129- // an intermediate data structure to hold sequence data while we are
130130- // scanning more bytes. This is tailored for input parsing only
131131- const Sequence = struct {
132132- // private indicators are 0x3C-0x3F
133133- private_indicator: ?u8 = null,
134134- // we won't be handling any sequences with more than one intermediate
135135- intermediate: ?u8 = null,
136136- // we should absolutely never have more then 16 params
137137- params: [16]u16 = undefined,
138138- param_idx: usize = 0,
139139- param_buf: [8]u8 = undefined,
140140- param_buf_idx: usize = 0,
141141- sub_state: std.StaticBitSet(16) = std.StaticBitSet(16).initEmpty(),
142142- empty_state: std.StaticBitSet(16) = std.StaticBitSet(16).initEmpty(),
143143- };
144144-145145- var seq: Sequence = .{};
146146-147115 // Set up fds for polling
148116 var pollfds: [2]std.os.pollfd = .{
149117 .{ .fd = self.fd, .events = std.os.POLL.IN, .revents = undefined },
···161129 }
162130163131 const n = try os.read(self.fd, &buf);
164164- var i: usize = 0;
165132 var start: usize = 0;
166166- // parse the read into events. This parser is bespoke for input parsing
167167- // and is not suitable for reuse as a generic vt parser
168168- while (i < n) : (i += 1) {
169169- const b = buf[i];
170170- switch (state) {
171171- .ground => {
172172- // ground state generates keypresses when parsing input. We
173173- // generally get ascii characters, but anything less than
174174- // 0x20 is a Ctrl+<c> keypress. We map these to lowercase
175175- // ascii characters when we can
176176- const key: Key = switch (b) {
177177- 0x00 => .{ .codepoint = '@', .mods = .{ .ctrl = true } },
178178- 0x01...0x1A => .{ .codepoint = b + 0x60, .mods = .{ .ctrl = true } },
179179- 0x1B => escape: {
180180- // NOTE: This could be an errant escape at the end
181181- // of a large read. That is _incredibly_ unlikely
182182- // given the size of read inputs and our read buffer
183183- if (i == (n - 1)) {
184184- const event = Key{
185185- .codepoint = Key.escape,
186186- };
187187- break :escape event;
188188- }
189189- state = .escape;
190190- continue;
191191- },
192192- 0x20...0x7E => .{ .codepoint = b },
193193- 0x7F => .{ .codepoint = Key.backspace },
194194- // TODO: graphemes
195195- else => .{ .codepoint = b },
196196- };
197197- if (@hasField(EventType, "key_press")) {
198198- vx.postEvent(.{ .key_press = key });
199199- }
200200- },
201201- .escape => {
202202- seq = .{};
203203- start = i;
204204- switch (b) {
205205- 0x4F => state = .ss3,
206206- 0x50 => state = .dcs,
207207- 0x58 => state = .sos,
208208- 0x5B => state = .csi,
209209- 0x5D => state = .osc,
210210- 0x5E => state = .pm,
211211- 0x5F => state = .apc,
212212- else => {
213213- // Anything else is an "alt + <b>" keypress
214214- if (@hasField(EventType, "key_press")) {
215215- vx.postEvent(.{
216216- .key_press = .{
217217- .codepoint = b,
218218- .mods = .{ .alt = true },
219219- },
220220- });
221221- }
222222- state = .ground;
223223- },
224224- }
225225- },
226226- .ss3 => {
227227- state = .ground;
228228- const key: Key = switch (b) {
229229- 'A' => .{ .codepoint = Key.up },
230230- 'B' => .{ .codepoint = Key.down },
231231- 'C' => .{ .codepoint = Key.right },
232232- 'D' => .{ .codepoint = Key.left },
233233- 'F' => .{ .codepoint = Key.end },
234234- 'H' => .{ .codepoint = Key.home },
235235- 'P' => .{ .codepoint = Key.f1 },
236236- 'Q' => .{ .codepoint = Key.f2 },
237237- 'R' => .{ .codepoint = Key.f3 },
238238- 'S' => .{ .codepoint = Key.f4 },
239239- else => {
240240- log.warn("unhandled ss3: {x}", .{b});
241241- continue;
242242- },
243243- };
244244-133133+ while (start < n) {
134134+ const result = try parser.parse(buf[start..n]);
135135+ start = result.n;
136136+ log.debug("something {}", .{result});
137137+ const event = result.event orelse continue;
138138+ switch (event) {
139139+ .key_press => |key| {
245140 if (@hasField(EventType, "key_press")) {
246141 vx.postEvent(.{ .key_press = key });
247142 }
248143 },
249249- .csi => {
250250- switch (b) {
251251- // c0 controls. we ignore these even though we should
252252- // "execute" them. This isn't seen in practice
253253- 0x00...0x1F => {},
254254- // intermediates. we only handle one. technically there
255255- // can be more
256256- 0x20...0x2F => seq.intermediate = b,
257257- 0x30...0x39 => {
258258- seq.param_buf[seq.param_buf_idx] = b;
259259- seq.param_buf_idx += 1;
260260- },
261261- // private indicators. These come before any params ('?')
262262- 0x3C...0x3F => seq.private_indicator = b,
263263- ';' => {
264264- if (seq.param_buf_idx == 0) {
265265- // empty param. default it to 0 and set the
266266- // empty state
267267- seq.params[seq.param_idx] = 0;
268268- seq.empty_state.set(seq.param_idx);
269269- seq.param_idx += 1;
270270- } else {
271271- const p = try std.fmt.parseUnsigned(u16, seq.param_buf[0..seq.param_buf_idx], 10);
272272- seq.param_buf_idx = 0;
273273- seq.params[seq.param_idx] = p;
274274- seq.param_idx += 1;
275275- }
276276- },
277277- ':' => {
278278- if (seq.param_buf_idx == 0) {
279279- // empty param. default it to 0 and set the
280280- // empty state
281281- seq.params[seq.param_idx] = 0;
282282- seq.empty_state.set(seq.param_idx);
283283- seq.param_idx += 1;
284284- // Set the *next* param as a subparam
285285- seq.sub_state.set(seq.param_idx);
286286- } else {
287287- const p = try std.fmt.parseUnsigned(u16, seq.param_buf[0..seq.param_buf_idx], 10);
288288- seq.param_buf_idx = 0;
289289- seq.params[seq.param_idx] = p;
290290- seq.param_idx += 1;
291291- // Set the *next* param as a subparam
292292- seq.sub_state.set(seq.param_idx);
293293- }
294294- },
295295- 0x40...0xFF => {
296296- if (seq.param_buf_idx > 0) {
297297- const p = try std.fmt.parseUnsigned(u16, seq.param_buf[0..seq.param_buf_idx], 10);
298298- seq.param_buf_idx = 0;
299299- seq.params[seq.param_idx] = p;
300300- seq.param_idx += 1;
301301- }
302302- // dispatch the sequence
303303- state = .ground;
304304- const codepoint: u21 = switch (b) {
305305- 'A' => Key.up,
306306- 'B' => Key.down,
307307- 'C' => Key.right,
308308- 'D' => Key.left,
309309- 'E' => Key.kp_begin,
310310- 'F' => Key.end,
311311- 'H' => Key.home,
312312- 'P' => Key.f1,
313313- 'Q' => Key.f2,
314314- 'R' => Key.f3,
315315- 'S' => Key.f4,
316316- '~' => blk: {
317317- // The first param will define this
318318- // codepoint
319319- if (seq.param_idx < 1) {
320320- log.warn("unhandled csi: CSI {s}", .{buf[start + 1 .. i + 1]});
321321- continue;
322322- }
323323- switch (seq.params[0]) {
324324- 2 => break :blk Key.insert,
325325- 3 => break :blk Key.delete,
326326- 5 => break :blk Key.page_up,
327327- 6 => break :blk Key.page_down,
328328- 7 => break :blk Key.home,
329329- 8 => break :blk Key.end,
330330- 11 => break :blk Key.f1,
331331- 12 => break :blk Key.f2,
332332- 13 => break :blk Key.f3,
333333- 14 => break :blk Key.f4,
334334- 15 => break :blk Key.f5,
335335- 17 => break :blk Key.f6,
336336- 18 => break :blk Key.f7,
337337- 19 => break :blk Key.f8,
338338- 20 => break :blk Key.f9,
339339- 21 => break :blk Key.f10,
340340- 23 => break :blk Key.f11,
341341- 24 => break :blk Key.f12,
342342- 200 => {
343343- // TODO: bracketed paste
344344- continue;
345345- },
346346- 201 => {
347347- // TODO: bracketed paste
348348- continue;
349349- },
350350- 57427 => break :blk Key.kp_begin,
351351- else => {
352352- log.warn("unhandled csi: CSI {s}", .{buf[start + 1 .. i + 1]});
353353- continue;
354354- },
355355- }
356356- },
357357- 'u' => blk: {
358358- if (seq.private_indicator) |_| {
359359- // response to our kitty query
360360- // TODO: kitty query handling
361361- log.warn("unhandled csi: CSI {s}", .{buf[start + 1 .. i + 1]});
362362- continue;
363363- }
364364- if (seq.param_idx == 0) {
365365- log.warn("unhandled csi: CSI {s}", .{buf[start + 1 .. i + 1]});
366366- continue;
367367- }
368368- // In any csi u encoding, the codepoint
369369- // directly maps to our keypoint definitions
370370- break :blk seq.params[0];
371371- },
372372-373373- 'I' => { // focus in
374374- if (@hasField(EventType, "focus_in")) {
375375- vx.postEvent(.focus_in);
376376- }
377377- continue;
378378- },
379379- 'O' => { // focus out
380380- if (@hasField(EventType, "focus_out")) {
381381- vx.postEvent(.focus_out);
382382- }
383383- continue;
384384- },
385385- else => {
386386- log.warn("unhandled csi: CSI {s}", .{buf[start + 1 .. i + 1]});
387387- continue;
388388- },
389389- };
390390-391391- var key: Key = .{ .codepoint = codepoint };
392392-393393- var idx: usize = 0;
394394- var field: u8 = 0;
395395- // parse the parameters
396396- while (idx < seq.param_idx) : (idx += 1) {
397397- switch (field) {
398398- 0 => {
399399- defer field += 1;
400400- // field 0 contains our codepoint. Any
401401- // subparameters shifted key code and
402402- // alternate keycode (csi u encoding)
403403-404404- // We already handled our codepoint so
405405- // we just need to check for subs
406406- if (!seq.sub_state.isSet(idx + 1)) {
407407- continue;
408408- }
409409- idx += 1;
410410- // The first one is a shifted code if it
411411- // isn't empty
412412- if (!seq.empty_state.isSet(idx)) {
413413- key.shifted_codepoint = seq.params[idx];
414414- }
415415- // check the next one for base layout
416416- // code
417417- if (!seq.sub_state.isSet(idx + 1)) {
418418- continue;
419419- }
420420- idx += 1;
421421- key.base_layout_codepoint = seq.params[idx];
422422- },
423423- 1 => {
424424- // field 1 is modifiers and optionally
425425- // the event type (csiu)
426426- const mod_mask: u8 = @truncate(seq.params[idx] - 1);
427427- key.mods = @bitCast(mod_mask);
428428- },
429429- else => {},
430430- }
431431- }
432432- if (@hasField(EventType, "key_press")) {
433433- vx.postEvent(.{ .key_press = key });
434434- }
435435- },
436436- }
437437- },
438438- else => {},
144144+ .focus_in => {},
145145+ .focus_out => {},
439146 }
440147 }
441148 }
+9
src/event.zig
···11+pub const Key = @import("Key.zig");
22+33+/// The events that Vaxis emits. This can be used as the generic EventType if
44+/// there are no internal events
55+pub const Event = union(enum) {
66+ key_press: Key,
77+ focus_in,
88+ focus_out,
99+};