aboutsummaryrefslogtreecommitdiff
path: root/libtui/libtui.ha
diff options
context:
space:
mode:
Diffstat (limited to 'libtui/libtui.ha')
-rw-r--r--libtui/libtui.ha145
1 files changed, 145 insertions, 0 deletions
diff --git a/libtui/libtui.ha b/libtui/libtui.ha
new file mode 100644
index 0000000..3d33680
--- /dev/null
+++ b/libtui/libtui.ha
@@ -0,0 +1,145 @@
+// License: MPL-2.0
+// (c) 2022 Julian Hurst <ark@mansus.space>
+
+use fmt;
+use os;
+use io;
+use unix::tty;
+use errors;
+use bufio;
+use encoding::utf8;
+use strings;
+
+export type key = (rune | specialkey);
+
+// A listener on a rune input that returns if the ui needs to terminate or not
+export type listener = *fn(ui: *ttyui, r: key) bool;
+
+export type ttyui = struct {
+ term: tty::termios,
+ f: io::file,
+ listeners: []listener,
+};
+
+// Initializes the UI and returns a ttyui.
+export fn init() ttyui = {
+ let f = match (tty::open()) {
+ case let f: io::file =>
+ yield f;
+ case let e: tty::error =>
+ fmt::fatal(tty::strerror(e));
+ };
+ if (!tty::isatty(f)) {
+ fmt::fatal("stream is not a tty");
+ };
+ let term = match (tty::termios_query(f)) {
+ case let t: tty::termios =>
+ yield t;
+ case let e: errors::error =>
+ fmt::fatal(errors::strerror(e));
+ };
+ tty::makeraw(&term)!;
+ //tty::noecho(&term)!;
+
+ let ui = ttyui {
+ term = term,
+ f = f,
+ listeners = [],
+ };
+ hidecursor(ui);
+ return ui;
+};
+
+export fn hidecursor(ui: ttyui) void = {
+ print(ui, "\x1B[?25l");
+};
+
+export fn showcursor(ui: ttyui) void = {
+ print(ui, "\x1B[?25h");
+};
+
+// Returns the window size for the given ttyui.
+export fn getwinsize(ui: ttyui) (tty::ttysize | tty::error) = {
+ return tty::winsize(ui.f);
+};
+
+// Suspend the UI. To restore it, use [[resume]].
+export fn suspend(ui: *ttyui) void = {
+ showcursor(*ui);
+ tty::termios_restore(&ui.term);
+};
+
+// Resumes the UI after a [[suspend]].
+export fn resume(ui: *ttyui) void = {
+ tty::makeraw(&ui.term)!;
+ tty::noecho(&ui.term)!;
+ hidecursor(*ui);
+};
+
+// Restores the UI state and closes and frees the resources associated with the
+// given ttyui.
+export fn finish(ui: *ttyui) void = {
+ showcursor(*ui);
+ tty::termios_restore(&ui.term);
+ io::close(ui.f)!;
+ free(ui.listeners);
+};
+
+// Scans a rune. A convenience function for [[bufio::scanrune]].
+export fn scan(ui: ttyui) (key | utf8::invalid | io::EOF | io::error) = {
+ //const r = bufio::scanrune(ui.f)?;
+ const r = match (bufio::scanrune(ui.f)?) {
+ case let r: rune =>
+ yield r;
+ case =>
+ return io::EOF;
+ };
+ return getkey(r);
+};
+
+// Notify (call) the ttyui's listeners with the ttyui and r as a parameter.
+// Returns true if a listener returned true, false otherwise.
+export fn notify(ui: *ttyui, r: (rune | specialkey)) bool = {
+ for (let i = 0z; i < len(ui.listeners); i += 1) {
+ if (ui.listeners[i](ui, r)) {
+ return true;
+ };
+ };
+ return false;
+};
+
+fn loop(ui: *ttyui) void = {
+ for (true) {
+ let r = match (bufio::scanrune(ui.f)) {
+ case let r: rune =>
+ yield r;
+ case utf8::invalid =>
+ fmt::fatal("Invalid utf8 sequence found");
+ case io::EOF =>
+ fmt::fatal("EOF");
+ case let e: io::error =>
+ fmt::fatal(io::strerror(e));
+ };
+ for (let i = 0z; i < len(ui.listeners); i += 1) {
+ if (ui.listeners[i](ui, r)) {
+ return;
+ };
+ };
+ };
+};
+
+// Add a listener to the given ttyui.
+export fn addlistener(ui: *ttyui, l: listener) void = {
+ append(ui.listeners, l);
+};
+
+// Print a string or rune to the ttyui.
+export fn print(ui: ttyui, arg: (str | rune)) void = {
+ fmt::fprint(ui.f, arg)!;
+ //fmt::fprintf(ui.f, "{}\r", arg)!;
+};
+
+// Clear the ttyui.
+export fn clear(ui: ttyui) void = {
+ fmt::fprintf(ui.f, "\x1B[2J\x1B[1;1H\r")!;
+};