2 termio = require("posix.termio"),
3 poll = require("posix.poll"),
4 unistd = require("posix.unistd"),
8 esc = string.char(27) .. "[",
16 local function color_from_hue(hue)
18 local x = (1 - math.abs(h % 2 - 1))
20 local i = math.floor(h)
37 function cutie.to_color(color)
41 return color_from_hue(color)
42 elseif t == "string" then
43 local color = color:gsub("#", "")
46 tonumber(color:sub(1, 2), 16) / 255,
47 tonumber(color:sub(3, 4), 16) / 255,
48 tonumber(color:sub(5, 6), 16) / 255,
55 function make_color(color, bg)
56 color = cutie.to_color(color)
59 .. (bg and "48" or "38") .. ";2"
60 .. ";" .. math.clamp(math.floor(color[1] * 255), 0, 255)
61 .. ";" .. math.clamp(math.floor(color[2] * 255), 0, 255)
62 .. ";" .. math.clamp(math.floor(color[3] * 255), 0, 255)
66 function cutie.color(color)
67 return make_color(color, false)
70 function cutie.background_color(color)
71 return make_color(color, true)
74 function cutie.set_color(color)
75 cutie.render(cutie.color(color))
78 function cutie.set_background(color)
79 cutie.render(cutie.background_color(color))
84 cutie.bold = cutie.esc .. "1m"
85 cutie.no_effects = cutie.esc .. "0m"
87 function cutie.set_bold()
88 cutie.render(cutie.bold)
91 function cutie.clear_effects()
92 cutie.render(cutie.no_effects)
95 function cutie.move_cursor(x, y)
96 cutie.render_escape(math.floor(y) .. ";" .. math.floor(x) .. "H")
99 function cutie.set_alternate_buffer(enabled)
100 cutie.render_escape("?1049" .. (enabled and "h" or "l"))
103 function cutie.set_cursor_shown(enabled)
104 cutie.render_escape("?25" .. (enabled and "h" or "l"))
109 function cutie.set_canon_input(enabled)
110 local termios = posix.termio.tcgetattr(0)
113 termios.lflag = bit32.bor(termios.lflag,
118 termios.lflag = bit32.band(termios.lflag, bit32.bnot(bit32.bor(
124 posix.termio.tcsetattr(0, posix.termio.TCSANOW, termios)
127 function cutie.set_input_buffer(enabled)
128 lua_async.poll_functions[cutie.poll_input] = enabled or nil
130 cutie.input.buffer = enabled and "" or nil
131 cutie.input.history = enabled and {} or nil
132 cutie.input.cursor = nil
135 local function getchar()
136 return posix.unistd.read(0, 1)
139 function cutie.poll_input()
140 local pfd = {[0] = {events = {IN = true}}}
141 posix.poll.poll(pfd, 0)
143 if pfd[0].revents and pfd[0].revents.IN then
144 local char = getchar()
148 cutie.input:dispatchEvent(Event("input", {input = cutie.input.buffer}))
149 table.insert(cutie.input.history, cutie.input.buffer)
150 cutie.input.buffer = ""
151 cutie.input.cursor = nil
153 elseif char == cutie.esc:sub(1, 1) then
154 local char2 = getchar()
156 if char2 == cutie.esc:sub(2, 2) then
157 local char3 = getchar()
159 if char3 == "A" or char3 == "B" then
160 cutie.input.cursor = (cutie.input.cursor or #cutie.input.history + 1) + (char3 == "A" and -1 or 1)
162 if cutie.input.cursor > #cutie.input.history then
163 cutie.input.cursor = #cutie.input.history + 1
166 if cutie.input.cursor < 1 then
167 cutie.input.cursor = 1
170 cutie.input.buffer = cutie.input.history[cutie.input.cursor] or ""
173 elseif char == string.char(127) then
174 cutie.input.buffer = cutie.input.buffer:sub(1, #cutie.input.buffer - 1)
176 cutie.input.buffer = cutie.input.buffer .. char
183 function cutie.clear_screen()
184 cutie.render_escape("2J")
187 function cutie.empty_screen()
188 cutie.move_cursor(1, 1)
190 local size = cutie.get_terminal_size()
191 local str = (string.rep(" ", size[1]) .. "\n"):rep(size[2])
192 str = str:sub(1, #str - 1)
197 function cutie.render(text)
198 cutie.buffer = cutie.buffer .. text
201 function cutie.render_escape(text)
202 cutie.render(cutie.esc .. text)
205 function cutie.render_at(array, x, y)
206 for i, line in ipairs(array) do
207 cutie.move_cursor(x + 1, y + i)
212 function cutie.get_dimensions(array)
215 for _, line in pairs(array) do
216 width = math.max(width, #line)
219 return {width, #array}
222 function cutie.flush_buffer()
223 io.write(cutie.buffer)
228 function cutie.box(size)
230 "┌" .. string.rep("─", size[1]) .. "┐", string.rep(
231 "│" .. string.rep(" ", size[1]) .. "│", size[2]),
232 "└" .. string.rep("─", size[1]) .. "┘",
238 function cutie.handle_resize()
239 local pf = io.popen("echo -ne \"cols\\nlines\" | tput -S", "r")
240 local size = pf:read("*all"):split("\n")
242 cutie.terminal_size = size
245 function cutie.get_terminal_size()
246 return cutie.terminal_size