login

<     >

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}