+5
-5
examples/vt.zig
+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
+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
+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
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
+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
+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
+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
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);