diff --git a/src/renderer/vaxis/renderer.zig b/src/renderer/vaxis/renderer.zig index f72bf3b..7490148 100644 --- a/src/renderer/vaxis/renderer.zig +++ b/src/renderer/vaxis/renderer.zig @@ -25,6 +25,7 @@ vx: vaxis.Vaxis, no_alternate: bool, event_buffer: std.ArrayList(u8), +input_buffer: std.ArrayList(u8), bracketed_paste: bool = false, bracketed_paste_buffer: std.ArrayList(u8), @@ -54,6 +55,7 @@ pub fn init(a: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool) ! .vx = try vaxis.init(a, opts), .no_alternate = no_alternate, .event_buffer = std.ArrayList(u8).init(a), + .input_buffer = std.ArrayList(u8).init(a), .bracketed_paste_buffer = std.ArrayList(u8).init(a), .handler_ctx = handler_ctx, .logger = log.logger(log_name), @@ -63,6 +65,7 @@ pub fn init(a: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool) ! pub fn deinit(self: *Self) void { self.vx.deinit(self.a); self.bracketed_paste_buffer.deinit(); + self.input_buffer.deinit(); self.event_buffer.deinit(); } @@ -70,7 +73,7 @@ pub fn run(self: *Self) !void { if (self.vx.tty == null) self.vx.tty = try vaxis.Tty.init(); if (!self.no_alternate) try self.vx.enterAltScreen(); try self.vx.queryTerminal(); - const ws = try vaxis.Tty.getWinsize(self.input_fd()); + const ws = try vaxis.Tty.getWinsize(self.input_fd_blocking()); try self.vx.resize(self.a, ws); self.vx.queueRefresh(); try self.vx.setMouseMode(true); @@ -82,7 +85,7 @@ pub fn render(self: *Self) !void { } pub fn refresh(self: *Self) !void { - const ws = try vaxis.Tty.getWinsize(self.input_fd()); + const ws = try vaxis.Tty.getWinsize(self.input_fd_blocking()); try self.vx.resize(self.a, ws); self.vx.queueRefresh(); } @@ -102,7 +105,7 @@ pub fn stdplane(self: *Self) Plane { return plane; } -pub fn input_fd(self: Self) i32 { +pub fn input_fd_blocking(self: Self) i32 { return self.vx.tty.?.fd; } @@ -110,19 +113,27 @@ pub fn leave_alternate_screen(self: *Self) void { self.vx.exitAltScreen() catch {}; } -pub fn process_input(self: *Self) !void { +pub fn process_input(self: *Self, input_: []const u8) !void { var parser: vaxis.Parser = .{ .grapheme_data = &self.vx.screen.unicode.grapheme_data, }; - var buf: [1024]u8 = undefined; - var start: usize = 0; - const n = std.posix.read(self.input_fd(), &buf) catch |e| switch (e) { - error.WouldBlock => return, - else => return e, - }; - while (start < n) { - const result = try parser.parse(buf[start..n]); - start += result.n; + try self.input_buffer.appendSlice(input_); + var buf = self.input_buffer.items; + defer { + if (buf.len == 0) { + self.input_buffer.clearRetainingCapacity(); + } else { + const rest = self.a.alloc(u8, buf.len) catch |e| std.debug.panic("{any}", .{e}); + @memcpy(rest, buf); + self.input_buffer.deinit(); + self.input_buffer = std.ArrayList(u8).fromOwnedSlice(self.a, rest); + } + } + while (buf.len > 0) { + const result = try parser.parse(buf); + if (result.n == 0) + return; + buf = buf[result.n..]; const event = result.event orelse continue; switch (event) { .key_press => |key_| { @@ -176,16 +187,17 @@ pub fn process_input(self: *Self) !void { 0, 0, })), - .drag => f(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), try self.fmtmsg(.{ - "D", - event_type.PRESS, - @intFromEnum(mouse.button), - input.utils.button_id_string(@intFromEnum(mouse.button)), - mouse.col, - mouse.row, - 0, - 0, - })), + .drag => if (self.dispatch_mouse_drag) |f_| + f_(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), true, try self.fmtmsg(.{ + "D", + event_type.PRESS, + @intFromEnum(mouse.button), + input.utils.button_id_string(@intFromEnum(mouse.button)), + mouse.col, + mouse.row, + 0, + 0, + })), }; }, .focus_in => { diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 8d51dd7..b245220 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -51,6 +51,7 @@ init_timer: ?tp.timeout, sigwinch_signal: ?tp.signal = null, no_sleep: bool = false, final_exit: []const u8 = "normal", +input_reader: ?*InputReader = null, const idle_frames = 1; @@ -115,12 +116,16 @@ fn init(a: Allocator) !*Self { defer instance_ = null; try self.rdr.run(); - self.fd_stdin = try tp.file_descriptor.init("stdin", self.rdr.input_fd()); - // self.fd_stdin = try tp.file_descriptor.init("stdin", std.os.STDIN_FILENO); + if (comptime @hasDecl(renderer, "input_fd")) + self.fd_stdin = try tp.file_descriptor.init("stdin", self.rdr.input_fd()); const n = self.rdr.stdplane(); try frame_clock.start(); - try self.fd_stdin.wait_read(); + if (comptime @hasDecl(renderer, "input_fd")) { + try self.fd_stdin.wait_read(); + } else { + self.input_reader = try InputReader.create(a, self.rdr.input_fd_blocking()); + } self.rdr.handler_ctx = self; self.rdr.dispatch_input = dispatch_input; @@ -156,8 +161,10 @@ fn deinit(self: *Self) void { self.frame_clock.deinit(); self.rdr.stop(); self.rdr.deinit(); - self.fd_stdin.deinit(); + if (comptime @hasDecl(renderer, "input_fd")) + self.fd_stdin.deinit(); self.logger.deinit(); + if (self.input_reader) |p| p.stop(); self.a.destroy(self); } @@ -172,7 +179,9 @@ fn receive(self: *Self, from: tp.pid_ref, m: tp.message) tp.result { instance_ = self; defer instance_ = null; errdefer self.deinit(); - errdefer self.fd_stdin.cancel() catch {}; + + errdefer if (comptime @hasDecl(renderer, "input_fd")) + self.fd_stdin.cancel() catch {}; errdefer self.rdr.leave_alternate_screen(); self.receive_safe(from, m) catch |e| { if (std.mem.eql(u8, "normal", tp.error_text())) @@ -227,11 +236,22 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) tp.result { return; } - if (self.dispatch_input_fd(m) catch |e| b: { - self.logger.err("input dispatch", e); - break :b true; - }) - return; + if (comptime @hasDecl(renderer, "input_fd")) { + if (self.dispatch_input_fd(m) catch |e| b: { + self.logger.err("input dispatch", e); + break :b true; + }) + return; + } else { + var input: []const u8 = undefined; + if (try m.match(.{ "process_input", tp.extract(&input) })) { + self.rdr.process_input(input) catch |e| return tp.exit_error(e); + try self.dispatch_flush_input_event(); + if (self.unrendered_input_events_count > 0 and !self.frame_clock_running) + need_render(); + return; + } + } if (try m.match(.{"render"})) { if (!self.frame_clock_running) @@ -789,3 +809,44 @@ pub const fallbacks: []const FallBack = &[_]FallBack{ .{ .ts = "repeat", .tm = "keyword.control.flow" }, .{ .ts = "field", .tm = "variable" }, }; + +const InputReader = struct { + a: std.mem.Allocator, + fd: std.posix.fd_t, + pid: tp.pid, + id: ?std.Thread.Id = null, + + fn create(a: std.mem.Allocator, fd: std.posix.fd_t) error{Exit}!*InputReader { + const self = a.create(InputReader) catch |e| return tp.exit_error(e); + self.* = .{ + .a = a, + .fd = fd, + .pid = tp.self_pid().clone(), + }; + const pid = tp.spawn_link(self.a, self, InputReader.start, "tui.InputReader") catch |e| return tp.exit_error(e); + pid.deinit(); + return self; + } + + fn deinit(self: *InputReader) void { + self.pid.deinit(); + self.a.destroy(self); + } + + fn start(self: *InputReader) tp.result { + defer self.deinit(); + self.id = std.Thread.getCurrentId(); + var buf: [4096]u8 = undefined; + while (true) { + const n = std.posix.read(self.fd, &buf) catch |e| return tp.exit_error(e); + if (n == 0) + return tp.exit_normal(); + try self.pid.send(.{ "process_input", buf[0..n] }); + } + } + + fn stop(self: *InputReader) void { + if (self.id) |id| + _ = std.os.linux.tkill(@intCast(id), std.os.linux.SIG.INT); + } +};