diff options
Diffstat (limited to 'libtui/libtui.ha')
| -rw-r--r-- | libtui/libtui.ha | 145 |
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")!; +}; |
