a modern tui library written in zig

fix: port terminal widget to zig-0.15

authored by neurocyte.flow-control.dev and committed by rockorager.dev cb00a914 b919803b

Changed files
+125 -151
examples
src
+5 -5
examples/vt.zig
··· 31 31 try loop.start(); 32 32 defer loop.stop(); 33 33 34 - var buffered = tty.bufferedWriter(); 35 - 36 34 try vx.enterAltScreen(tty.writer()); 37 35 try vx.queryTerminal(tty.writer(), 1 * std.time.ns_per_s); 38 36 var env = try std.process.getEnvMap(alloc); ··· 50 48 }; 51 49 const shell = env.get("SHELL") orelse "bash"; 52 50 const argv = [_][]const u8{shell}; 51 + var write_buf: [4096]u8 = undefined; 53 52 var vt = try vaxis.widgets.Terminal.init( 54 53 alloc, 55 54 &argv, 56 55 &env, 57 56 &vx.unicode, 58 57 vt_opts, 58 + &write_buf, 59 59 ); 60 60 defer vt.deinit(); 61 61 try vt.spawn(); ··· 108 108 .x_pixel = 0, 109 109 .y_pixel = 0, 110 110 }); 111 - try vt.draw(child); 111 + try vt.draw(alloc, child); 112 112 113 - try vx.render(buffered.writer().any()); 114 - try buffered.flush(); 113 + try vx.render(tty.writer()); 114 + try tty.writer().flush(); 115 115 } 116 116 }
+8 -8
src/widgets/terminal/Command.zig
··· 36 36 37 37 // set the controlling terminal 38 38 var u: c_uint = std.posix.STDIN_FILENO; 39 - if (posix.system.ioctl(self.pty.tty, posix.T.IOCSCTTY, @intFromPtr(&u)) != 0) return error.IoctlError; 39 + if (posix.system.ioctl(self.pty.tty.handle, posix.T.IOCSCTTY, @intFromPtr(&u)) != 0) return error.IoctlError; 40 40 41 41 // set up io 42 - try posix.dup2(self.pty.tty, std.posix.STDIN_FILENO); 43 - try posix.dup2(self.pty.tty, std.posix.STDOUT_FILENO); 44 - try posix.dup2(self.pty.tty, std.posix.STDERR_FILENO); 42 + try posix.dup2(self.pty.tty.handle, std.posix.STDIN_FILENO); 43 + try posix.dup2(self.pty.tty.handle, std.posix.STDOUT_FILENO); 44 + try posix.dup2(self.pty.tty.handle, std.posix.STDERR_FILENO); 45 45 46 - posix.close(self.pty.tty); 47 - if (self.pty.pty > 2) posix.close(self.pty.pty); 46 + self.pty.tty.close(); 47 + if (self.pty.pty.handle > 2) self.pty.pty.close(); 48 48 49 49 if (self.working_directory) |wd| { 50 50 try std.posix.chdir(wd); ··· 75 75 return; 76 76 } 77 77 78 - fn handleSigChild(_: c_int) callconv(.C) void { 78 + fn handleSigChild(_: c_int) callconv(.c) void { 79 79 const result = std.posix.waitpid(-1, 0); 80 80 81 81 Terminal.global_vt_mutex.lock(); ··· 107 107 { 108 108 var it = map.iterator(); 109 109 while (it.next()) |pair| { 110 - envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }); 110 + envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0); 111 111 i += 1; 112 112 } 113 113 }
+26 -28
src/widgets/terminal/Parser.zig
··· 2 2 const Parser = @This(); 3 3 4 4 const std = @import("std"); 5 - const Reader = std.io.AnyReader; 5 + const Reader = std.Io.Reader; 6 6 const ansi = @import("ansi.zig"); 7 - const BufferedReader = std.io.BufferedReader(4096, std.io.AnyReader); 8 7 9 8 /// A terminal event 10 9 const Event = union(enum) { ··· 18 17 apc: []const u8, 19 18 }; 20 19 21 - buf: std.ArrayList(u8), 20 + buf: std.array_list.Managed(u8), 22 21 /// a leftover byte from a ground event 23 22 pending_byte: ?u8 = null, 24 23 25 - pub fn parseReader(self: *Parser, buffered: *BufferedReader) !Event { 26 - const reader = buffered.reader().any(); 24 + pub fn parseReader(self: *Parser, reader: *Reader) !Event { 27 25 self.buf.clearRetainingCapacity(); 28 26 while (true) { 29 - const b = if (self.pending_byte) |p| p else try reader.readByte(); 27 + const b = if (self.pending_byte) |p| p else try reader.takeByte(); 30 28 self.pending_byte = null; 31 29 switch (b) { 32 30 // Escape sequence 33 31 0x1b => { 34 - const next = try reader.readByte(); 32 + const next = try reader.takeByte(); 35 33 switch (next) { 36 - 0x4E => return .{ .ss2 = try reader.readByte() }, 37 - 0x4F => return .{ .ss3 = try reader.readByte() }, 34 + 0x4E => return .{ .ss2 = try reader.takeByte() }, 35 + 0x4F => return .{ .ss3 = try reader.takeByte() }, 38 36 0x50 => try skipUntilST(reader), // DCS 39 37 0x58 => try skipUntilST(reader), // SOS 40 38 0x5B => return self.parseCsi(reader), // CSI ··· 58 56 => return .{ .c0 = @enumFromInt(b) }, 59 57 else => { 60 58 try self.buf.append(b); 61 - return self.parseGround(buffered); 59 + return self.parseGround(reader); 62 60 }, 63 61 } 64 62 } 65 63 } 66 64 67 - inline fn parseGround(self: *Parser, reader: *BufferedReader) !Event { 65 + inline fn parseGround(self: *Parser, reader: *Reader) !Event { 68 66 var buf: [1]u8 = undefined; 69 67 { 70 68 std.debug.assert(self.buf.items.len > 0); ··· 72 70 const len = try std.unicode.utf8ByteSequenceLength(self.buf.items[0]); 73 71 var i: usize = 1; 74 72 while (i < len) : (i += 1) { 75 - const read = try reader.read(&buf); 73 + const read = try reader.readSliceShort(&buf); 76 74 if (read == 0) return error.EOF; 77 75 try self.buf.append(buf[0]); 78 76 } 79 77 } 80 78 while (true) { 81 - if (reader.start == reader.end) return .{ .print = self.buf.items }; 82 - const n = try reader.read(&buf); 79 + if (reader.bufferedLen() == 0) return .{ .print = self.buf.items }; 80 + const n = try reader.readSliceShort(&buf); 83 81 if (n == 0) return error.EOF; 84 82 const b = buf[0]; 85 83 switch (b) { ··· 92 90 const len = try std.unicode.utf8ByteSequenceLength(b); 93 91 var i: usize = 1; 94 92 while (i < len) : (i += 1) { 95 - const read = try reader.read(&buf); 93 + const read = try reader.readSliceShort(&buf); 96 94 if (read == 0) return error.EOF; 97 95 98 96 try self.buf.append(buf[0]); ··· 103 101 } 104 102 105 103 /// parse until b >= 0x30 106 - inline fn parseEscape(self: *Parser, reader: Reader) !Event { 104 + inline fn parseEscape(self: *Parser, reader: *Reader) !Event { 107 105 while (true) { 108 - const b = try reader.readByte(); 106 + const b = try reader.takeByte(); 109 107 switch (b) { 110 108 0x20...0x2F => continue, 111 109 else => { ··· 116 114 } 117 115 } 118 116 119 - inline fn parseApc(self: *Parser, reader: Reader) !Event { 117 + inline fn parseApc(self: *Parser, reader: *Reader) !Event { 120 118 while (true) { 121 - const b = try reader.readByte(); 119 + const b = try reader.takeByte(); 122 120 switch (b) { 123 121 0x00...0x17, 124 122 0x19, 125 123 0x1c...0x1f, 126 124 => continue, 127 125 0x1b => { 128 - try reader.skipBytes(1, .{ .buf_size = 1 }); 126 + _ = try reader.discard(std.Io.Limit.limited(1)); 129 127 return .{ .apc = self.buf.items }; 130 128 }, 131 129 else => try self.buf.append(b), ··· 134 132 } 135 133 136 134 /// Skips sequences until we see an ST (String Terminator, ESC \) 137 - inline fn skipUntilST(reader: Reader) !void { 138 - try reader.skipUntilDelimiterOrEof('\x1b'); 139 - try reader.skipBytes(1, .{ .buf_size = 1 }); 135 + inline fn skipUntilST(reader: *Reader) !void { 136 + _ = try reader.discardDelimiterExclusive('\x1b'); 137 + _ = try reader.discard(std.Io.Limit.limited(1)); 140 138 } 141 139 142 140 /// Parses an OSC sequence 143 - inline fn parseOsc(self: *Parser, reader: Reader) !Event { 141 + inline fn parseOsc(self: *Parser, reader: *Reader) !Event { 144 142 while (true) { 145 - const b = try reader.readByte(); 143 + const b = try reader.takeByte(); 146 144 switch (b) { 147 145 0x00...0x06, 148 146 0x08...0x17, ··· 150 148 0x1c...0x1f, 151 149 => continue, 152 150 0x1b => { 153 - try reader.skipBytes(1, .{ .buf_size = 1 }); 151 + _ = try reader.discard(std.Io.Limit.limited(1)); 154 152 return .{ .osc = self.buf.items }; 155 153 }, 156 154 0x07 => return .{ .osc = self.buf.items }, ··· 159 157 } 160 158 } 161 159 162 - inline fn parseCsi(self: *Parser, reader: Reader) !Event { 160 + inline fn parseCsi(self: *Parser, reader: *Reader) !Event { 163 161 var intermediate: ?u8 = null; 164 162 var pm: ?u8 = null; 165 163 166 164 while (true) { 167 - const b = try reader.readByte(); 165 + const b = try reader.takeByte(); 168 166 switch (b) { 169 167 0x20...0x2F => intermediate = b, 170 168 0x30...0x3B => try self.buf.append(b),
+7 -7
src/widgets/terminal/Pty.zig
··· 7 7 8 8 const posix = std.posix; 9 9 10 - pty: posix.fd_t, 11 - tty: posix.fd_t, 10 + pty: std.fs.File, 11 + tty: std.fs.File, 12 12 13 13 /// opens a new tty/pty pair 14 14 pub fn init() !Pty { ··· 20 20 21 21 /// closes the tty and pty 22 22 pub fn deinit(self: Pty) void { 23 - posix.close(self.pty); 24 - posix.close(self.tty); 23 + self.pty.close(); 24 + self.tty.close(); 25 25 } 26 26 27 27 /// sets the size of the pty ··· 32 32 .xpixel = @truncate(ws.x_pixel), 33 33 .ypixel = @truncate(ws.y_pixel), 34 34 }; 35 - if (posix.system.ioctl(self.pty, posix.T.IOCSWINSZ, @intFromPtr(&_ws)) != 0) 35 + if (posix.system.ioctl(self.pty.handle, posix.T.IOCSWINSZ, @intFromPtr(&_ws)) != 0) 36 36 return error.SetWinsizeError; 37 37 } 38 38 ··· 53 53 const t = try posix.open(sname, .{ .ACCMODE = .RDWR, .NOCTTY = true }, 0); 54 54 55 55 return .{ 56 - .pty = p, 57 - .tty = t, 56 + .pty = .{ .handle = p }, 57 + .tty = .{ .handle = t }, 58 58 }; 59 59 }
+34 -33
src/widgets/terminal/Screen.zig
··· 9 9 const Screen = @This(); 10 10 11 11 pub const Cell = struct { 12 - char: std.ArrayList(u8) = undefined, 12 + char: std.ArrayList(u8) = .empty, 13 13 style: vaxis.Style = .{}, 14 - uri: std.ArrayList(u8) = undefined, 15 - uri_id: std.ArrayList(u8) = undefined, 14 + uri: std.ArrayList(u8) = .empty, 15 + uri_id: std.ArrayList(u8) = .empty, 16 16 width: u8 = 1, 17 17 18 18 wrapped: bool = false, 19 19 dirty: bool = true, 20 20 21 - pub fn erase(self: *Cell, bg: vaxis.Color) void { 21 + pub fn erase(self: *Cell, allocator: std.mem.Allocator, bg: vaxis.Color) void { 22 22 self.char.clearRetainingCapacity(); 23 - self.char.append(' ') catch unreachable; // we never completely free this list 23 + self.char.append(allocator, ' ') catch unreachable; // we never completely free this list 24 24 self.style = .{}; 25 25 self.style.bg = bg; 26 26 self.uri.clearRetainingCapacity(); ··· 30 30 self.dirty = true; 31 31 } 32 32 33 - pub fn copyFrom(self: *Cell, src: Cell) !void { 33 + pub fn copyFrom(self: *Cell, allocator: std.mem.Allocator, src: Cell) !void { 34 34 self.char.clearRetainingCapacity(); 35 - try self.char.appendSlice(src.char.items); 35 + try self.char.appendSlice(allocator, src.char.items); 36 36 self.style = src.style; 37 37 self.uri.clearRetainingCapacity(); 38 - try self.uri.appendSlice(src.uri.items); 38 + try self.uri.appendSlice(allocator, src.uri.items); 39 39 self.uri_id.clearRetainingCapacity(); 40 - try self.uri_id.appendSlice(src.uri_id.items); 40 + try self.uri_id.appendSlice(allocator, src.uri_id.items); 41 41 self.width = src.width; 42 42 self.wrapped = src.wrapped; 43 43 ··· 81 81 } 82 82 }; 83 83 84 + allocator: std.mem.Allocator, 85 + 84 86 width: u16 = 0, 85 87 height: u16 = 0, 86 88 ··· 95 97 /// sets each cell to the default cell 96 98 pub fn init(alloc: std.mem.Allocator, w: u16, h: u16) !Screen { 97 99 var screen = Screen{ 100 + .allocator = alloc, 98 101 .buf = try alloc.alloc(Cell, @as(usize, @intCast(w)) * h), 99 102 .scrolling_region = .{ 100 103 .top = 0, ··· 107 110 }; 108 111 for (screen.buf, 0..) |_, i| { 109 112 screen.buf[i] = .{ 110 - .char = try std.ArrayList(u8).initCapacity(alloc, 1), 111 - .uri = std.ArrayList(u8).init(alloc), 112 - .uri_id = std.ArrayList(u8).init(alloc), 113 + .char = try .initCapacity(alloc, 1), 113 114 }; 114 - try screen.buf[i].char.append(' '); 115 + try screen.buf[i].char.append(alloc, ' '); 115 116 } 116 117 return screen; 117 118 } 118 119 119 120 pub fn deinit(self: *Screen, alloc: std.mem.Allocator) void { 120 121 for (self.buf, 0..) |_, i| { 121 - self.buf[i].char.deinit(); 122 - self.buf[i].uri.deinit(); 123 - self.buf[i].uri_id.deinit(); 122 + self.buf[i].char.deinit(alloc); 123 + self.buf[i].uri.deinit(alloc); 124 + self.buf[i].uri_id.deinit(alloc); 124 125 } 125 126 126 127 alloc.free(self.buf); 127 128 } 128 129 129 130 /// copies the visible area to the destination screen 130 - pub fn copyTo(self: *Screen, dst: *Screen) !void { 131 + pub fn copyTo(self: *Screen, allocator: std.mem.Allocator, dst: *Screen) !void { 131 132 dst.cursor = self.cursor; 132 133 for (self.buf, 0..) |cell, i| { 133 134 if (!cell.dirty) continue; 134 135 self.buf[i].dirty = false; 135 136 const grapheme = cell.char.items; 136 137 dst.buf[i].char.clearRetainingCapacity(); 137 - try dst.buf[i].char.appendSlice(grapheme); 138 + try dst.buf[i].char.appendSlice(allocator, grapheme); 138 139 dst.buf[i].width = cell.width; 139 140 dst.buf[i].style = cell.style; 140 141 } ··· 182 183 const i = (row * self.width) + col; 183 184 assert(i < self.buf.len); 184 185 self.buf[i].char.clearRetainingCapacity(); 185 - self.buf[i].char.appendSlice(grapheme) catch { 186 + self.buf[i].char.appendSlice(self.allocator, grapheme) catch { 186 187 log.warn("couldn't write grapheme", .{}); 187 188 }; 188 189 self.buf[i].uri.clearRetainingCapacity(); 189 - self.buf[i].uri.appendSlice(self.cursor.uri.items) catch { 190 + self.buf[i].uri.appendSlice(self.allocator, self.cursor.uri.items) catch { 190 191 log.warn("couldn't write uri", .{}); 191 192 }; 192 193 self.buf[i].uri_id.clearRetainingCapacity(); 193 - self.buf[i].uri_id.appendSlice(self.cursor.uri_id.items) catch { 194 + self.buf[i].uri_id.appendSlice(self.allocator, self.cursor.uri_id.items) catch { 194 195 log.warn("couldn't write uri_id", .{}); 195 196 }; 196 197 self.buf[i].style = self.cursor.style; ··· 368 369 const end = (self.cursor.row * self.width) + (self.width); 369 370 var i = (self.cursor.row * self.width) + self.cursor.col; 370 371 while (i < end) : (i += 1) { 371 - self.buf[i].erase(self.cursor.style.bg); 372 + self.buf[i].erase(self.allocator, self.cursor.style.bg); 372 373 } 373 374 } 374 375 ··· 378 379 const end = start + self.cursor.col + 1; 379 380 var i = start; 380 381 while (i < end) : (i += 1) { 381 - self.buf[i].erase(self.cursor.style.bg); 382 + self.buf[i].erase(self.allocator, self.cursor.style.bg); 382 383 } 383 384 } 384 385 ··· 388 389 const end = start + self.width; 389 390 var i = start; 390 391 while (i < end) : (i += 1) { 391 - self.buf[i].erase(self.cursor.style.bg); 392 + self.buf[i].erase(self.allocator, self.cursor.style.bg); 392 393 } 393 394 } 394 395 ··· 411 412 while (col <= self.scrolling_region.right) : (col += 1) { 412 413 const i = (row * self.width) + col; 413 414 if (row + cnt > self.scrolling_region.bottom) 414 - self.buf[i].erase(self.cursor.style.bg) 415 + self.buf[i].erase(self.allocator, self.cursor.style.bg) 415 416 else 416 - try self.buf[i].copyFrom(self.buf[i + stride]); 417 + try self.buf[i].copyFrom(self.allocator, self.buf[i + stride]); 417 418 } 418 419 } 419 420 } ··· 434 435 var col: usize = self.scrolling_region.left; 435 436 while (col <= self.scrolling_region.right) : (col += 1) { 436 437 const i = (row * self.width) + col; 437 - try self.buf[i].copyFrom(self.buf[i - stride]); 438 + try self.buf[i].copyFrom(self.allocator, self.buf[i - stride]); 438 439 } 439 440 } 440 441 ··· 443 444 var col: usize = self.scrolling_region.left; 444 445 while (col <= self.scrolling_region.right) : (col += 1) { 445 446 const i = (row * self.width) + col; 446 - self.buf[i].erase(self.cursor.style.bg); 447 + self.buf[i].erase(self.allocator, self.cursor.style.bg); 447 448 } 448 449 } 449 450 } ··· 454 455 const start = (self.cursor.row * self.width) + (self.width); 455 456 var i = start; 456 457 while (i < self.buf.len) : (i += 1) { 457 - self.buf[i].erase(self.cursor.style.bg); 458 + self.buf[i].erase(self.allocator, self.cursor.style.bg); 458 459 } 459 460 } 460 461 ··· 465 466 const end = self.cursor.row * self.width; 466 467 var i = start; 467 468 while (i < end) : (i += 1) { 468 - self.buf[i].erase(self.cursor.style.bg); 469 + self.buf[i].erase(self.allocator, self.cursor.style.bg); 469 470 } 470 471 } 471 472 472 473 pub fn eraseAll(self: *Screen) void { 473 474 var i: usize = 0; 474 475 while (i < self.buf.len) : (i += 1) { 475 - self.buf[i].erase(self.cursor.style.bg); 476 + self.buf[i].erase(self.allocator, self.cursor.style.bg); 476 477 } 477 478 } 478 479 ··· 483 484 var col = self.cursor.col; 484 485 while (col <= self.scrolling_region.right) : (col += 1) { 485 486 if (col + n <= self.scrolling_region.right) 486 - try self.buf[col].copyFrom(self.buf[col + n]) 487 + try self.buf[col].copyFrom(self.allocator, self.buf[col + n]) 487 488 else 488 - self.buf[col].erase(self.cursor.style.bg); 489 + self.buf[col].erase(self.allocator, self.cursor.style.bg); 489 490 } 490 491 } 491 492
+38 -56
src/widgets/terminal/Terminal.zig
··· 53 53 scrollback_size: u16, 54 54 55 55 pty: Pty, 56 + pty_writer: std.fs.File.Writer, 56 57 cmd: Command, 57 58 thread: ?std.Thread = null, 58 59 ··· 76 77 mode: Mode = .{}, 77 78 78 79 tab_stops: std.ArrayList(u16), 79 - title: std.ArrayList(u8), 80 - working_directory: std.ArrayList(u8), 80 + title: std.ArrayList(u8) = .empty, 81 + working_directory: std.ArrayList(u8) = .empty, 81 82 82 83 last_printed: []const u8 = "", 83 84 ··· 91 92 env: *const std.process.EnvMap, 92 93 unicode: *const vaxis.Unicode, 93 94 opts: Options, 95 + write_buf: []u8, 94 96 ) !Terminal { 95 97 // Verify we have an absolute path 96 98 if (opts.initial_working_directory) |pwd| { ··· 104 106 .pty = pty, 105 107 .working_directory = opts.initial_working_directory, 106 108 }; 107 - var tabs = try std.ArrayList(u16).initCapacity(allocator, opts.winsize.cols / 8); 109 + var tabs: std.ArrayList(u16) = try .initCapacity(allocator, opts.winsize.cols / 8); 108 110 var col: u16 = 0; 109 111 while (col < opts.winsize.cols) : (col += 8) { 110 - try tabs.append(col); 112 + try tabs.append(allocator, col); 111 113 } 112 114 return .{ 113 115 .allocator = allocator, 114 116 .pty = pty, 117 + .pty_writer = pty.pty.writerStreaming(write_buf), 115 118 .cmd = cmd, 116 119 .scrollback_size = opts.scrollback_size, 117 120 .front_screen = try Screen.init(allocator, opts.winsize.cols, opts.winsize.rows), ··· 119 122 .back_screen_alt = try Screen.init(allocator, opts.winsize.cols, opts.winsize.rows), 120 123 .unicode = unicode, 121 124 .tab_stops = tabs, 122 - .title = std.ArrayList(u8).init(allocator), 123 - .working_directory = std.ArrayList(u8).init(allocator), 124 125 }; 125 126 } 126 127 ··· 143 144 if (self.thread) |thread| { 144 145 // write an EOT into the tty to trigger a read on our thread 145 146 const EOT = "\x04"; 146 - _ = std.posix.write(self.pty.tty, EOT) catch {}; 147 + _ = self.pty.tty.write(EOT) catch {}; 147 148 thread.join(); 148 149 self.thread = null; 149 150 } ··· 151 152 self.front_screen.deinit(self.allocator); 152 153 self.back_screen_pri.deinit(self.allocator); 153 154 self.back_screen_alt.deinit(self.allocator); 154 - self.tab_stops.deinit(); 155 - self.title.deinit(); 156 - self.working_directory.deinit(); 155 + self.tab_stops.deinit(self.allocator); 156 + self.title.deinit(self.allocator); 157 + self.working_directory.deinit(self.allocator); 157 158 } 158 159 159 160 pub fn spawn(self: *Terminal) !void { ··· 164 165 165 166 self.working_directory.clearRetainingCapacity(); 166 167 if (self.cmd.working_directory) |pwd| { 167 - try self.working_directory.appendSlice(pwd); 168 + try self.working_directory.appendSlice(self.allocator, pwd); 168 169 } else { 169 170 const pwd = std.fs.cwd(); 170 171 var buffer: [std.fs.max_path_bytes]u8 = undefined; 171 172 const out_path = try std.os.getFdPath(pwd.fd, &buffer); 172 - try self.working_directory.appendSlice(out_path); 173 + try self.working_directory.appendSlice(self.allocator, out_path); 173 174 } 174 175 175 176 { ··· 208 209 try self.pty.setSize(ws); 209 210 } 210 211 211 - pub fn draw(self: *Terminal, win: vaxis.Window) !void { 212 + pub fn draw(self: *Terminal, allocator: std.mem.Allocator, win: vaxis.Window) !void { 212 213 if (self.back_mutex.tryLock()) { 213 214 defer self.back_mutex.unlock(); 214 215 // We keep this as a separate condition so we don't deadlock by obtaining the lock but not 215 216 // having sync 216 217 if (!self.mode.sync) { 217 - try self.back_screen.copyTo(&self.front_screen); 218 + try self.back_screen.copyTo(allocator, &self.front_screen); 218 219 self.dirty = false; 219 220 } 220 221 } ··· 245 246 } 246 247 } 247 248 248 - fn opaqueWrite(ptr: *const anyopaque, buf: []const u8) !usize { 249 - const self: *const Terminal = @ptrCast(@alignCast(ptr)); 250 - return posix.write(self.pty.pty, buf); 251 - } 252 - 253 - pub fn writer(self: *const Terminal) *std.io.Writer { 254 - const local = struct { 255 - var writer: std.io.Writer = .{ 256 - .context = self, 257 - .writeFn = Terminal.opaqueWrite, 258 - }; 259 - }; 260 - return &local.writer; 261 - } 262 - 263 - fn opaqueRead(ptr: *const anyopaque, buf: []u8) !usize { 264 - const self: *const Terminal = @ptrCast(@alignCast(ptr)); 265 - return posix.read(self.pty.pty, buf); 249 + pub fn writer(self: *Terminal) *std.Io.Writer { 250 + return &self.pty_writer.interface; 266 251 } 267 252 268 - fn anyReader(self: *const Terminal) std.io.AnyReader { 269 - return .{ 270 - .context = self, 271 - .readFn = Terminal.opaqueRead, 272 - }; 253 + fn reader(self: *const Terminal, buf: []u8) std.fs.File.Reader { 254 + return self.pty.pty.readerStreaming(buf); 273 255 } 274 256 275 257 /// process the output from the command on the pty 276 258 fn run(self: *Terminal) !void { 277 259 var parser: Parser = .{ 278 - .buf = try std.ArrayList(u8).initCapacity(self.allocator, 128), 260 + .buf = try .initCapacity(self.allocator, 128), 279 261 }; 280 262 defer parser.buf.deinit(); 281 263 282 - // Use our anyReader to make a buffered reader, then get *that* any reader 283 - var reader = std.io.bufferedReader(self.anyReader()); 264 + var reader_buf: [4096]u8 = undefined; 265 + var reader_ = self.reader(&reader_buf); 284 266 285 267 while (!self.should_quit) { 286 - const event = try parser.parseReader(&reader); 268 + const event = try parser.parseReader(&reader_.interface); 287 269 self.back_mutex.lock(); 288 270 defer self.back_mutex.unlock(); 289 271 ··· 318 300 if (ts == self.back_screen.cursor.col) break true; 319 301 } else false; 320 302 if (already_set) continue; 321 - try self.tab_stops.append(@truncate(self.back_screen.cursor.col)); 303 + try self.tab_stops.append(self.allocator, @truncate(self.back_screen.cursor.col)); 322 304 std.mem.sort(u16, self.tab_stops.items, {}, std.sort.asc(u16)); 323 305 }, 324 306 // Reverse Index ··· 469 451 self.tab_stops.clearRetainingCapacity(); 470 452 var col: u16 = 0; 471 453 while (col < self.back_screen.width) : (col += 8) { 472 - try self.tab_stops.append(col); 454 + try self.tab_stops.append(self.allocator, col); 473 455 } 474 456 } 475 457 }, ··· 485 467 ); 486 468 var i: usize = start; 487 469 while (i < end) : (i += 1) { 488 - self.back_screen.buf[i].erase(self.back_screen.cursor.style.bg); 470 + self.back_screen.buf[i].erase(self.allocator, self.back_screen.cursor.style.bg); 489 471 } 490 472 }, 491 473 'Z' => { ··· 525 507 // Secondary 526 508 '>' => try self.writer().writeAll("\x1B[>1;69;0c"), 527 509 '=' => try self.writer().writeAll("\x1B[=0000c"), 528 - else => log.info("unhandled CSI: {}", .{seq}), 510 + else => log.info("unhandled CSI: {f}", .{seq}), 529 511 } 530 512 } else { 531 513 // Primary ··· 563 545 const n = iter.next() orelse 0; 564 546 switch (n) { 565 547 0 => { 566 - const current = try self.tab_stops.toOwnedSlice(); 567 - defer self.tab_stops.allocator.free(current); 548 + const current = try self.tab_stops.toOwnedSlice(self.allocator); 549 + defer self.allocator.free(current); 568 550 self.tab_stops.clearRetainingCapacity(); 569 551 for (current) |stop| { 570 552 if (stop == self.back_screen.cursor.col) continue; 571 - try self.tab_stops.append(stop); 553 + try self.tab_stops.append(self.allocator, stop); 572 554 } 573 555 }, 574 - 3 => self.tab_stops.clearAndFree(), 575 - else => log.info("unhandled CSI: {}", .{seq}), 556 + 3 => self.tab_stops.clearAndFree(self.allocator), 557 + else => log.info("unhandled CSI: {f}", .{seq}), 576 558 } 577 559 }, 578 560 'h', 'l' => { ··· 599 581 self.back_screen.cursor.row + 1, 600 582 self.back_screen.cursor.col + 1, 601 583 }), 602 - else => log.info("unhandled CSI: {}", .{seq}), 584 + else => log.info("unhandled CSI: {f}", .{seq}), 603 585 } 604 586 } 605 587 }, ··· 618 600 }, 619 601 } 620 602 }, 621 - else => log.info("unhandled CSI: {}", .{seq}), 603 + else => log.info("unhandled CSI: {f}", .{seq}), 622 604 } 623 605 } 624 606 }, ··· 640 622 "\x1bP>|libvaxis {s}\x1B\\", 641 623 .{"dev"}, 642 624 ), 643 - else => log.info("unhandled CSI: {}", .{seq}), 625 + else => log.info("unhandled CSI: {f}", .{seq}), 644 626 } 645 627 } 646 628 }, ··· 668 650 self.back_screen.cursor.row = 0; 669 651 } 670 652 }, 671 - else => log.info("unhandled CSI: {}", .{seq}), 653 + else => log.info("unhandled CSI: {f}", .{seq}), 672 654 } 673 655 }, 674 656 .osc => |osc| { ··· 683 665 switch (ps) { 684 666 0 => { 685 667 self.title.clearRetainingCapacity(); 686 - try self.title.appendSlice(osc[semicolon + 1 ..]); 668 + try self.title.appendSlice(self.allocator, osc[semicolon + 1 ..]); 687 669 self.event_queue.push(.{ .title_change = self.title.items }); 688 670 }, 689 671 7 => { ··· 702 684 defer i += 2; 703 685 break :blk try std.fmt.parseUnsigned(u8, enc[i + 1 .. i + 3], 16); 704 686 } else enc[i]; 705 - try self.working_directory.append(b); 687 + try self.working_directory.append(self.allocator, b); 706 688 } 707 689 self.event_queue.push(.{ .pwd_change = self.working_directory.items }); 708 690 },
+5 -12
src/widgets/terminal/ansi.zig
··· 55 55 return .{ .bytes = self.params }; 56 56 } 57 57 58 - pub fn format( 59 - self: CSI, 60 - comptime layout: []const u8, 61 - opts: std.fmt.FormatOptions, 62 - writer: anytype, 63 - ) !void { 64 - _ = layout; 65 - _ = opts; 58 + pub fn format(self: CSI, writer: anytype) !void { 66 59 if (self.private_marker == null and self.intermediate == null) 67 - try std.fmt.format(writer, "CSI {s} {c}", .{ 60 + try writer.print("CSI {s} {c}", .{ 68 61 self.params, 69 62 self.final, 70 63 }) 71 64 else if (self.private_marker != null and self.intermediate == null) 72 - try std.fmt.format(writer, "CSI {c} {s} {c}", .{ 65 + try writer.print("CSI {c} {s} {c}", .{ 73 66 self.private_marker.?, 74 67 self.params, 75 68 self.final, 76 69 }) 77 70 else if (self.private_marker == null and self.intermediate != null) 78 - try std.fmt.format(writer, "CSI {s} {c} {c}", .{ 71 + try writer.print("CSI {s} {c} {c}", .{ 79 72 self.params, 80 73 self.intermediate.?, 81 74 self.final, 82 75 }) 83 76 else 84 - try std.fmt.format(writer, "CSI {c} {s} {c} {c}", .{ 77 + try writer.print("CSI {c} {s} {c} {c}", .{ 85 78 self.private_marker.?, 86 79 self.params, 87 80 self.intermediate.?,
+2 -2
src/widgets/terminal/key.zig
··· 2 2 const vaxis = @import("../../main.zig"); 3 3 4 4 pub fn encode( 5 - writer: std.io.AnyWriter, 5 + writer: *std.Io.Writer, 6 6 key: vaxis.Key, 7 7 press: bool, 8 8 kitty_flags: vaxis.Key.KittyFlags, ··· 19 19 } 20 20 } 21 21 22 - fn legacy(writer: std.io.AnyWriter, key: vaxis.Key) !void { 22 + fn legacy(writer: *std.Io.Writer, key: vaxis.Key) !void { 23 23 // If we have text, we always write it directly 24 24 if (key.text) |text| { 25 25 try writer.writeAll(text);