Compare commits
No commits in common. "54c43bab5dbfc9458181da10bb0db26876da591b" and "6e85cd1fca64548052e3f05dd9e6c8dd4f49ef46" have entirely different histories.
54c43bab5d
...
6e85cd1fca
15 changed files with 104 additions and 946 deletions
11
build.zig
11
build.zig
|
@ -4,13 +4,11 @@ pub fn build(b: *std.Build) void {
|
|||
const include_libxev = b.option(bool, "libxev", "Enable support for libxev library (default: true)") orelse true;
|
||||
const include_images = b.option(bool, "images", "Enable support for images (default: true)") orelse true;
|
||||
const include_text_input = b.option(bool, "text_input", "Enable support for the TextInput widget (default: true)") orelse true;
|
||||
const include_aio = b.option(bool, "aio", "Enable support for zig-aio library (default: false)") orelse false;
|
||||
|
||||
const options = b.addOptions();
|
||||
options.addOption(bool, "libxev", include_libxev);
|
||||
options.addOption(bool, "images", include_images);
|
||||
options.addOption(bool, "text_input", include_text_input);
|
||||
options.addOption(bool, "aio", include_aio);
|
||||
|
||||
const options_mod = options.createModule();
|
||||
|
||||
|
@ -35,10 +33,6 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
.target = target,
|
||||
}) else null;
|
||||
const aio_dep = if (include_aio) b.lazyDependency("aio", .{
|
||||
.optimize = optimize,
|
||||
.target = target,
|
||||
}) else null;
|
||||
|
||||
// Module
|
||||
const vaxis_mod = b.addModule("vaxis", .{
|
||||
|
@ -52,8 +46,6 @@ pub fn build(b: *std.Build) void {
|
|||
if (zigimg_dep) |dep| vaxis_mod.addImport("zigimg", dep.module("zigimg"));
|
||||
if (gap_buffer_dep) |dep| vaxis_mod.addImport("gap_buffer", dep.module("gap_buffer"));
|
||||
if (xev_dep) |dep| vaxis_mod.addImport("xev", dep.module("xev"));
|
||||
if (aio_dep) |dep| vaxis_mod.addImport("aio", dep.module("aio"));
|
||||
if (aio_dep) |dep| vaxis_mod.addImport("coro", dep.module("coro"));
|
||||
vaxis_mod.addImport("build_options", options_mod);
|
||||
|
||||
// Examples
|
||||
|
@ -67,7 +59,6 @@ pub fn build(b: *std.Build) void {
|
|||
vaxis,
|
||||
vt,
|
||||
xev,
|
||||
aio,
|
||||
};
|
||||
const example_option = b.option(Example, "example", "Example to run (default: text_input)") orelse .text_input;
|
||||
const example_step = b.step("example", "Run example");
|
||||
|
@ -82,8 +73,6 @@ pub fn build(b: *std.Build) void {
|
|||
});
|
||||
example.root_module.addImport("vaxis", vaxis_mod);
|
||||
if (xev_dep) |dep| example.root_module.addImport("xev", dep.module("xev"));
|
||||
if (aio_dep) |dep| example.root_module.addImport("aio", dep.module("aio"));
|
||||
if (aio_dep) |dep| example.root_module.addImport("coro", dep.module("coro"));
|
||||
|
||||
const example_run = b.addRunArtifact(example);
|
||||
example_step.dependOn(&example_run.step);
|
||||
|
|
|
@ -22,11 +22,6 @@
|
|||
.hash = "12207b7a5b538ffb7fb18f954ae17d2f8490b6e3778a9e30564ad82c58ee8da52361",
|
||||
.lazy = true,
|
||||
},
|
||||
.aio = .{
|
||||
.url = "git+https://github.com/Cloudef/zig-aio#be8e2b374bf223202090e282447fa4581029c2eb",
|
||||
.hash = "122012a11b37a350395a32fdb514e57ff54a0f9d8d4ce09498b6c45ffb7211232920",
|
||||
.lazy = true,
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"LICENSE",
|
||||
|
|
171
examples/aio.zig
171
examples/aio.zig
|
@ -1,171 +0,0 @@
|
|||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const vaxis = @import("vaxis");
|
||||
const aio = @import("aio");
|
||||
const coro = @import("coro");
|
||||
|
||||
pub const panic = vaxis.panic_handler;
|
||||
|
||||
const Event = union(enum) {
|
||||
key_press: vaxis.Key,
|
||||
winsize: vaxis.Winsize,
|
||||
};
|
||||
|
||||
const Loop = vaxis.aio.Loop(Event);
|
||||
|
||||
const Video = enum { no_state, ready, end };
|
||||
const Audio = enum { no_state, ready, end };
|
||||
|
||||
fn downloadTask(allocator: std.mem.Allocator, url: []const u8) ![]const u8 {
|
||||
var client: std.http.Client = .{ .allocator = allocator };
|
||||
defer client.deinit();
|
||||
var body = std.ArrayList(u8).init(allocator);
|
||||
_ = try client.fetch(.{
|
||||
.location = .{ .url = url },
|
||||
.response_storage = .{ .dynamic = &body },
|
||||
.max_append_size = 1.6e+7,
|
||||
});
|
||||
return try body.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn audioTask(allocator: std.mem.Allocator) !void {
|
||||
errdefer coro.yield(Audio.end) catch {};
|
||||
|
||||
// var child = std.process.Child.init(&.{ "aplay", "-Dplug:default", "-q", "-f", "S16_LE", "-r", "8000" }, allocator);
|
||||
var child = std.process.Child.init(&.{ "mpv", "--audio-samplerate=16000", "--audio-channels=mono", "--audio-format=s16", "-" }, allocator);
|
||||
child.stdin_behavior = .Pipe;
|
||||
child.stdout_behavior = .Ignore;
|
||||
child.stderr_behavior = .Ignore;
|
||||
child.spawn() catch return; // no sound
|
||||
defer _ = child.kill() catch {};
|
||||
|
||||
const sound = blk: {
|
||||
var tpool: coro.ThreadPool = .{};
|
||||
try tpool.start(allocator, 1);
|
||||
defer tpool.deinit();
|
||||
break :blk try tpool.yieldForCompletition(downloadTask, .{ allocator, "https://keroserene.net/lol/roll.s16" });
|
||||
};
|
||||
defer allocator.free(sound);
|
||||
|
||||
try coro.yield(Audio.ready);
|
||||
|
||||
var audio_off: usize = 0;
|
||||
while (audio_off < sound.len) {
|
||||
var written: usize = 0;
|
||||
try coro.io.single(aio.Write{ .file = child.stdin.?, .buffer = sound[audio_off..], .out_written = &written });
|
||||
audio_off += written;
|
||||
}
|
||||
|
||||
// the audio is already fed to the player and the defer
|
||||
// would kill the child stay here chilling
|
||||
coro.yield(Audio.end) catch {};
|
||||
}
|
||||
|
||||
fn videoTask(writer: std.io.AnyWriter) !void {
|
||||
defer coro.yield(Video.end) catch {};
|
||||
|
||||
var socket: std.posix.socket_t = undefined;
|
||||
try coro.io.single(aio.Socket{
|
||||
.domain = std.posix.AF.INET,
|
||||
.flags = std.posix.SOCK.STREAM | std.posix.SOCK.CLOEXEC,
|
||||
.protocol = std.posix.IPPROTO.TCP,
|
||||
.out_socket = &socket,
|
||||
});
|
||||
defer std.posix.close(socket);
|
||||
|
||||
const address = std.net.Address.initIp4(.{ 44, 224, 41, 160 }, 1987);
|
||||
try coro.io.single(aio.Connect{
|
||||
.socket = socket,
|
||||
.addr = &address.any,
|
||||
.addrlen = address.getOsSockLen(),
|
||||
});
|
||||
|
||||
try coro.yield(Video.ready);
|
||||
|
||||
var buf: [1024]u8 = undefined;
|
||||
while (true) {
|
||||
var read: usize = 0;
|
||||
try coro.io.single(aio.Recv{ .socket = socket, .buffer = &buf, .out_read = &read });
|
||||
if (read == 0) break;
|
||||
_ = try writer.write(buf[0..read]);
|
||||
}
|
||||
}
|
||||
|
||||
fn loadingTask(vx: *vaxis.Vaxis, writer: std.io.AnyWriter) !void {
|
||||
var color_idx: u8 = 30;
|
||||
var dir: enum { up, down } = .up;
|
||||
|
||||
while (true) {
|
||||
try coro.io.single(aio.Timeout{ .ns = 8 * std.time.ns_per_ms });
|
||||
|
||||
const style: vaxis.Style = .{ .fg = .{ .rgb = [_]u8{ color_idx, color_idx, color_idx } } };
|
||||
const segment: vaxis.Segment = .{ .text = vaxis.logo, .style = style };
|
||||
|
||||
const win = vx.window();
|
||||
win.clear();
|
||||
|
||||
var loc = vaxis.widgets.alignment.center(win, 28, 4);
|
||||
_ = try loc.printSegment(segment, .{ .wrap = .grapheme });
|
||||
|
||||
switch (dir) {
|
||||
.up => {
|
||||
color_idx += 1;
|
||||
if (color_idx == 255) dir = .down;
|
||||
},
|
||||
.down => {
|
||||
color_idx -= 1;
|
||||
if (color_idx == 30) dir = .up;
|
||||
},
|
||||
}
|
||||
|
||||
try vx.render(writer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var tty = try vaxis.Tty.init();
|
||||
defer tty.deinit();
|
||||
|
||||
var vx = try vaxis.init(allocator, .{});
|
||||
defer vx.deinit(allocator, tty.anyWriter());
|
||||
|
||||
var scheduler = try coro.Scheduler.init(allocator, .{});
|
||||
defer scheduler.deinit();
|
||||
|
||||
var loop = try Loop.init();
|
||||
try loop.spawn(&scheduler, &vx, &tty, null, .{});
|
||||
defer loop.deinit(&vx, &tty);
|
||||
|
||||
try vx.enterAltScreen(tty.anyWriter());
|
||||
try vx.queryTerminalSend(tty.anyWriter());
|
||||
|
||||
var buffered_tty_writer = tty.bufferedWriter();
|
||||
const loading = try scheduler.spawn(loadingTask, .{ &vx, buffered_tty_writer.writer().any() }, .{});
|
||||
const audio = try scheduler.spawn(audioTask, .{allocator}, .{});
|
||||
const video = try scheduler.spawn(videoTask, .{buffered_tty_writer.writer().any()}, .{});
|
||||
|
||||
main: while (try scheduler.tick(.blocking) > 0) {
|
||||
while (try loop.popEvent()) |event| switch (event) {
|
||||
.key_press => |key| {
|
||||
if (key.matches('c', .{ .ctrl = true })) {
|
||||
break :main;
|
||||
}
|
||||
},
|
||||
.winsize => |ws| try vx.resize(allocator, buffered_tty_writer.writer().any(), ws),
|
||||
};
|
||||
|
||||
if (audio.state(Video) == .ready and video.state(Audio) == .ready) {
|
||||
loading.cancel();
|
||||
audio.wakeup();
|
||||
video.wakeup();
|
||||
} else if (audio.state(Audio) == .end and video.state(Video) == .end) {
|
||||
break :main;
|
||||
}
|
||||
|
||||
try buffered_tty_writer.flush();
|
||||
}
|
||||
}
|
|
@ -61,11 +61,9 @@ pub fn main() !void {
|
|||
|
||||
var redraw: bool = false;
|
||||
while (true) {
|
||||
std.debug.print("inside while loop before resize\n", .{});
|
||||
std.time.sleep(8 * std.time.ns_per_ms);
|
||||
// try vt events first
|
||||
while (vt.tryEvent()) |event| {
|
||||
std.debug.print("inside stryEventloop \n", .{});
|
||||
redraw = true;
|
||||
switch (event) {
|
||||
.bell => {},
|
||||
|
@ -76,7 +74,6 @@ pub fn main() !void {
|
|||
}
|
||||
}
|
||||
while (loop.tryEvent()) |event| {
|
||||
std.debug.print("inside loop.tryEvent\n", .{});
|
||||
redraw = true;
|
||||
switch (event) {
|
||||
.key_press => |key| {
|
||||
|
|
172
log.lg
172
log.lg
|
@ -1,172 +0,0 @@
|
|||
info(loop): pixel mouse capability detected
|
||||
info(loop): unicode capability detected
|
||||
info(loop): color_scheme_updates capability detected
|
||||
info(loop): kitty keyboard capability detected
|
||||
info(loop): kitty graphics capability detected
|
||||
slave name: /dev/ttys008
|
||||
posix opened : /dev/ttys008
|
||||
set size done
|
||||
inside spawnwe are not child: 56928
|
||||
inside while loop before resize
|
||||
inside run
|
||||
inside while loop
|
||||
inside loop.tryEvent
|
||||
debug(vaxis): resizing screen: width=81 height=49
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside loop.tryEvent
|
||||
before switch event
|
||||
inside print event
|
||||
inside while loop
|
||||
inside while loop before resize
|
||||
inside stryEventloop
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside loop.tryEvent
|
||||
before switch event
|
||||
inside print event
|
||||
inside while loop
|
||||
inside while loop before resize
|
||||
inside stryEventloop
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside loop.tryEvent
|
||||
before switch event
|
||||
inside handlec0
|
||||
inside while loop
|
||||
before switch event
|
||||
inside handlec0
|
||||
inside while loop
|
||||
inside while loop before resize
|
||||
inside stryEventloop
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside loop.tryEvent
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside while loop before resize
|
||||
inside loop.tryEvent
|
||||
before switch event
|
||||
inside handlec0
|
||||
debug(vaxis): total renders = 8
|
||||
debug(vaxis): microseconds per render = 1393
|
|
@ -704,6 +704,18 @@ pub fn translateMouse(self: Vaxis, mouse: Mouse) Mouse {
|
|||
result.row = ypos / ycell;
|
||||
result.xoffset = xpos % xcell;
|
||||
result.yoffset = ypos % ycell;
|
||||
log.debug("translateMouse x/ypos:{d}/{d} cell:{d}/{d} xtra:{d}/{d} col/rol:{d}/{d} x/y:{d}/{d}", .{
|
||||
xpos, ypos,
|
||||
xcell, ycell,
|
||||
xextra, yextra,
|
||||
result.col, result.row,
|
||||
result.xoffset, result.yoffset,
|
||||
});
|
||||
} else {
|
||||
log.debug("translateMouse col/rol:{d}/{d} x/y:{d}/{d}", .{
|
||||
result.col, result.row,
|
||||
result.xoffset, result.yoffset,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
275
src/Window.zig
275
src/Window.zig
|
@ -182,8 +182,8 @@ pub fn child(self: Window, opts: ChildOptions) Window {
|
|||
const y_off: usize = if (loc.top) 1 else 0;
|
||||
const h_delt: usize = if (loc.bottom) 1 else 0;
|
||||
const w_delt: usize = if (loc.right) 1 else 0;
|
||||
const h_ch: usize = h -| y_off -| h_delt;
|
||||
const w_ch: usize = w -| x_off -| w_delt;
|
||||
const h_ch: usize = h - y_off - h_delt;
|
||||
const w_ch: usize = w - x_off - w_delt;
|
||||
return result.initChild(x_off, y_off, .{ .limit = w_ch }, .{ .limit = h_ch });
|
||||
}
|
||||
|
||||
|
@ -256,8 +256,6 @@ pub fn setCursorShape(self: Window, shape: Cell.CursorShape) void {
|
|||
pub const PrintOptions = struct {
|
||||
/// vertical offset to start printing at
|
||||
row_offset: usize = 0,
|
||||
/// horizontal offset to start printing at
|
||||
col_offset: usize = 0,
|
||||
|
||||
/// wrap behavior for printing
|
||||
wrap: enum {
|
||||
|
@ -287,7 +285,7 @@ pub fn print(self: Window, segments: []const Segment, opts: PrintOptions) !Print
|
|||
var row = opts.row_offset;
|
||||
switch (opts.wrap) {
|
||||
.grapheme => {
|
||||
var col: usize = opts.col_offset;
|
||||
var col: usize = 0;
|
||||
const overflow: bool = blk: for (segments) |segment| {
|
||||
var iter = self.screen.unicode.graphemeIterator(segment.text);
|
||||
while (iter.next()) |grapheme| {
|
||||
|
@ -326,77 +324,94 @@ pub fn print(self: Window, segments: []const Segment, opts: PrintOptions) !Print
|
|||
};
|
||||
},
|
||||
.word => {
|
||||
var col: usize = opts.col_offset;
|
||||
var col: usize = 0;
|
||||
var overflow: bool = false;
|
||||
var soft_wrapped: bool = false;
|
||||
outer: for (segments) |segment| {
|
||||
var line_iter: LineIterator = .{ .buf = segment.text };
|
||||
while (line_iter.next()) |line| {
|
||||
defer {
|
||||
// We only set soft_wrapped to false if a segment actually contains a linebreak
|
||||
if (line_iter.has_break) {
|
||||
soft_wrapped = false;
|
||||
for (segments) |segment| {
|
||||
var start: usize = 0;
|
||||
var i: usize = 0;
|
||||
while (i < segment.text.len) : (i += 1) {
|
||||
// for (segment.text, 0..) |b, i| {
|
||||
const b = segment.text[i];
|
||||
const end = switch (b) {
|
||||
' ',
|
||||
'\r',
|
||||
'\n',
|
||||
=> i,
|
||||
else => if (i != segment.text.len - 1) continue else i + 1,
|
||||
};
|
||||
const word = segment.text[start..end];
|
||||
// find the start of the next word
|
||||
start = while (i + 1 < segment.text.len) : (i += 1) {
|
||||
if (segment.text[i + 1] == ' ') continue;
|
||||
break i + 1;
|
||||
} else i;
|
||||
const width = self.gwidth(word);
|
||||
const non_wsp_width: usize = for (word, 0..) |wb, wi| {
|
||||
if (wb == '\r' or wb == '\n') {
|
||||
row += 1;
|
||||
col = 0;
|
||||
break width -| wi -| 1;
|
||||
}
|
||||
if (wb != ' ') break width - wi;
|
||||
} else 0;
|
||||
|
||||
if (width + col > self.width and non_wsp_width < self.width) {
|
||||
// wrap
|
||||
row += 1;
|
||||
col = 0;
|
||||
soft_wrapped = true;
|
||||
}
|
||||
if (row >= self.height) {
|
||||
overflow = true;
|
||||
break;
|
||||
}
|
||||
// if we are soft wrapped, (col == 0 and row > 0), then trim
|
||||
// leading spaces
|
||||
const printed_word = if (soft_wrapped)
|
||||
std.mem.trimLeft(u8, word, " ")
|
||||
else
|
||||
word;
|
||||
defer soft_wrapped = false;
|
||||
var iter = self.screen.unicode.graphemeIterator(printed_word);
|
||||
while (iter.next()) |grapheme| {
|
||||
const s = grapheme.bytes(printed_word);
|
||||
const w = self.gwidth(s);
|
||||
if (opts.commit) self.writeCell(col, row, .{
|
||||
.char = .{
|
||||
.grapheme = s,
|
||||
.width = w,
|
||||
},
|
||||
.style = segment.style,
|
||||
.link = segment.link,
|
||||
});
|
||||
col += w;
|
||||
if (col >= self.width) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
var iter: WhitespaceTokenizer = .{ .buf = line };
|
||||
while (iter.next()) |token| {
|
||||
switch (token) {
|
||||
.whitespace => |len| {
|
||||
if (soft_wrapped) continue;
|
||||
for (0..len) |_| {
|
||||
if (col >= self.width) {
|
||||
col = 0;
|
||||
row += 1;
|
||||
break;
|
||||
}
|
||||
if (opts.commit) {
|
||||
self.writeCell(col, row, .{
|
||||
.char = .{
|
||||
.grapheme = " ",
|
||||
.width = 1,
|
||||
},
|
||||
.style = segment.style,
|
||||
.link = segment.link,
|
||||
});
|
||||
}
|
||||
col += 1;
|
||||
}
|
||||
},
|
||||
.word => |word| {
|
||||
const width = self.gwidth(word);
|
||||
if (width + col > self.width and width < self.width) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
}
|
||||
|
||||
var grapheme_iterator = self.screen.unicode.graphemeIterator(word);
|
||||
while (grapheme_iterator.next()) |grapheme| {
|
||||
soft_wrapped = false;
|
||||
if (row >= self.height) {
|
||||
overflow = true;
|
||||
break :outer;
|
||||
}
|
||||
const s = grapheme.bytes(word);
|
||||
const w = self.gwidth(s);
|
||||
if (opts.commit) self.writeCell(col, row, .{
|
||||
.char = .{
|
||||
.grapheme = s,
|
||||
.width = w,
|
||||
},
|
||||
.style = segment.style,
|
||||
.link = segment.link,
|
||||
});
|
||||
col += w;
|
||||
if (col >= self.width) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
soft_wrapped = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
switch (b) {
|
||||
' ' => {
|
||||
if (col > 0) {
|
||||
if (opts.commit) self.writeCell(col, row, .{
|
||||
.char = .{
|
||||
.grapheme = " ",
|
||||
.width = 1,
|
||||
},
|
||||
.style = segment.style,
|
||||
.link = segment.link,
|
||||
});
|
||||
col += 1;
|
||||
}
|
||||
},
|
||||
'\r',
|
||||
'\n',
|
||||
=> {
|
||||
col = 0;
|
||||
row += 1;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +423,7 @@ pub fn print(self: Window, segments: []const Segment, opts: PrintOptions) !Print
|
|||
};
|
||||
},
|
||||
.none => {
|
||||
var col: usize = opts.col_offset;
|
||||
var col: usize = 0;
|
||||
const overflow: bool = blk: for (segments) |segment| {
|
||||
var iter = self.screen.unicode.graphemeIterator(segment.text);
|
||||
while (iter.next()) |grapheme| {
|
||||
|
@ -621,24 +636,6 @@ test "print: word" {
|
|||
try std.testing.expectEqual(0, result.row);
|
||||
try std.testing.expectEqual(false, result.overflow);
|
||||
}
|
||||
{
|
||||
var segments = [_]Segment{
|
||||
.{ .text = " " },
|
||||
};
|
||||
const result = try win.print(&segments, opts);
|
||||
try std.testing.expectEqual(1, result.col);
|
||||
try std.testing.expectEqual(0, result.row);
|
||||
try std.testing.expectEqual(false, result.overflow);
|
||||
}
|
||||
{
|
||||
var segments = [_]Segment{
|
||||
.{ .text = " a" },
|
||||
};
|
||||
const result = try win.print(&segments, opts);
|
||||
try std.testing.expectEqual(2, result.col);
|
||||
try std.testing.expectEqual(0, result.row);
|
||||
try std.testing.expectEqual(false, result.overflow);
|
||||
}
|
||||
{
|
||||
var segments = [_]Segment{
|
||||
.{ .text = "a b" },
|
||||
|
@ -752,104 +749,4 @@ test "print: word" {
|
|||
try std.testing.expectEqual(1, result.row);
|
||||
try std.testing.expectEqual(false, result.overflow);
|
||||
}
|
||||
{
|
||||
var segments = [_]Segment{
|
||||
.{ .text = "note" },
|
||||
.{ .text = " now" },
|
||||
};
|
||||
const result = try win.print(&segments, opts);
|
||||
try std.testing.expectEqual(3, result.col);
|
||||
try std.testing.expectEqual(1, result.row);
|
||||
try std.testing.expectEqual(false, result.overflow);
|
||||
}
|
||||
{
|
||||
var segments = [_]Segment{
|
||||
.{ .text = "note " },
|
||||
.{ .text = "now" },
|
||||
};
|
||||
const result = try win.print(&segments, opts);
|
||||
try std.testing.expectEqual(3, result.col);
|
||||
try std.testing.expectEqual(1, result.row);
|
||||
try std.testing.expectEqual(false, result.overflow);
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates a slice of bytes by linebreaks. Lines are split by '\r', '\n', or '\r\n'
|
||||
const LineIterator = struct {
|
||||
buf: []const u8,
|
||||
index: usize = 0,
|
||||
has_break: bool = true,
|
||||
|
||||
fn next(self: *LineIterator) ?[]const u8 {
|
||||
if (self.index >= self.buf.len) return null;
|
||||
|
||||
const start = self.index;
|
||||
const end = std.mem.indexOfAnyPos(u8, self.buf, self.index, "\r\n") orelse {
|
||||
if (start == 0) self.has_break = false;
|
||||
self.index = self.buf.len;
|
||||
return self.buf[start..];
|
||||
};
|
||||
|
||||
self.index = end;
|
||||
self.consumeCR();
|
||||
self.consumeLF();
|
||||
return self.buf[start..end];
|
||||
}
|
||||
|
||||
// consumes a \n byte
|
||||
fn consumeLF(self: *LineIterator) void {
|
||||
if (self.index >= self.buf.len) return;
|
||||
if (self.buf[self.index] == '\n') self.index += 1;
|
||||
}
|
||||
|
||||
// consumes a \r byte
|
||||
fn consumeCR(self: *LineIterator) void {
|
||||
if (self.index >= self.buf.len) return;
|
||||
if (self.buf[self.index] == '\r') self.index += 1;
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns tokens of text and whitespace
|
||||
const WhitespaceTokenizer = struct {
|
||||
buf: []const u8,
|
||||
index: usize = 0,
|
||||
|
||||
const Token = union(enum) {
|
||||
// the length of whitespace. Tab = 8
|
||||
whitespace: usize,
|
||||
word: []const u8,
|
||||
};
|
||||
|
||||
fn next(self: *WhitespaceTokenizer) ?Token {
|
||||
if (self.index >= self.buf.len) return null;
|
||||
const Mode = enum {
|
||||
whitespace,
|
||||
word,
|
||||
};
|
||||
const first = self.buf[self.index];
|
||||
const mode: Mode = if (first == ' ' or first == '\t') .whitespace else .word;
|
||||
switch (mode) {
|
||||
.whitespace => {
|
||||
var len: usize = 0;
|
||||
while (self.index < self.buf.len) : (self.index += 1) {
|
||||
switch (self.buf[self.index]) {
|
||||
' ' => len += 1,
|
||||
'\t' => len += 8,
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
return .{ .whitespace = len };
|
||||
},
|
||||
.word => {
|
||||
const start = self.index;
|
||||
while (self.index < self.buf.len) : (self.index += 1) {
|
||||
switch (self.buf[self.index]) {
|
||||
' ', '\t' => break,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
return .{ .word = self.buf[start..self.index] };
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
268
src/aio.zig
268
src/aio.zig
|
@ -1,268 +0,0 @@
|
|||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const aio = @import("aio");
|
||||
const coro = @import("coro");
|
||||
const vaxis = @import("main.zig");
|
||||
const log = std.log.scoped(.vaxis_aio);
|
||||
|
||||
comptime {
|
||||
if (builtin.target.os.tag == .windows) {
|
||||
@compileError("Windows is not supported right now");
|
||||
}
|
||||
}
|
||||
|
||||
const Yield = enum { no_state, took_event };
|
||||
|
||||
/// zig-aio based event loop
|
||||
/// <https://github.com/Cloudef/zig-aio>
|
||||
pub fn Loop(comptime T: type) type {
|
||||
return struct {
|
||||
const Event = T;
|
||||
|
||||
winsize_task: ?coro.Task.Generic2(winsizeTask) = null,
|
||||
reader_task: ?coro.Task.Generic2(ttyReaderTask) = null,
|
||||
queue: std.BoundedArray(T, 512) = .{},
|
||||
source: aio.EventSource,
|
||||
fatal: bool = false,
|
||||
|
||||
pub fn init() !@This() {
|
||||
return .{ .source = try aio.EventSource.init() };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This(), vx: *vaxis.Vaxis, tty: *vaxis.Tty) void {
|
||||
vx.deviceStatusReport(tty.anyWriter()) catch {};
|
||||
if (self.winsize_task) |task| task.cancel();
|
||||
if (self.reader_task) |task| task.cancel();
|
||||
self.source.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
fn winsizeInner(self: *@This(), tty: *vaxis.Tty) !void {
|
||||
const Context = struct {
|
||||
loop: *@TypeOf(self.*),
|
||||
tty: *vaxis.Tty,
|
||||
winsize: ?vaxis.Winsize = null,
|
||||
fn cb(ptr: *anyopaque) void {
|
||||
std.debug.assert(coro.current() == null);
|
||||
const ctx: *@This() = @ptrCast(@alignCast(ptr));
|
||||
ctx.winsize = vaxis.Tty.getWinsize(ctx.tty.fd) catch return;
|
||||
ctx.loop.source.notify();
|
||||
}
|
||||
};
|
||||
|
||||
// keep on stack
|
||||
var ctx: Context = .{ .loop = self, .tty = tty };
|
||||
if (@hasField(Event, "winsize")) {
|
||||
const handler: vaxis.Tty.SignalHandler = .{ .context = &ctx, .callback = Context.cb };
|
||||
try vaxis.Tty.notifyWinsize(handler);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
try coro.io.single(aio.WaitEventSource{ .source = &self.source });
|
||||
if (ctx.winsize) |winsize| {
|
||||
if (!@hasField(Event, "winsize")) unreachable;
|
||||
ctx.loop.postEvent(.{ .winsize = winsize }) catch {};
|
||||
ctx.winsize = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn winsizeTask(self: *@This(), tty: *vaxis.Tty) void {
|
||||
self.winsizeInner(tty) catch |err| {
|
||||
if (err != error.Canceled) log.err("winsize: {}", .{err});
|
||||
self.fatal = true;
|
||||
};
|
||||
}
|
||||
|
||||
fn ttyReaderInner(self: *@This(), vx: *vaxis.Vaxis, tty: *vaxis.Tty, paste_allocator: ?std.mem.Allocator) !void {
|
||||
// initialize a grapheme cache
|
||||
var cache: vaxis.GraphemeCache = .{};
|
||||
|
||||
// get our initial winsize
|
||||
const winsize = try vaxis.Tty.getWinsize(tty.fd);
|
||||
if (@hasField(Event, "winsize")) {
|
||||
try self.postEvent(.{ .winsize = winsize });
|
||||
}
|
||||
|
||||
var parser: vaxis.Parser = .{
|
||||
.grapheme_data = &vx.unicode.grapheme_data,
|
||||
};
|
||||
|
||||
const file: std.fs.File = .{ .handle = tty.fd };
|
||||
while (true) {
|
||||
var buf: [4096]u8 = undefined;
|
||||
var n: usize = undefined;
|
||||
var read_start: usize = 0;
|
||||
try coro.io.single(aio.Read{ .file = file, .buffer = buf[read_start..], .out_read = &n });
|
||||
var seq_start: usize = 0;
|
||||
while (seq_start < n) {
|
||||
const result = try parser.parse(buf[seq_start..n], paste_allocator);
|
||||
if (result.n == 0) {
|
||||
// copy the read to the beginning. We don't use memcpy because
|
||||
// this could be overlapping, and it's also rare
|
||||
const initial_start = seq_start;
|
||||
while (seq_start < n) : (seq_start += 1) {
|
||||
buf[seq_start - initial_start] = buf[seq_start];
|
||||
}
|
||||
read_start = seq_start - initial_start + 1;
|
||||
continue;
|
||||
}
|
||||
read_start = 0;
|
||||
seq_start += result.n;
|
||||
|
||||
const event = result.event orelse continue;
|
||||
switch (event) {
|
||||
.key_press => |key| {
|
||||
if (@hasField(Event, "key_press")) {
|
||||
// HACK: yuck. there has to be a better way
|
||||
var mut_key = key;
|
||||
if (key.text) |text| {
|
||||
mut_key.text = cache.put(text);
|
||||
}
|
||||
try self.postEvent(.{ .key_press = mut_key });
|
||||
}
|
||||
},
|
||||
.key_release => |*key| {
|
||||
if (@hasField(Event, "key_release")) {
|
||||
// HACK: yuck. there has to be a better way
|
||||
var mut_key = key;
|
||||
if (key.text) |text| {
|
||||
mut_key.text = cache.put(text);
|
||||
}
|
||||
try self.postEvent(.{ .key_release = mut_key });
|
||||
}
|
||||
},
|
||||
.mouse => |mouse| {
|
||||
if (@hasField(Event, "mouse")) {
|
||||
try self.postEvent(.{ .mouse = vx.translateMouse(mouse) });
|
||||
}
|
||||
},
|
||||
.focus_in => {
|
||||
if (@hasField(Event, "focus_in")) {
|
||||
try self.postEvent(.focus_in);
|
||||
}
|
||||
},
|
||||
.focus_out => {
|
||||
if (@hasField(Event, "focus_out")) {
|
||||
try self.postEvent(.focus_out);
|
||||
}
|
||||
},
|
||||
.paste_start => {
|
||||
if (@hasField(Event, "paste_start")) {
|
||||
try self.postEvent(.paste_start);
|
||||
}
|
||||
},
|
||||
.paste_end => {
|
||||
if (@hasField(Event, "paste_end")) {
|
||||
try self.postEvent(.paste_end);
|
||||
}
|
||||
},
|
||||
.paste => |text| {
|
||||
if (@hasField(Event, "paste")) {
|
||||
try self.postEvent(.{ .paste = text });
|
||||
} else {
|
||||
if (paste_allocator) |_|
|
||||
paste_allocator.?.free(text);
|
||||
}
|
||||
},
|
||||
.color_report => |report| {
|
||||
if (@hasField(Event, "color_report")) {
|
||||
try self.postEvent(.{ .color_report = report });
|
||||
}
|
||||
},
|
||||
.color_scheme => |scheme| {
|
||||
if (@hasField(Event, "color_scheme")) {
|
||||
try self.postEvent(.{ .color_scheme = scheme });
|
||||
}
|
||||
},
|
||||
.cap_kitty_keyboard => {
|
||||
log.info("kitty keyboard capability detected", .{});
|
||||
vx.caps.kitty_keyboard = true;
|
||||
},
|
||||
.cap_kitty_graphics => {
|
||||
if (!vx.caps.kitty_graphics) {
|
||||
log.info("kitty graphics capability detected", .{});
|
||||
vx.caps.kitty_graphics = true;
|
||||
}
|
||||
},
|
||||
.cap_rgb => {
|
||||
log.info("rgb capability detected", .{});
|
||||
vx.caps.rgb = true;
|
||||
},
|
||||
.cap_unicode => {
|
||||
log.info("unicode capability detected", .{});
|
||||
vx.caps.unicode = .unicode;
|
||||
vx.screen.width_method = .unicode;
|
||||
},
|
||||
.cap_sgr_pixels => {
|
||||
log.info("pixel mouse capability detected", .{});
|
||||
vx.caps.sgr_pixels = true;
|
||||
},
|
||||
.cap_color_scheme_updates => {
|
||||
log.info("color_scheme_updates capability detected", .{});
|
||||
vx.caps.color_scheme_updates = true;
|
||||
},
|
||||
.cap_da1 => {
|
||||
std.Thread.Futex.wake(&vx.query_futex, 10);
|
||||
},
|
||||
.winsize => unreachable, // handled elsewhere for posix
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ttyReaderTask(self: *@This(), vx: *vaxis.Vaxis, tty: *vaxis.Tty, paste_allocator: ?std.mem.Allocator) void {
|
||||
self.ttyReaderInner(vx, tty, paste_allocator) catch |err| {
|
||||
if (err != error.Canceled) log.err("ttyReader: {}", .{err});
|
||||
self.fatal = true;
|
||||
};
|
||||
}
|
||||
|
||||
/// Spawns tasks to handle winsize signal and tty
|
||||
pub fn spawn(
|
||||
self: *@This(),
|
||||
scheduler: *coro.Scheduler,
|
||||
vx: *vaxis.Vaxis,
|
||||
tty: *vaxis.Tty,
|
||||
paste_allocator: ?std.mem.Allocator,
|
||||
spawn_options: coro.Scheduler.SpawnOptions,
|
||||
) coro.Scheduler.SpawnError!void {
|
||||
if (self.reader_task) |_| unreachable; // programming error
|
||||
// This is required even if app doesn't care about winsize
|
||||
// It is because it consumes the EventSource, so it can wakeup the scheduler
|
||||
// Without that custom `postEvent`'s wouldn't wake up the scheduler and UI wouldn't update
|
||||
self.winsize_task = try scheduler.spawn(winsizeTask, .{ self, tty }, spawn_options);
|
||||
self.reader_task = try scheduler.spawn(ttyReaderTask, .{ self, vx, tty, paste_allocator }, spawn_options);
|
||||
}
|
||||
|
||||
pub const PopEventError = error{TtyCommunicationSevered};
|
||||
|
||||
/// Call this in a while loop in the main event handler until it returns null
|
||||
pub fn popEvent(self: *@This()) PopEventError!?T {
|
||||
if (self.fatal) return error.TtyCommunicationSevered;
|
||||
defer self.winsize_task.?.wakeupIf(Yield.took_event);
|
||||
defer self.reader_task.?.wakeupIf(Yield.took_event);
|
||||
return self.queue.popOrNull();
|
||||
}
|
||||
|
||||
pub const PostEventError = error{Overflow};
|
||||
|
||||
pub fn postEvent(self: *@This(), event: T) !void {
|
||||
if (coro.current()) |_| {
|
||||
while (true) {
|
||||
self.queue.insert(0, event) catch {
|
||||
// wait for the app to take event
|
||||
try coro.yield(Yield.took_event);
|
||||
continue;
|
||||
};
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// queue can be full, app could handle this error by spinning the scheduler
|
||||
try self.queue.insert(0, event);
|
||||
}
|
||||
// wakes up the scheduler, so custom events update UI
|
||||
self.source.notify();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -6,7 +6,6 @@ pub const Vaxis = @import("Vaxis.zig");
|
|||
|
||||
pub const Loop = @import("Loop.zig").Loop;
|
||||
pub const xev = @import("xev.zig");
|
||||
pub const aio = @import("aio.zig");
|
||||
|
||||
pub const Queue = @import("queue.zig").Queue;
|
||||
pub const Key = @import("Key.zig");
|
||||
|
|
|
@ -25,7 +25,8 @@ pub fn draw(self: Scrollbar, win: vaxis.Window) void {
|
|||
// don't draw when all items can be shown
|
||||
if (self.view_size >= self.total) return;
|
||||
|
||||
const bar_height = @max(std.math.divCeil(usize, self.view_size * win.height, self.total) catch unreachable, 1);
|
||||
var bar_height = self.view_size * win.height / self.total;
|
||||
if (bar_height < 0) bar_height = 1;
|
||||
const bar_top = self.top * win.height / self.total;
|
||||
var i: usize = 0;
|
||||
while (i < bar_height) : (i += 1)
|
||||
|
|
|
@ -17,22 +17,6 @@ pid: ?std.posix.pid_t = null,
|
|||
env_map: *const std.process.EnvMap,
|
||||
|
||||
pty: Pty,
|
||||
const TIOCSCTTY = if (builtin.os.tag == .macos) 536900705 else c.TIOCSCTTY;
|
||||
const TIOCSWINSZ = if (builtin.os.tag == .macos) 2148037735 else c.TIOCSWINSZ;
|
||||
const TIOCGWINSZ = if (builtin.os.tag == .macos) 1074295912 else c.TIOCGWINSZ;
|
||||
extern "c" fn setsid() std.c.pid_t;
|
||||
const c = struct {
|
||||
usingnamespace switch (builtin.os.tag) {
|
||||
.macos => @cImport({
|
||||
@cInclude("sys/ioctl.h"); // ioctl and constants
|
||||
@cInclude("util.h"); // openpty()
|
||||
}),
|
||||
else => @cImport({
|
||||
@cInclude("sys/ioctl.h"); // ioctl and constants
|
||||
@cInclude("pty.h");
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
pub fn spawn(self: *Command, allocator: std.mem.Allocator) !void {
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(allocator);
|
||||
|
@ -49,12 +33,10 @@ pub fn spawn(self: *Command, allocator: std.mem.Allocator) !void {
|
|||
if (pid == 0) {
|
||||
// we are the child
|
||||
_ = std.os.linux.setsid();
|
||||
_ = setsid();
|
||||
// if (setsid() < 0) return error.ProcessGroupFailed;
|
||||
|
||||
// set the controlling terminal
|
||||
var u: c_uint = std.posix.STDIN_FILENO;
|
||||
if (c.ioctl(self.pty.tty, TIOCSCTTY, @intFromPtr(&u)) != 0) return error.IoctlError;
|
||||
if (posix.system.ioctl(self.pty.tty, posix.T.IOCSCTTY, @intFromPtr(&u)) != 0) return error.IoctlError;
|
||||
|
||||
// set up io
|
||||
try posix.dup2(self.pty.tty, std.posix.STDIN_FILENO);
|
||||
|
@ -71,10 +53,6 @@ pub fn spawn(self: *Command, allocator: std.mem.Allocator) !void {
|
|||
// exec
|
||||
const err = std.posix.execvpeZ(argv_buf.ptr[0].?, argv_buf.ptr, envp);
|
||||
_ = err catch {};
|
||||
} else {
|
||||
std.debug.print("we are not child: {d}\n", .{pid});
|
||||
// posix.close(self.pty.tty);
|
||||
_ = posix.waitpid(pid, 0);
|
||||
}
|
||||
|
||||
// we are the parent
|
||||
|
|
|
@ -7,25 +7,6 @@ const Winsize = @import("../../main.zig").Winsize;
|
|||
|
||||
const posix = std.posix;
|
||||
|
||||
extern "c" fn setsid() std.c.pid_t;
|
||||
|
||||
const c = struct {
|
||||
usingnamespace switch (builtin.os.tag) {
|
||||
.macos => @cImport({
|
||||
@cInclude("sys/ioctl.h"); // ioctl and constants
|
||||
@cInclude("util.h"); // openpty()
|
||||
}),
|
||||
else => @cImport({
|
||||
@cInclude("sys/ioctl.h"); // ioctl and constants
|
||||
@cInclude("pty.h");
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
const TIOCSCTTY = if (builtin.os.tag == .macos) 536900705 else c.TIOCSCTTY;
|
||||
const TIOCSWINSZ = if (builtin.os.tag == .macos) 2148037735 else c.TIOCSWINSZ;
|
||||
const TIOCGWINSZ = if (builtin.os.tag == .macos) 1074295912 else c.TIOCGWINSZ;
|
||||
|
||||
pty: posix.fd_t,
|
||||
tty: posix.fd_t,
|
||||
|
||||
|
@ -33,7 +14,6 @@ tty: posix.fd_t,
|
|||
pub fn init() !Pty {
|
||||
switch (builtin.os.tag) {
|
||||
.linux => return openPtyLinux(),
|
||||
.macos => return openPtyMacos2(),
|
||||
else => @compileError("unsupported os"),
|
||||
}
|
||||
}
|
||||
|
@ -52,75 +32,10 @@ pub fn setSize(self: Pty, ws: Winsize) !void {
|
|||
.ws_xpixel = @truncate(ws.x_pixel),
|
||||
.ws_ypixel = @truncate(ws.y_pixel),
|
||||
};
|
||||
if (c.ioctl(self.pty, TIOCSWINSZ, @intFromPtr(&_ws)) != 0)
|
||||
if (posix.system.ioctl(self.pty, posix.T.IOCSWINSZ, @intFromPtr(&_ws)) != 0)
|
||||
return error.SetWinsizeError;
|
||||
}
|
||||
|
||||
fn openPtyMacos2() !Pty {
|
||||
const p = try posix.open("/dev/ptmx", .{ .ACCMODE = .RDWR, .NOCTTY = true }, 0);
|
||||
errdefer posix.close(p);
|
||||
|
||||
// unlockpt
|
||||
// var n: c_uint = 0;
|
||||
if (c.ioctl(p, c.TIOCPTYUNLK) != 0) return error.IoctlError;
|
||||
|
||||
// ptsname
|
||||
if (c.ioctl(p, c.TIOCPTYGRANT) != 0) return error.IoctlError;
|
||||
|
||||
var buf: [128]u8 = undefined;
|
||||
// var buf2: [128]u8 = undefined;
|
||||
if (c.ioctl(p, c.TIOCPTYGNAME, &buf) != 0) return error.IoctlError;
|
||||
const sname = buf[0 .. 13 - 1];
|
||||
// std.debug.print("sizeof buf: {d}", .{buf.len});
|
||||
// const sname = try std.fmt.bufPrint(&buf2, "{s}", .{buf});
|
||||
std.debug.print("slave name: {s}\n", .{sname});
|
||||
// const sname = try std.fmt.bufPrint(&buf, "/dev/pts/{d}", .{n});
|
||||
// std.log.err("pts: {s}", .{sname});
|
||||
|
||||
const t = try posix.open(sname, .{ .ACCMODE = .RDWR, .NOCTTY = true }, 0);
|
||||
std.debug.print("posix opened : {s}\n", .{sname});
|
||||
|
||||
var attrs: c.termios = undefined;
|
||||
if (c.tcgetattr(p, &attrs) != 0)
|
||||
return error.OpenptyFailed;
|
||||
attrs.c_iflag |= c.IUTF8;
|
||||
if (c.tcsetattr(p, c.TCSANOW, &attrs) != 0)
|
||||
return error.OpenptyFailed;
|
||||
|
||||
return .{
|
||||
.pty = p,
|
||||
.tty = t,
|
||||
};
|
||||
}
|
||||
|
||||
fn openPtyMacos() !Pty {
|
||||
var master_fd: posix.fd_t = undefined;
|
||||
var slave_fd: posix.fd_t = undefined;
|
||||
if (c.openpty(
|
||||
&master_fd,
|
||||
&slave_fd,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
) < 0)
|
||||
return error.OpenptyFailed;
|
||||
errdefer {
|
||||
_ = posix.system.close(master_fd);
|
||||
_ = posix.system.close(slave_fd);
|
||||
}
|
||||
var attrs: c.termios = undefined;
|
||||
if (c.tcgetattr(master_fd, &attrs) != 0)
|
||||
return error.OpenptyFailed;
|
||||
attrs.c_iflag |= c.IUTF8;
|
||||
if (c.tcsetattr(master_fd, c.TCSANOW, &attrs) != 0)
|
||||
return error.OpenptyFailed;
|
||||
|
||||
return .{
|
||||
.pty = master_fd,
|
||||
.tty = slave_fd,
|
||||
};
|
||||
}
|
||||
|
||||
fn openPtyLinux() !Pty {
|
||||
const p = try posix.open("/dev/ptmx", .{ .ACCMODE = .RDWR, .NOCTTY = true }, 0);
|
||||
errdefer posix.close(p);
|
||||
|
|
|
@ -96,7 +96,6 @@ pub fn init(
|
|||
) !Terminal {
|
||||
const pty = try Pty.init();
|
||||
try pty.setSize(opts.winsize);
|
||||
std.debug.print("set size done\n", .{});
|
||||
const cmd: Command = .{
|
||||
.argv = argv,
|
||||
.env_map = env,
|
||||
|
@ -156,7 +155,6 @@ pub fn deinit(self: *Terminal) void {
|
|||
}
|
||||
|
||||
pub fn spawn(self: *Terminal) !void {
|
||||
std.debug.print("inside spawn", .{});
|
||||
if (self.thread != null) return;
|
||||
self.back_screen = &self.back_screen_pri;
|
||||
|
||||
|
@ -261,7 +259,6 @@ fn anyReader(self: *const Terminal) std.io.AnyReader {
|
|||
|
||||
/// process the output from the command on the pty
|
||||
fn run(self: *Terminal) !void {
|
||||
std.debug.print("inside run\n", .{});
|
||||
var parser: Parser = .{
|
||||
.buf = try std.ArrayList(u8).initCapacity(self.allocator, 128),
|
||||
};
|
||||
|
@ -271,7 +268,6 @@ fn run(self: *Terminal) !void {
|
|||
var reader = std.io.bufferedReader(self.anyReader());
|
||||
|
||||
while (!self.should_quit) {
|
||||
std.debug.print("inside while loop\n", .{});
|
||||
const event = try parser.parseReader(&reader);
|
||||
self.back_mutex.lock();
|
||||
defer self.back_mutex.unlock();
|
||||
|
@ -279,10 +275,8 @@ fn run(self: *Terminal) !void {
|
|||
if (!self.dirty and self.event_queue.tryPush(.redraw))
|
||||
self.dirty = true;
|
||||
|
||||
std.debug.print("before switch event\n", .{});
|
||||
switch (event) {
|
||||
.print => |str| {
|
||||
std.debug.print("inside print event\n", .{});
|
||||
var iter = grapheme.Iterator.init(str, &self.unicode.grapheme_data);
|
||||
while (iter.next()) |g| {
|
||||
const gr = g.bytes(str);
|
||||
|
@ -293,7 +287,6 @@ fn run(self: *Terminal) !void {
|
|||
},
|
||||
.c0 => |b| try self.handleC0(b),
|
||||
.escape => |esc| {
|
||||
std.debug.print("inside escape event\n", .{});
|
||||
const final = esc[esc.len - 1];
|
||||
switch (final) {
|
||||
'B' => {}, // TODO: handle charsets
|
||||
|
@ -707,7 +700,6 @@ fn run(self: *Terminal) !void {
|
|||
}
|
||||
|
||||
inline fn handleC0(self: *Terminal, b: ansi.C0) !void {
|
||||
std.debug.print("inside handlec0\n", .{});
|
||||
switch (b) {
|
||||
.NUL, .SOH, .STX => {},
|
||||
.EOT => {}, // we send EOT to quit the read thread
|
||||
|
@ -724,7 +716,6 @@ inline fn handleC0(self: *Terminal, b: ansi.C0) !void {
|
|||
}
|
||||
|
||||
pub fn setMode(self: *Terminal, mode: u16, val: bool) void {
|
||||
std.debug.print("inside setmode\n", .{});
|
||||
switch (mode) {
|
||||
7 => self.mode.autowrap = val,
|
||||
25 => self.mode.cursor = val,
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
info(loop): pixel mouse capability detected
|
||||
info(loop): unicode capability detected
|
||||
info(loop): color_scheme_updates capability detected
|
||||
info(loop): kitty keyboard capability detected
|
||||
info(loop): kitty graphics capability detected
|
|
@ -379,8 +379,8 @@ pub fn nextEvent(self: *Tty) !Event {
|
|||
return windows.unexpectedError(windows.kernel32.GetLastError());
|
||||
}
|
||||
const window_rect = console_info.srWindow;
|
||||
const width = window_rect.Right - window_rect.Left + 1;
|
||||
const height = window_rect.Bottom - window_rect.Top + 1;
|
||||
const width = window_rect.Right - window_rect.Left;
|
||||
const height = window_rect.Bottom - window_rect.Top;
|
||||
return .{
|
||||
.winsize = .{
|
||||
.cols = @intCast(width),
|
||||
|
|
Loading…
Reference in a new issue