aio: update to latest, windows, code reuse
Update to latest aio, which has minor changes such as the thread pool argument and special aio.ReadTty operation. In future the aio.ReadTty might have option to translate to vt escape sequences, but for vaxis it will use the direct mode. I was not really able to test the windows at all actually as wine did not seem to play nice with any vaxis example, but it compiles and ... runs?
This commit is contained in:
parent
9c2d18d5a2
commit
9b78bb8a78
5 changed files with 464 additions and 513 deletions
|
@ -23,8 +23,8 @@
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
.aio = .{
|
.aio = .{
|
||||||
.url = "git+https://github.com/Cloudef/zig-aio#be8e2b374bf223202090e282447fa4581029c2eb",
|
.url = "git+https://github.com/Cloudef/zig-aio#407bb416136b61087cec2c561fa4b4103a44c5b1",
|
||||||
.hash = "122012a11b37a350395a32fdb514e57ff54a0f9d8d4ce09498b6c45ffb7211232920",
|
.hash = "12202405ca6dd40f314dba6472983fcbb388118ab7446d75065b1efb982d03f515d2",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,7 +41,7 @@ fn audioTask(allocator: std.mem.Allocator) !void {
|
||||||
|
|
||||||
const sound = blk: {
|
const sound = blk: {
|
||||||
var tpool: coro.ThreadPool = .{};
|
var tpool: coro.ThreadPool = .{};
|
||||||
try tpool.start(allocator, 1);
|
try tpool.start(allocator, .{});
|
||||||
defer tpool.deinit();
|
defer tpool.deinit();
|
||||||
break :blk try tpool.yieldForCompletition(downloadTask, .{ allocator, "https://keroserene.net/lol/roll.s16" });
|
break :blk try tpool.yieldForCompletition(downloadTask, .{ allocator, "https://keroserene.net/lol/roll.s16" });
|
||||||
};
|
};
|
||||||
|
|
124
src/Loop.zig
124
src/Loop.zig
|
@ -8,6 +8,7 @@ const Parser = @import("Parser.zig");
|
||||||
const Queue = @import("queue.zig").Queue;
|
const Queue = @import("queue.zig").Queue;
|
||||||
const Tty = @import("main.zig").Tty;
|
const Tty = @import("main.zig").Tty;
|
||||||
const Vaxis = @import("Vaxis.zig");
|
const Vaxis = @import("Vaxis.zig");
|
||||||
|
const log = std.log.scoped(.loop);
|
||||||
|
|
||||||
pub fn Loop(comptime T: type) type {
|
pub fn Loop(comptime T: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
|
@ -15,8 +16,6 @@ pub fn Loop(comptime T: type) type {
|
||||||
|
|
||||||
const Event = T;
|
const Event = T;
|
||||||
|
|
||||||
const log = std.log.scoped(.loop);
|
|
||||||
|
|
||||||
tty: *Tty,
|
tty: *Tty,
|
||||||
vaxis: *Vaxis,
|
vaxis: *Vaxis,
|
||||||
|
|
||||||
|
@ -110,38 +109,7 @@ pub fn Loop(comptime T: type) type {
|
||||||
.windows => {
|
.windows => {
|
||||||
while (!self.should_quit) {
|
while (!self.should_quit) {
|
||||||
const event = try self.tty.nextEvent();
|
const event = try self.tty.nextEvent();
|
||||||
switch (event) {
|
try handleEventGeneric(self, self.vaxis, &cache, Event, event, null);
|
||||||
.winsize => |ws| {
|
|
||||||
if (@hasField(Event, "winsize")) {
|
|
||||||
self.postEvent(.{ .winsize = ws });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
self.postEvent(.{ .key_release = mut_key });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.cap_da1 => {
|
|
||||||
std.Thread.Futex.wake(&self.vaxis.query_futex, 10);
|
|
||||||
},
|
|
||||||
.mouse => {}, // Unsupported currently
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
|
@ -178,7 +146,24 @@ pub fn Loop(comptime T: type) type {
|
||||||
seq_start += result.n;
|
seq_start += result.n;
|
||||||
|
|
||||||
const event = result.event orelse continue;
|
const event = result.event orelse continue;
|
||||||
|
try handleEventGeneric(self, self.vaxis, &cache, Event, event, paste_allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handleEventGeneric(self: anytype, vx: *Vaxis, cache: *GraphemeCache, Event: type, event: anytype, paste_allocator: ?std.mem.Allocator) !void {
|
||||||
|
switch (builtin.os.tag) {
|
||||||
|
.windows => {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
.winsize => |ws| {
|
||||||
|
if (@hasField(Event, "winsize")) {
|
||||||
|
return self.postEvent(.{ .winsize = ws });
|
||||||
|
}
|
||||||
|
},
|
||||||
.key_press => |key| {
|
.key_press => |key| {
|
||||||
if (@hasField(Event, "key_press")) {
|
if (@hasField(Event, "key_press")) {
|
||||||
// HACK: yuck. there has to be a better way
|
// HACK: yuck. there has to be a better way
|
||||||
|
@ -186,7 +171,7 @@ pub fn Loop(comptime T: type) type {
|
||||||
if (key.text) |text| {
|
if (key.text) |text| {
|
||||||
mut_key.text = cache.put(text);
|
mut_key.text = cache.put(text);
|
||||||
}
|
}
|
||||||
self.postEvent(.{ .key_press = mut_key });
|
return self.postEvent(.{ .key_press = mut_key });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.key_release => |*key| {
|
.key_release => |*key| {
|
||||||
|
@ -196,37 +181,66 @@ pub fn Loop(comptime T: type) type {
|
||||||
if (key.text) |text| {
|
if (key.text) |text| {
|
||||||
mut_key.text = cache.put(text);
|
mut_key.text = cache.put(text);
|
||||||
}
|
}
|
||||||
self.postEvent(.{ .key_release = mut_key });
|
return self.postEvent(.{ .key_release = mut_key });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.cap_da1 => {
|
||||||
|
std.Thread.Futex.wake(&vx.query_futex, 10);
|
||||||
|
},
|
||||||
|
.mouse => {}, // Unsupported currently
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return 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);
|
||||||
|
}
|
||||||
|
return self.postEvent(.{ .key_release = mut_key });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.mouse => |mouse| {
|
.mouse => |mouse| {
|
||||||
if (@hasField(Event, "mouse")) {
|
if (@hasField(Event, "mouse")) {
|
||||||
self.postEvent(.{ .mouse = self.vaxis.translateMouse(mouse) });
|
return self.postEvent(.{ .mouse = vx.translateMouse(mouse) });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.focus_in => {
|
.focus_in => {
|
||||||
if (@hasField(Event, "focus_in")) {
|
if (@hasField(Event, "focus_in")) {
|
||||||
self.postEvent(.focus_in);
|
return self.postEvent(.focus_in);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.focus_out => {
|
.focus_out => {
|
||||||
if (@hasField(Event, "focus_out")) {
|
if (@hasField(Event, "focus_out")) {
|
||||||
self.postEvent(.focus_out);
|
return self.postEvent(.focus_out);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.paste_start => {
|
.paste_start => {
|
||||||
if (@hasField(Event, "paste_start")) {
|
if (@hasField(Event, "paste_start")) {
|
||||||
self.postEvent(.paste_start);
|
return self.postEvent(.paste_start);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.paste_end => {
|
.paste_end => {
|
||||||
if (@hasField(Event, "paste_end")) {
|
if (@hasField(Event, "paste_end")) {
|
||||||
self.postEvent(.paste_end);
|
return self.postEvent(.paste_end);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.paste => |text| {
|
.paste => |text| {
|
||||||
if (@hasField(Event, "paste")) {
|
if (@hasField(Event, "paste")) {
|
||||||
self.postEvent(.{ .paste = text });
|
return self.postEvent(.{ .paste = text });
|
||||||
} else {
|
} else {
|
||||||
if (paste_allocator) |_|
|
if (paste_allocator) |_|
|
||||||
paste_allocator.?.free(text);
|
paste_allocator.?.free(text);
|
||||||
|
@ -234,50 +248,46 @@ pub fn Loop(comptime T: type) type {
|
||||||
},
|
},
|
||||||
.color_report => |report| {
|
.color_report => |report| {
|
||||||
if (@hasField(Event, "color_report")) {
|
if (@hasField(Event, "color_report")) {
|
||||||
self.postEvent(.{ .color_report = report });
|
return self.postEvent(.{ .color_report = report });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.color_scheme => |scheme| {
|
.color_scheme => |scheme| {
|
||||||
if (@hasField(Event, "color_scheme")) {
|
if (@hasField(Event, "color_scheme")) {
|
||||||
self.postEvent(.{ .color_scheme = scheme });
|
return self.postEvent(.{ .color_scheme = scheme });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.cap_kitty_keyboard => {
|
.cap_kitty_keyboard => {
|
||||||
log.info("kitty keyboard capability detected", .{});
|
log.info("kitty keyboard capability detected", .{});
|
||||||
self.vaxis.caps.kitty_keyboard = true;
|
vx.caps.kitty_keyboard = true;
|
||||||
},
|
},
|
||||||
.cap_kitty_graphics => {
|
.cap_kitty_graphics => {
|
||||||
if (!self.vaxis.caps.kitty_graphics) {
|
if (!vx.caps.kitty_graphics) {
|
||||||
log.info("kitty graphics capability detected", .{});
|
log.info("kitty graphics capability detected", .{});
|
||||||
self.vaxis.caps.kitty_graphics = true;
|
vx.caps.kitty_graphics = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.cap_rgb => {
|
.cap_rgb => {
|
||||||
log.info("rgb capability detected", .{});
|
log.info("rgb capability detected", .{});
|
||||||
self.vaxis.caps.rgb = true;
|
vx.caps.rgb = true;
|
||||||
},
|
},
|
||||||
.cap_unicode => {
|
.cap_unicode => {
|
||||||
log.info("unicode capability detected", .{});
|
log.info("unicode capability detected", .{});
|
||||||
self.vaxis.caps.unicode = .unicode;
|
vx.caps.unicode = .unicode;
|
||||||
self.vaxis.screen.width_method = .unicode;
|
vx.screen.width_method = .unicode;
|
||||||
},
|
},
|
||||||
.cap_sgr_pixels => {
|
.cap_sgr_pixels => {
|
||||||
log.info("pixel mouse capability detected", .{});
|
log.info("pixel mouse capability detected", .{});
|
||||||
self.vaxis.caps.sgr_pixels = true;
|
vx.caps.sgr_pixels = true;
|
||||||
},
|
},
|
||||||
.cap_color_scheme_updates => {
|
.cap_color_scheme_updates => {
|
||||||
log.info("color_scheme_updates capability detected", .{});
|
log.info("color_scheme_updates capability detected", .{});
|
||||||
self.vaxis.caps.color_scheme_updates = true;
|
vx.caps.color_scheme_updates = true;
|
||||||
},
|
},
|
||||||
.cap_da1 => {
|
.cap_da1 => {
|
||||||
std.Thread.Futex.wake(&self.vaxis.query_futex, 10);
|
std.Thread.Futex.wake(&vx.query_futex, 10);
|
||||||
},
|
},
|
||||||
.winsize => unreachable, // handled elsewhere for posix
|
.winsize => unreachable, // handled elsewhere for posix
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
140
src/aio.zig
140
src/aio.zig
|
@ -3,14 +3,9 @@ const std = @import("std");
|
||||||
const aio = @import("aio");
|
const aio = @import("aio");
|
||||||
const coro = @import("coro");
|
const coro = @import("coro");
|
||||||
const vaxis = @import("main.zig");
|
const vaxis = @import("main.zig");
|
||||||
|
const handleEventGeneric = @import("Loop.zig").handleEventGeneric;
|
||||||
const log = std.log.scoped(.vaxis_aio);
|
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 };
|
const Yield = enum { no_state, took_event };
|
||||||
|
|
||||||
/// zig-aio based event loop
|
/// zig-aio based event loop
|
||||||
|
@ -52,10 +47,12 @@ pub fn Loop(comptime T: type) type {
|
||||||
|
|
||||||
// keep on stack
|
// keep on stack
|
||||||
var ctx: Context = .{ .loop = self, .tty = tty };
|
var ctx: Context = .{ .loop = self, .tty = tty };
|
||||||
|
if (builtin.target.os.tag != .windows) {
|
||||||
if (@hasField(Event, "winsize")) {
|
if (@hasField(Event, "winsize")) {
|
||||||
const handler: vaxis.Tty.SignalHandler = .{ .context = &ctx, .callback = Context.cb };
|
const handler: vaxis.Tty.SignalHandler = .{ .context = &ctx, .callback = Context.cb };
|
||||||
try vaxis.Tty.notifyWinsize(handler);
|
try vaxis.Tty.notifyWinsize(handler);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try coro.io.single(aio.WaitEventSource{ .source = &self.source });
|
try coro.io.single(aio.WaitEventSource{ .source = &self.source });
|
||||||
|
@ -74,7 +71,32 @@ pub fn Loop(comptime T: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ttyReaderInner(self: *@This(), vx: *vaxis.Vaxis, tty: *vaxis.Tty, paste_allocator: ?std.mem.Allocator) !void {
|
fn windowsReadEvent(tty: *vaxis.Tty) !vaxis.Event {
|
||||||
|
var state: vaxis.Tty.EventState = .{};
|
||||||
|
while (true) {
|
||||||
|
var bytes_read: usize = 0;
|
||||||
|
var input_record: vaxis.Tty.INPUT_RECORD = undefined;
|
||||||
|
try coro.io.single(aio.ReadTty{
|
||||||
|
.tty = .{ .handle = tty.stdin },
|
||||||
|
.buffer = std.mem.asBytes(&input_record),
|
||||||
|
.out_read = &bytes_read,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (try tty.eventFromRecord(&input_record, &state)) |ev| {
|
||||||
|
return ev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ttyReaderWindows(self: *@This(), vx: *vaxis.Vaxis, tty: *vaxis.Tty) !void {
|
||||||
|
var cache: vaxis.GraphemeCache = .{};
|
||||||
|
while (true) {
|
||||||
|
const event = try windowsReadEvent(tty);
|
||||||
|
try handleEventGeneric(self, vx, &cache, Event, event, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ttyReaderPosix(self: *@This(), vx: *vaxis.Vaxis, tty: *vaxis.Tty, paste_allocator: ?std.mem.Allocator) !void {
|
||||||
// initialize a grapheme cache
|
// initialize a grapheme cache
|
||||||
var cache: vaxis.GraphemeCache = .{};
|
var cache: vaxis.GraphemeCache = .{};
|
||||||
|
|
||||||
|
@ -93,7 +115,7 @@ pub fn Loop(comptime T: type) type {
|
||||||
var buf: [4096]u8 = undefined;
|
var buf: [4096]u8 = undefined;
|
||||||
var n: usize = undefined;
|
var n: usize = undefined;
|
||||||
var read_start: usize = 0;
|
var read_start: usize = 0;
|
||||||
try coro.io.single(aio.Read{ .file = file, .buffer = buf[read_start..], .out_read = &n });
|
try coro.io.single(aio.ReadTty{ .tty = file, .buffer = buf[read_start..], .out_read = &n });
|
||||||
var seq_start: usize = 0;
|
var seq_start: usize = 0;
|
||||||
while (seq_start < n) {
|
while (seq_start < n) {
|
||||||
const result = try parser.parse(buf[seq_start..n], paste_allocator);
|
const result = try parser.parse(buf[seq_start..n], paste_allocator);
|
||||||
|
@ -111,108 +133,16 @@ pub fn Loop(comptime T: type) type {
|
||||||
seq_start += result.n;
|
seq_start += result.n;
|
||||||
|
|
||||||
const event = result.event orelse continue;
|
const event = result.event orelse continue;
|
||||||
switch (event) {
|
try handleEventGeneric(self, vx, &cache, Event, event, paste_allocator);
|
||||||
.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 {
|
fn ttyReaderTask(self: *@This(), vx: *vaxis.Vaxis, tty: *vaxis.Tty, paste_allocator: ?std.mem.Allocator) void {
|
||||||
self.ttyReaderInner(vx, tty, paste_allocator) catch |err| {
|
return switch (builtin.target.os.tag) {
|
||||||
|
.windows => self.ttyReaderWindows(vx, tty),
|
||||||
|
else => self.ttyReaderPosix(vx, tty, paste_allocator),
|
||||||
|
} catch |err| {
|
||||||
if (err != error.Canceled) log.err("ttyReader: {}", .{err});
|
if (err != error.Canceled) log.err("ttyReader: {}", .{err});
|
||||||
self.fatal = true;
|
self.fatal = true;
|
||||||
};
|
};
|
||||||
|
|
|
@ -119,33 +119,44 @@ pub fn bufferedWriter(self: *const Tty) std.io.BufferedWriter(4096, std.io.AnyWr
|
||||||
|
|
||||||
pub fn nextEvent(self: *Tty) !Event {
|
pub fn nextEvent(self: *Tty) !Event {
|
||||||
// We use a loop so we can ignore certain events
|
// We use a loop so we can ignore certain events
|
||||||
var ansi_buf: [128]u8 = undefined;
|
var state: EventState = .{};
|
||||||
var ansi_idx: usize = 0;
|
|
||||||
var escape_st: bool = false;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var event_count: u32 = 0;
|
var event_count: u32 = 0;
|
||||||
var input_record: INPUT_RECORD = undefined;
|
var input_record: INPUT_RECORD = undefined;
|
||||||
if (ReadConsoleInputW(self.stdin, &input_record, 1, &event_count) == 0)
|
if (ReadConsoleInputW(self.stdin, &input_record, 1, &event_count) == 0)
|
||||||
return windows.unexpectedError(windows.kernel32.GetLastError());
|
return windows.unexpectedError(windows.kernel32.GetLastError());
|
||||||
|
|
||||||
switch (input_record.EventType) {
|
if (try self.eventFromRecord(&input_record, &state)) |ev| {
|
||||||
|
return ev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const EventState = struct {
|
||||||
|
ansi_buf: [128]u8 = undefined,
|
||||||
|
ansi_idx: usize = 0,
|
||||||
|
escape_st: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn eventFromRecord(self: *Tty, record: *const INPUT_RECORD, state: *EventState) !?Event {
|
||||||
|
switch (record.EventType) {
|
||||||
0x0001 => { // Key event
|
0x0001 => { // Key event
|
||||||
const event = input_record.Event.KeyEvent;
|
const event = record.Event.KeyEvent;
|
||||||
|
|
||||||
const base_layout: u21 = switch (event.wVirtualKeyCode) {
|
const base_layout: u21 = switch (event.wVirtualKeyCode) {
|
||||||
0x00 => { // delivered when we get an escape sequence
|
0x00 => { // delivered when we get an escape sequence
|
||||||
ansi_buf[ansi_idx] = event.uChar.AsciiChar;
|
state.ansi_buf[state.ansi_idx] = event.uChar.AsciiChar;
|
||||||
ansi_idx += 1;
|
state.ansi_idx += 1;
|
||||||
if (ansi_idx <= 2) {
|
if (state.ansi_idx <= 2) {
|
||||||
continue;
|
return null;
|
||||||
}
|
}
|
||||||
switch (ansi_buf[1]) {
|
switch (state.ansi_buf[1]) {
|
||||||
'[' => { // CSI, read until 0x40 to 0xFF
|
'[' => { // CSI, read until 0x40 to 0xFF
|
||||||
switch (event.uChar.AsciiChar) {
|
switch (event.uChar.AsciiChar) {
|
||||||
0x40...0xFF => {
|
0x40...0xFF => {
|
||||||
return .cap_da1;
|
return .cap_da1;
|
||||||
},
|
},
|
||||||
else => continue,
|
else => return null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
']' => { // OSC, read until ESC \ or BEL
|
']' => { // OSC, read until ESC \ or BEL
|
||||||
|
@ -154,19 +165,19 @@ pub fn nextEvent(self: *Tty) !Event {
|
||||||
return .cap_da1;
|
return .cap_da1;
|
||||||
},
|
},
|
||||||
0x1B => {
|
0x1B => {
|
||||||
escape_st = true;
|
state.escape_st = true;
|
||||||
continue;
|
return null;
|
||||||
},
|
},
|
||||||
'\\' => {
|
'\\' => {
|
||||||
if (escape_st) {
|
if (state.escape_st) {
|
||||||
return .cap_da1;
|
return .cap_da1;
|
||||||
}
|
}
|
||||||
continue;
|
return null;
|
||||||
},
|
},
|
||||||
else => continue,
|
else => return null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => continue,
|
else => return null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
0x08 => Key.backspace,
|
0x08 => Key.backspace,
|
||||||
|
@ -257,7 +268,7 @@ pub fn nextEvent(self: *Tty) !Event {
|
||||||
0xdc => '\\',
|
0xdc => '\\',
|
||||||
0xdd => ']',
|
0xdd => ']',
|
||||||
0xde => '\'',
|
0xde => '\'',
|
||||||
else => continue,
|
else => return null,
|
||||||
};
|
};
|
||||||
|
|
||||||
var codepoint: u21 = base_layout;
|
var codepoint: u21 = base_layout;
|
||||||
|
@ -286,7 +297,7 @@ pub fn nextEvent(self: *Tty) !Event {
|
||||||
0x0002 => { // Mouse event
|
0x0002 => { // Mouse event
|
||||||
// see https://learn.microsoft.com/en-us/windows/console/mouse-event-record-str
|
// see https://learn.microsoft.com/en-us/windows/console/mouse-event-record-str
|
||||||
|
|
||||||
const event = input_record.Event.MouseEvent;
|
const event = record.Event.MouseEvent;
|
||||||
|
|
||||||
// High word of dwButtonState represents mouse wheel. Positive is wheel_up, negative
|
// High word of dwButtonState represents mouse wheel. Positive is wheel_up, negative
|
||||||
// is wheel_down
|
// is wheel_down
|
||||||
|
@ -349,7 +360,7 @@ pub fn nextEvent(self: *Tty) !Event {
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
std.log.warn("unknown mouse event: {}", .{event});
|
std.log.warn("unknown mouse event: {}", .{event});
|
||||||
continue;
|
return null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -391,14 +402,14 @@ pub fn nextEvent(self: *Tty) !Event {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
0x0010 => { // Focus events
|
0x0010 => { // Focus events
|
||||||
switch (input_record.Event.FocusEvent.bSetFocus) {
|
switch (record.Event.FocusEvent.bSetFocus) {
|
||||||
0 => return .focus_out,
|
0 => return .focus_out,
|
||||||
else => return .focus_in,
|
else => return .focus_in,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translateMods(mods: u32) Key.Modifiers {
|
fn translateMods(mods: u32) Key.Modifiers {
|
||||||
|
|
Loading…
Reference in a new issue