mod(read_input): read user input from tty
This commit is contained in:
@@ -29,6 +29,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
.target = target,
|
.target = target,
|
||||||
});
|
});
|
||||||
|
const zg_dep = b.dependency("zg", .{});
|
||||||
|
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = "tui-website",
|
.name = "tui-website",
|
||||||
@@ -39,6 +40,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
exe.root_module.addImport("vaxis", vaxis_dep.module("vaxis"));
|
exe.root_module.addImport("vaxis", vaxis_dep.module("vaxis"));
|
||||||
exe.root_module.addImport("zlog", zlog_dep.module("zlog"));
|
exe.root_module.addImport("zlog", zlog_dep.module("zlog"));
|
||||||
exe.root_module.addImport("zmd", zmd_dep.module("zmd"));
|
exe.root_module.addImport("zmd", zmd_dep.module("zmd"));
|
||||||
|
exe.root_module.addImport("code_point", zg_dep.module("code_point"));
|
||||||
|
|
||||||
// This declares intent for the executable to be installed into the
|
// This declares intent for the executable to be installed into the
|
||||||
// standard location when the user invokes the "install" step (the default
|
// standard location when the user invokes the "install" step (the default
|
||||||
|
|||||||
@@ -35,6 +35,10 @@
|
|||||||
.url = "git+https://github.com/jetzig-framework/zmd#90e52429cacd4fdc90fd615596fe584ae40ec8e9",
|
.url = "git+https://github.com/jetzig-framework/zmd#90e52429cacd4fdc90fd615596fe584ae40ec8e9",
|
||||||
.hash = "12202a4edefedd52478223a44cdc9a3b41d4bc5cf3670497a48377f45ff16a5e3363",
|
.hash = "12202a4edefedd52478223a44cdc9a3b41d4bc5cf3670497a48377f45ff16a5e3363",
|
||||||
},
|
},
|
||||||
|
.zg = .{
|
||||||
|
.url = "https://codeberg.org/dude_the_builder/zg/archive/v0.13.2.tar.gz",
|
||||||
|
.hash = "122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
.paths = .{
|
.paths = .{
|
||||||
"build.zig",
|
"build.zig",
|
||||||
|
|||||||
47
src/app.zig
47
src/app.zig
@@ -1,6 +1,7 @@
|
|||||||
//! Application type for TUI-applications
|
//! Application type for TUI-applications
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const terminal = @import("terminal.zig");
|
const terminal = @import("terminal.zig");
|
||||||
|
const code_point = terminal.code_point;
|
||||||
const mergeTaggedUnions = @import("event.zig").mergeTaggedUnions;
|
const mergeTaggedUnions = @import("event.zig").mergeTaggedUnions;
|
||||||
const isTaggedUnion = @import("event.zig").isTaggedUnion;
|
const isTaggedUnion = @import("event.zig").isTaggedUnion;
|
||||||
|
|
||||||
@@ -70,20 +71,42 @@ pub fn App(comptime E: type) type {
|
|||||||
const size = terminal.getTerminalSize();
|
const size = terminal.getTerminalSize();
|
||||||
this.postEvent(.{ .resize = size });
|
this.postEvent(.{ .resize = size });
|
||||||
// read input in loop
|
// read input in loop
|
||||||
const buf: [256]u8 = undefined;
|
var buf: [256]u8 = undefined;
|
||||||
_ = buf;
|
|
||||||
while (!this.quit) {
|
while (!this.quit) {
|
||||||
std.time.sleep(5 * std.time.ns_per_s);
|
// FIXME: here is a race-condition -> i.e. there could be events
|
||||||
break;
|
// in the queue, but they will not be executed because the main
|
||||||
// try terminal.read(buf[0..]);
|
// loop will close!
|
||||||
// TODO: send corresponding events with key_presses
|
const read_bytes = try terminal.read(buf[0..]);
|
||||||
// -> create corresponding event
|
// escape key presses
|
||||||
// -> handle key inputs (modifier, op codes, etc.)
|
if (buf[0] == 0x1b and read_bytes > 1) {
|
||||||
// -> I could take inspiration from `libvaxis` for this
|
switch (buf[1]) {
|
||||||
|
// TODO: parse corresponding codes
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const b = buf[0];
|
||||||
|
const key: terminal.Key = switch (b) {
|
||||||
|
0x00 => .{ .cp = '@', .mod = .{ .ctrl = true } },
|
||||||
|
0x08 => .{ .cp = terminal.Key.backspace },
|
||||||
|
0x09 => .{ .cp = terminal.Key.tab },
|
||||||
|
0x0a, 0x0d => .{ .cp = terminal.Key.enter },
|
||||||
|
0x01...0x07, 0x0b...0x0c, 0x0e...0x1a => .{ .cp = b + 0x60, .mod = .{ .ctrl = true } },
|
||||||
|
0x1b => escape: {
|
||||||
|
std.debug.assert(read_bytes == 1);
|
||||||
|
break :escape .{ .cp = terminal.Key.escape };
|
||||||
|
},
|
||||||
|
0x7f => .{ .cp = terminal.Key.backspace },
|
||||||
|
else => {
|
||||||
|
var iter = code_point.Iterator{ .bytes = buf[0..read_bytes] };
|
||||||
|
while (iter.next()) |cp| {
|
||||||
|
this.postEvent(.{ .key = .{ .cp = cp.code } });
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.postEvent(.{ .key = key });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// FIXME: here is a race-condition -> i.e. there could be events in
|
|
||||||
// the queue, but they will not be executed because the main loop
|
|
||||||
// will close!
|
|
||||||
this.postEvent(.quit);
|
this.postEvent(.quit);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const ApplicationEvent = union(enum) {
|
|||||||
// size has changed, etc.
|
// size has changed, etc.
|
||||||
const SystemEvent = union(enum) {
|
const SystemEvent = union(enum) {
|
||||||
resize: terminal.Size,
|
resize: terminal.Size,
|
||||||
// key_press: terminal.Key,
|
key: terminal.Key,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const BuiltinEvent = mergeTaggedUnions(SystemEvent, ApplicationEvent);
|
pub const BuiltinEvent = mergeTaggedUnions(SystemEvent, ApplicationEvent);
|
||||||
|
|||||||
@@ -50,6 +50,12 @@ pub fn main() !void {
|
|||||||
try terminal.setCursorPositionHome();
|
try terminal.setCursorPositionHome();
|
||||||
log.debug("Size := [x: {d}, y: {d}]", .{ size.cols, size.rows });
|
log.debug("Size := [x: {d}, y: {d}]", .{ size.cols, size.rows });
|
||||||
},
|
},
|
||||||
|
.key => |key| {
|
||||||
|
log.debug("received key: {any}", .{key});
|
||||||
|
if (terminal.Key.matches(key, .{ .cp = 'q' })) {
|
||||||
|
app.quit = true; // TODO: who should emit the .quit event?
|
||||||
|
}
|
||||||
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
const events = try layout.handle(event);
|
const events = try layout.handle(event);
|
||||||
|
|||||||
136
src/terminal.zig
136
src/terminal.zig
@@ -1,4 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
pub const code_point = @import("code_point");
|
||||||
|
|
||||||
const log = std.log.scoped(.terminal);
|
const log = std.log.scoped(.terminal);
|
||||||
|
|
||||||
@@ -12,6 +13,137 @@ pub const Position = struct {
|
|||||||
row: u16,
|
row: u16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Key = struct {
|
||||||
|
cp: u21,
|
||||||
|
mod: Modifier = .{},
|
||||||
|
|
||||||
|
pub fn matches(self: Key, other: Key) bool {
|
||||||
|
return std.meta.eql(self, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
// codepoints for keys
|
||||||
|
pub const tab: u21 = 0x09;
|
||||||
|
pub const enter: u21 = 0x0D;
|
||||||
|
pub const escape: u21 = 0x1B;
|
||||||
|
pub const space: u21 = 0x20;
|
||||||
|
pub const backspace: u21 = 0x7F;
|
||||||
|
|
||||||
|
// kitty key encodings (re-used here)
|
||||||
|
pub const insert: u21 = 57348;
|
||||||
|
pub const delete: u21 = 57349;
|
||||||
|
pub const left: u21 = 57350;
|
||||||
|
pub const right: u21 = 57351;
|
||||||
|
pub const up: u21 = 57352;
|
||||||
|
pub const down: u21 = 57353;
|
||||||
|
pub const page_up: u21 = 57354;
|
||||||
|
pub const page_down: u21 = 57355;
|
||||||
|
pub const home: u21 = 57356;
|
||||||
|
pub const end: u21 = 57357;
|
||||||
|
pub const caps_lock: u21 = 57358;
|
||||||
|
pub const scroll_lock: u21 = 57359;
|
||||||
|
pub const num_lock: u21 = 57360;
|
||||||
|
pub const print_screen: u21 = 57361;
|
||||||
|
pub const pause: u21 = 57362;
|
||||||
|
pub const menu: u21 = 57363;
|
||||||
|
pub const f1: u21 = 57364;
|
||||||
|
pub const f2: u21 = 57365;
|
||||||
|
pub const f3: u21 = 57366;
|
||||||
|
pub const f4: u21 = 57367;
|
||||||
|
pub const f5: u21 = 57368;
|
||||||
|
pub const f6: u21 = 57369;
|
||||||
|
pub const f7: u21 = 57370;
|
||||||
|
pub const f8: u21 = 57371;
|
||||||
|
pub const f9: u21 = 57372;
|
||||||
|
pub const f10: u21 = 57373;
|
||||||
|
pub const f11: u21 = 57374;
|
||||||
|
pub const f12: u21 = 57375;
|
||||||
|
pub const f13: u21 = 57376;
|
||||||
|
pub const f14: u21 = 57377;
|
||||||
|
pub const f15: u21 = 57378;
|
||||||
|
pub const @"f16": u21 = 57379;
|
||||||
|
pub const f17: u21 = 57380;
|
||||||
|
pub const f18: u21 = 57381;
|
||||||
|
pub const f19: u21 = 57382;
|
||||||
|
pub const f20: u21 = 57383;
|
||||||
|
pub const f21: u21 = 57384;
|
||||||
|
pub const f22: u21 = 57385;
|
||||||
|
pub const f23: u21 = 57386;
|
||||||
|
pub const f24: u21 = 57387;
|
||||||
|
pub const f25: u21 = 57388;
|
||||||
|
pub const f26: u21 = 57389;
|
||||||
|
pub const f27: u21 = 57390;
|
||||||
|
pub const f28: u21 = 57391;
|
||||||
|
pub const f29: u21 = 57392;
|
||||||
|
pub const f30: u21 = 57393;
|
||||||
|
pub const f31: u21 = 57394;
|
||||||
|
pub const @"f32": u21 = 57395;
|
||||||
|
pub const f33: u21 = 57396;
|
||||||
|
pub const f34: u21 = 57397;
|
||||||
|
pub const f35: u21 = 57398;
|
||||||
|
pub const kp_0: u21 = 57399;
|
||||||
|
pub const kp_1: u21 = 57400;
|
||||||
|
pub const kp_2: u21 = 57401;
|
||||||
|
pub const kp_3: u21 = 57402;
|
||||||
|
pub const kp_4: u21 = 57403;
|
||||||
|
pub const kp_5: u21 = 57404;
|
||||||
|
pub const kp_6: u21 = 57405;
|
||||||
|
pub const kp_7: u21 = 57406;
|
||||||
|
pub const kp_8: u21 = 57407;
|
||||||
|
pub const kp_9: u21 = 57408;
|
||||||
|
pub const kp_decimal: u21 = 57409;
|
||||||
|
pub const kp_divide: u21 = 57410;
|
||||||
|
pub const kp_multiply: u21 = 57411;
|
||||||
|
pub const kp_subtract: u21 = 57412;
|
||||||
|
pub const kp_add: u21 = 57413;
|
||||||
|
pub const kp_enter: u21 = 57414;
|
||||||
|
pub const kp_equal: u21 = 57415;
|
||||||
|
pub const kp_separator: u21 = 57416;
|
||||||
|
pub const kp_left: u21 = 57417;
|
||||||
|
pub const kp_right: u21 = 57418;
|
||||||
|
pub const kp_up: u21 = 57419;
|
||||||
|
pub const kp_down: u21 = 57420;
|
||||||
|
pub const kp_page_up: u21 = 57421;
|
||||||
|
pub const kp_page_down: u21 = 57422;
|
||||||
|
pub const kp_home: u21 = 57423;
|
||||||
|
pub const kp_end: u21 = 57424;
|
||||||
|
pub const kp_insert: u21 = 57425;
|
||||||
|
pub const kp_delete: u21 = 57426;
|
||||||
|
pub const kp_begin: u21 = 57427;
|
||||||
|
pub const media_play: u21 = 57428;
|
||||||
|
pub const media_pause: u21 = 57429;
|
||||||
|
pub const media_play_pause: u21 = 57430;
|
||||||
|
pub const media_reverse: u21 = 57431;
|
||||||
|
pub const media_stop: u21 = 57432;
|
||||||
|
pub const media_fast_forward: u21 = 57433;
|
||||||
|
pub const media_rewind: u21 = 57434;
|
||||||
|
pub const media_track_next: u21 = 57435;
|
||||||
|
pub const media_track_previous: u21 = 57436;
|
||||||
|
pub const media_record: u21 = 57437;
|
||||||
|
pub const lower_volume: u21 = 57438;
|
||||||
|
pub const raise_volume: u21 = 57439;
|
||||||
|
pub const mute_volume: u21 = 57440;
|
||||||
|
pub const left_shift: u21 = 57441;
|
||||||
|
pub const left_control: u21 = 57442;
|
||||||
|
pub const left_alt: u21 = 57443;
|
||||||
|
pub const left_super: u21 = 57444;
|
||||||
|
pub const left_hyper: u21 = 57445;
|
||||||
|
pub const left_meta: u21 = 57446;
|
||||||
|
pub const right_shift: u21 = 57447;
|
||||||
|
pub const right_control: u21 = 57448;
|
||||||
|
pub const right_alt: u21 = 57449;
|
||||||
|
pub const right_super: u21 = 57450;
|
||||||
|
pub const right_hyper: u21 = 57451;
|
||||||
|
pub const right_meta: u21 = 57452;
|
||||||
|
pub const iso_level_3_shift: u21 = 57453;
|
||||||
|
pub const iso_level_5_shift: u21 = 57454;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Modifier = struct {
|
||||||
|
shift: bool = false,
|
||||||
|
alt: bool = false,
|
||||||
|
ctrl: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
// Ref: https://vt100.net/docs/vt510-rm/DECRPM.html
|
// Ref: https://vt100.net/docs/vt510-rm/DECRPM.html
|
||||||
pub const ReportMode = enum {
|
pub const ReportMode = enum {
|
||||||
not_recognized,
|
not_recognized,
|
||||||
@@ -52,6 +184,10 @@ pub fn setCursorPositionHome() !void {
|
|||||||
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[H");
|
_ = try std.posix.write(std.posix.STDERR_FILENO, "\x1b[H");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read(buf: []u8) !usize {
|
||||||
|
return try std.posix.read(std.posix.STDIN_FILENO, buf);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getCursorPosition() !Position {
|
pub fn getCursorPosition() !Position {
|
||||||
// Needs Raw mode (no wait for \n) to work properly cause
|
// Needs Raw mode (no wait for \n) to work properly cause
|
||||||
// control sequence will not be written without it.
|
// control sequence will not be written without it.
|
||||||
|
|||||||
Reference in New Issue
Block a user