mod(tui): create own terminal interface framework
This commit is contained in:
90
src/app.zig
Normal file
90
src/app.zig
Normal file
@@ -0,0 +1,90 @@
|
||||
//! Application type for TUI-applications
|
||||
const std = @import("std");
|
||||
const terminal = @import("terminal.zig");
|
||||
const mergeTaggedUnions = @import("event.zig").mergeTaggedUnions;
|
||||
const isTaggedUnion = @import("event.zig").isTaggedUnion;
|
||||
|
||||
const BuiltinEvent = @import("event.zig").BuiltinEvent;
|
||||
const Queue = @import("queue.zig").Queue;
|
||||
|
||||
const log = std.log.scoped(.app);
|
||||
|
||||
// Create the App Type with the associated user events `E` which describes
|
||||
// an tagged union for all the user events that can be send through the
|
||||
// applications event loop.
|
||||
pub fn App(comptime E: type) type {
|
||||
if (!isTaggedUnion(E)) {
|
||||
@compileError("Provided user event `E` for `App(comptime E: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
return struct {
|
||||
pub const Event = mergeTaggedUnions(BuiltinEvent, E);
|
||||
pub const Layout = @import("layout.zig").Layout(Event);
|
||||
pub const Widget = @import("widget.zig").Widget(Event);
|
||||
|
||||
queue: Queue(Event, 256) = .{},
|
||||
thread: ?std.Thread = null,
|
||||
quit: bool = false,
|
||||
termios: ?std.posix.termios = null,
|
||||
|
||||
// TODO: event loop function?
|
||||
// layout handling?
|
||||
|
||||
pub fn init() @This() {
|
||||
return .{};
|
||||
}
|
||||
|
||||
pub fn start(this: *@This()) !void {
|
||||
if (this.thread) |_| return;
|
||||
|
||||
this.thread = try std.Thread.spawn(.{}, @This().run, .{this});
|
||||
var termios: std.posix.termios = undefined;
|
||||
try terminal.enableRawMode(&termios);
|
||||
this.termios = termios;
|
||||
try terminal.saveScreen();
|
||||
}
|
||||
|
||||
pub fn stop(this: *@This()) !void {
|
||||
if (this.termios) |*termios| {
|
||||
try terminal.disableRawMode(termios);
|
||||
try terminal.restoreScreen();
|
||||
}
|
||||
this.quit = true;
|
||||
if (this.thread) |thread| {
|
||||
thread.join();
|
||||
this.thread = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the next available event, blocking until one is available.
|
||||
pub fn nextEvent(this: *@This()) Event {
|
||||
return this.queue.pop();
|
||||
}
|
||||
|
||||
/// Post an event into the queue. Blocks if there is no capacity for the event.
|
||||
pub fn postEvent(this: *@This(), event: Event) void {
|
||||
this.queue.push(event);
|
||||
}
|
||||
|
||||
fn run(this: *@This()) !void {
|
||||
// thread to read user inputs
|
||||
const size = terminal.getTerminalSize();
|
||||
this.postEvent(.{ .resize = size });
|
||||
// read input in loop
|
||||
const buf: [256]u8 = undefined;
|
||||
_ = buf;
|
||||
while (!this.quit) {
|
||||
std.time.sleep(5 * std.time.ns_per_s);
|
||||
break;
|
||||
// try terminal.read(buf[0..]);
|
||||
// TODO: send corresponding events with key_presses
|
||||
// -> create corresponding event
|
||||
// -> handle key inputs (modifier, op codes, etc.)
|
||||
// -> I could take inspiration from `libvaxis` for this
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user