LuPPC/src/lua/core/textgpu.lua

298 lines
6.9 KiB
Lua

local textgpu = {}
local mapping = {}
local palette = {}
-- generate color mapping
for g=0, 255, 0x24 do
for b=0, 256, 0x40 do
for r=0, 255, 0x33 do
mapping[#mapping+1] = (math.min(r, 255) * 0x10000)
+ (math.min(g, 255) * 0x100) + math.min(b, 255)
end
end
end
for i=0, 15, 1 do
mapping[#mapping+1] = (i+1) * 0x0f0f0f
palette[i] = (i+1) * 0x0f0f0f
end
local write = io.write
local flush = io.flush
local background = 1
local foreground = 256
local tbuffer = {}
local bbuffer = {}
local fbuffer = {}
local function prep(w, h)
local tl = string.rep(" ", w)
local bb = string.rep("\0", w)
local fb = string.rep("\255", w)
for i=1, h, 1 do
tbuffer[i] = tl
bbuffer[i] = bb
fbuffer[i] = fb
end
end
local fg = 256
local bg = 1
local w, h = 1, 1
local unsub
local function set(x, y, text, f, b)
if x > w or x < 1 or y > h or y < 1 or not tbuffer[y] then
error("index out of bounds: " .. y, 3)
end
f = f or fg
b = b or bg
local len = utf8.len(text)
if x + len > w then
text = unsub(text, 1, -1 - ((x + len) - w))
end
tbuffer[y] = tbuffer[y]:sub(1, x - 1) ..
text .. tbuffer[y]:sub(x + len)
if type(f) == "string" then
fbuffer[y] = fbuffer[y]:sub(1, x - 1) ..
f .. fbuffer[y]:sub(x + len)
else
fbuffer[y] = fbuffer[y]:sub(1, x - 1) ..
string.rep(string.char(f - 1), len) .. fbuffer[y]:sub(x + len)
end
if type(b) == "string" then
bbuffer[y] = bbuffer[y]:sub(1, x - 1) ..
b .. bbuffer[y]:sub(x + len)
else
bbuffer[y] = fbuffer[y]:sub(1, x - 1) ..
string.rep(string.char(b - 1), len) .. bbuffer[y]:sub(x + len)
end
local F, B = mapping[f], mapping[b]
write("\27[38;2;", F&0xFF0000, ";", F&0x00FF00, ";", F&0x0000FF, "m")
write("\27[48;2;", B&0xFF0000, ";", B&0x00FF00, ";", B&0x0000FF, "m")
write("\27[", x, ";", y, "H", text)
end
local function fullRefresh()
for i=1, h, 1 do
local text = tbuffer[i]
local fgt = fbuffer[i]
local bgt = bbuffer[i]
local pb, pf
fgt = fgt:gsub("()(.)", function(n, fc)
local bc, tc = bgt:sub(n,n), text:sub(n,n)
local str = ""
if bc ~= pb then
pb = bc
local B = mapping[bc]
str = str .. string.format("\27[48;2;%d;%d;%dm",
B&0xFF0000, B&0x00FF00, B&0x0000FF)
end
if fc ~= pf then
pf = fc
local F = mapping[fc]
str = str .. string.format("\27[38;2;%d;%d;%dm",
F&0xFF0000, F&0x00FF00, F&0x0000FF)
end
str = str .. tc
return str
end)
write("\27[", h, ";1H", fgt)
flush()
end
end
local function get(x, y, len)
if x < 1 or x > w or y < 1 or y > h then
error("index out of bounds: " .. y, 3)
end
len = (len or 1) - 1
local txt = unsub(tbuffer[y], x, x + len)
if len == 0 then
return text,
mapping[fbuffer[y]:sub(x, x):byte() + 1],
mapping[bbuffer[y]:sub(x, x):byte() + 1]
else
return text, fbuffer[y]:sub(x, x + len), bbuffer[y]:sub(x, x + len)
end
end
function textgpu.start()
unsub = modules.sandbox.unicode.sub
local gpu = {}
function gpu.setForeground(color, ispalette)
checkArg(1, color, "number")
checkArg(2, ispalette, "boolean", "nil")
if ispalette then
if not palette[color] then
error("invalid palette index", 2)
end
color = palette[color]
end
local index = math.floor(modules.color.nearest(color, mapping))
fg = index or fg
return true
end
function gpu.setBackground(color, ispalette)
checkArg(1, color, "number")
checkArg(2, ispalette, "boolean", "nil")
if ispalette then
if not palette[color] then
error("invalid palette index", 2)
end
color = palette[color]
end
local index = math.floor(modules.color.nearest(color, mapping))
bg = index or bg
return true
end
function gpu.setPaletteColor(c, v)
checkArg(1, c, "number")
checkArg(2, v, "number")
if not palette[c] then
error("invalid palette index", 2)
end
palette[c] = v
end
function gpu.getPaletteColor(c)
checkArg(1, c, "number")
if not palette[c] then
error("invalid palette index", 2)
end
return palette[c]
end
function gpu.getForeground()
return mapping[fg]
end
function gpu.getBackground()
return mapping[bg]
end
function gpu.getDepth()
return 4
end
function gpu.maxDepth()
return 4
end
function gpu.setDepth()
return true
end
function gpu.getResolution()
return termutils.getSize()
end
function gpu.setResolution()
return false
end
gpu.maxResolution = gpu.getResolution
gpu.getViewport = gpu.getResolution
gpu.setViewport = gpu.setResolution
function gpu.set(x, y, text, vert)
checkArg(1, x, "number")
checkArg(2, y, "number")
checkArg(3, text, "string")
checkArg(4, vert, "boolean", "nil")
if vert then
local i = 1
local len = utf8.len(text)
while i <= len do
set(x, y + i - 1, unsub(text, i, i))
i = i + 1
end
return true
else
set(x, y, text)
end
end
function gpu.get(x, y)
checkArg(1, x, "number")
checkArg(2, y, "number")
return get(x, y)
end
function gpu.fill(x, y, W, H, c)
checkArg(1, x, "number")
checkArg(1, y, "number")
checkArg(1, W, "number")
checkArg(1, H, "number")
checkArg(1, c, "string")
c = unsub(c, 1, 1)
if #c == 0 then return true end
local str = c:rep(W)
for i=1, h, 1 do
set(x, y + i - 1, str)
end
return true
end
function gpu.copy(x, y, W, H, xd, yd)
checkArg(1, x, "number")
checkArg(2, y, "number")
checkArg(3, W, "number")
checkArg(4, H, "number")
checkArg(5, xd, "number")
checkArg(6, yd, "number")
local start, stop, step
if yd > 0 then -- moving up - copy from the top down
start, stop, step = y, y + H, 1
else -- moving down - copy from the bottom up
start, stop, step = y + H, y, -1
end
for i=start, stop, step do
local str, fstr, bstr = get(x, i, W)
set(x + xd, i + yd, str, fstr, bstr)
end
fullRefresh()
return true
end
local screenAddr
function gpu.getScreen()
return screenAddr
end
function gpu.bind() end
if not termutils.init() then
return nil, "Cannot initialize terminal based gpu"
end
write("\x1b[?25l") --Disable cursor
w, h = gpu.getResolution()
prep(w, h)
gpu.setForeground(0xFFFFFF)
gpu.setBackground(0x000000)
local gpuaddr = modules.component.api.register(nil, "gpu", gpu)
screenAddr = modules.component.api.register(nil, "screen", {getKeyboards = function() return {"TODO:SetThisUuid"} end}) --verry dummy screen, TODO: make it better, kbd uuid also in epoll.c
modules.component.api.register("TODO:SetThisUuid", "keyboard", {})
deadhooks[#deadhooks + 1] = function()
write("\x1b[?25h\x1b[" .. ((h-1)|0) .. ";1H") --Enable cursor on quit
io.flush()
termutils.restore()
end
return gpuaddr
end
return textgpu