2023-11-21 17:06:42 (UTC-03:00)
Marcel Rodrigues <marcelgmr@gmail.com>
add tui module
diff --git a/lib/arco.lua b/lib/arco.lua index 3eb1655..b6b3794 100644 --- a/lib/arco.lua +++ b/lib/arco.lua @@ -1,4 +1,5 @@ local arco = {} +arco.tui = require "arco.tui" arco.sdl = require "arco.sdl2" arco.net = require "arco.enet" arco.rand = require "arco.rand" diff --git a/lib/arco/tui.lua b/lib/arco/tui.lua new file mode 100644 index 0000000..122bae4 --- /dev/null +++ b/lib/arco/tui.lua @@ -0,0 +1,177 @@ +local bit = require "bit" +local ffi = require "ffi" + +ffi.cdef[[ +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[32]; /* control characters */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +}; +int tcgetattr(int fd, struct termios *termios_p); +int tcsetattr(int fd, int optional_actions, const struct termios *termios_p); +]] + +local consts = { + VINTR = 0, + VQUIT = 1, + VERASE = 2, + VKILL = 3, + VEOF = 4, + VTIME = 5, + VMIN = 6, + VSWTC = 7, + VSTART = 8, + VSTOP = 9, + VSUSP = 10, + VEOL = 11, + VREPRINT = 12, + VDISCARD = 13, + VWERASE = 14, + VLNEXT = 15, + VEOL2 = 16, + + ISIG = 0000001, + ICANON = 0000002, + ECHO = 0000010, + ECHOE = 0000020, + ECHOK = 0000040, + ECHONL = 0000100, + NOFLSH = 0000200, + TOSTOP = 0000400, + IEXTEN = 0100000, + + TCSANOW = 0, + TCSADRAIN = 1, + TCSAFLUSH = 2, +} + +local termios = setmetatable(consts, { + __index = function (t, k) return ffi.C[k] end +}) + +local Term = {} +Term.__index = Term + +function Term:setup(timeout) + timeout = timeout or 1 -- in deciseconds + local praw = ffi.new("struct termios [1]") + local raw = praw[0] + termios.tcgetattr(0, self.term_prev) + ffi.copy(praw, self.term_prev, ffi.sizeof(raw)) + raw.c_lflag = bit.band(raw.c_lflag, bit.bnot(bit.bor(termios.ECHO, termios.ICANON))) + raw.c_cc[termios.VMIN] = 0 + raw.c_cc[termios.VTIME] = timeout + termios.tcsetattr(0, termios.TCSAFLUSH, praw) +end + +function Term:restore() + termios.tcsetattr(0, termios.TCSAFLUSH, self.term_prev) +end + +function Term:hide_cursor() + io.write("\x1B[?25l") +end + +function Term:show_cursor() + io.write("\x1B[?25h") +end + +function Term:getpos() + io.write("\x1b[6n") + io.read(2) -- "\x1b[" + local y = io.read("*n") + io.read(1) -- ";" + local x = io.read("*n") + io.read(1) -- "R" + return x, y +end + +function Term:size() + io.write("\x1b7") -- save cursor position + io.write("\x1b[999C\x1b[999B") -- go to bottom-right corner + local w, h = self:getpos() + io.write("\x1b8") -- restore cursor position + return w, h +end + +function Term:up(n) + io.write("\x1B["..(n or "").."A") +end + +function Term:down(n) + io.write("\x1B["..(n or "").."B") +end + +function Term:right(n) + io.write("\x1B["..(n or "").."C") +end + +function Term:left(n) + io.write("\x1B["..(n or "").."D") +end + +function Term:goto(x, y) + io.write(("\x1B[%d;%dH"):format(y, x)) +end + +function Term:goto_col(x) + io.write(("\x1B[%dG"):format(x)) +end + +function Term:clear() + io.write("\x1B[2J") + io.write("\x1B[H") -- move to home +end + +function Term:clear_line() + io.write("\x1B[2K") +end + +function Term:enter_altbuf() + io.write("\x1B[?47h") +end + +function Term:exit_altbuf() + io.write("\x1B[?47l") +end + +function Term:delay(n) + for i = 1, n do + io.read(1) + end +end + +function Term:get_key() + local key + while key == nil do + key = io.read(1) + end + return key +end + +function Term:input(query) + query = query or "" + io.write(query) + self:restore() + self:show_cursor() + local answer = io.read() + self:hide_cursor() + self:setup() + return answer +end + +local function new_term() + local self = setmetatable({}, Term) + self.term_prev = ffi.new("struct termios [1]") + return self +end + +return {new_term=new_term}