completely rewrote gpu, still broken but getting better, i hope?

This commit is contained in:
ocawesome101 2021-05-28 00:14:36 -04:00
parent 7367dd1d59
commit b9747364d7
3 changed files with 211 additions and 510 deletions

View File

@ -13,7 +13,6 @@ extern char lua_init[];
extern char lua_internet[]; extern char lua_internet[];
extern char lua_sandbox[]; extern char lua_sandbox[];
extern char lua_textgpu[]; extern char lua_textgpu[];
extern char lua_textgpu_new[];
extern char lua_textgpu_old[]; extern char lua_textgpu_old[];
extern char lua_util_buffer[]; extern char lua_util_buffer[];
extern char lua_util_color[]; extern char lua_util_color[];

View File

@ -1,262 +1,283 @@
local textgpu = {} local textgpu = {}
local mapping = {} local mapping = {}
local palette = {}
-- generate the mapping -- generate color mapping
local i = 0
for g=0, 255, 0x24 do for g=0, 255, 0x24 do
for b=0, 255, 0x40 do for b=0, 256, 0x40 do
for r=0, 255, 0x33 do for r=0, 255, 0x33 do
mapping[tostring(i)] = (r * 0x10000) + (g * 0x100) + b mapping[#mapping+1] = (math.min(r, 255) * 0x10000)
i = i + 1 + (math.min(g, 255) * 0x100) + math.min(b, 255)
end end
end end
end end
--[[local nw = io.write for i=0, 15, 1 do
io.write = function(...) mapping[#mapping+1] = (i+1) * 0x0f0f0f
nw(...) palette[i] = (i+1) * 0x0f0f0f
io.flush() end
native.sleep(20000)
end]]--
local write = io.write local write = io.write
local flush = io.flush local flush = io.flush
local background = "\0" local background = 1
local foreground = "\255" local foreground = 256
local tbuffer = {} local tbuffer = {}
local bbuffer = {} local bbuffer = {}
local fbuffer = {} local fbuffer = {}
local function prepareBuffers(w, h) local function prep(w, h)
local tbline = (" "):rep(w) local tl = string.rep(" ", w)
local bbline = ("\0"):rep(w) local bb = string.rep("\0", w)
local fbline = ("\7"):rep(w) local fb = string.rep("\255", w)
for i=1, h do
tbuffer[i] = tbline for i=1, h, 1 do
bbuffer[i] = bbline tbuffer[i] = tl
fbuffer[i] = fbline bbuffer[i] = bb
fbuffer[i] = fb
end end
end end
local usub local fg = 256
local function insertString(main, sub, at) local bg = 1
checkArg(1, main, "string") local w, h = 1, 1
checkArg(2, sub, "string")
checkArg(3, at, "number")
return usub(main, 1, at - 1) local unsub
.. sub .. usub(main, at + (utf8.len(sub) or 0)) 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 end
function textgpu.start() function textgpu.start()
usub = modules.sandbox.unicode.sub unsub = modules.sandbox.unicode.sub
local _height = 0
local gpu = {} local gpu = {}
function gpu.bind() return false, "This is static bound gpu" end
function gpu.setBackground(color, isPaletteIndex) function gpu.setForeground(color, ispalette)
checkArg(1, color, "number") checkArg(1, color, "number")
checkArg(2, isPaletteIndex, "boolean", "nil") checkArg(2, ispalette, "boolean", "nil")
if isPaletteIndex then if ispalette then
return --TODO: Maybe? if not palette[color] then
error("invalid palette index", 2)
end
color = palette[color]
end end
local old = background local index = math.floor(modules.color.nearest(color, mapping))
background = tostring(math.floor(modules.color.nearest(color, mapping))) fg = index or fg
write("\x1b[48;5;" .. background:byte() .. "m") return true
flush()
return mapping[old]
end end
function gpu.setForeground(color, isPaletteIndex)
function gpu.setBackground(color, ispalette)
checkArg(1, color, "number") checkArg(1, color, "number")
checkArg(2, isPaletteIndex, "boolean", "nil") checkArg(2, ispalette, "boolean", "nil")
if isPaletteIndex then if ispalette then
return --TODO: Maybe? if not palette[color] then
error("invalid palette index", 2)
end
color = palette[color]
end end
local old = foreground local index = math.floor(modules.color.nearest(color, mapping))
foreground = tostring(math.floor(modules.color.nearest(color, mapping))) bg = index or bg
write("\x1b[38;5;" .. foreground:byte() .. "m") return true
flush()
return mapping[old]
end end
function gpu.getBackground()
return mapping[background], false 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 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() function gpu.getForeground()
return mapping[foreground], false return mapping[fg]
end end
function gpu.getPaletteColor()
return nil function gpu.getBackground()
end return mapping[bg]
function gpu.setPaletteColor()
return nil
end
function gpu.maxDepth()
return 3
end
function gpu.setDepth()
return false
end end
function gpu.getDepth() function gpu.getDepth()
return 3 return 4
end end
function gpu.maxResolution()
return termutils.getSize() function gpu.maxDepth()
return 4
end end
function gpu.setDepth()
return true
end
function gpu.getResolution() function gpu.getResolution()
return termutils.getSize() return termutils.getSize()
end end
function gpu.getViewport()
return termutils.getSize() function gpu.setResolution()
return false
end end
function gpu.setViewport(w, h)
checkArg(1, w, "number") gpu.maxResolution = gpu.getResolution
checkArg(2, h, "number") gpu.getViewport = gpu.getResolution
return false, "Viewport not supported for this gpu" gpu.setViewport = gpu.setResolution
end
function gpu.setResolution(w, h) function gpu.set(x, y, text, vert)
checkArg(1, w, "number") checkArg(1, x, "number")
checkArg(2, h, "number") checkArg(2, y, "number")
return false, "Non resizeable gpu" 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 end
function gpu.get(x, y) function gpu.get(x, y)
checkArg(1, x, "number") checkArg(1, x, "number")
checkArg(2, y, "number") checkArg(2, y, "number")
return get(x, y)
return tbuffer[y]:sub(x,x), mapping[fbuffer[y]:sub(x,x)], mapping[bbuffer[y]:sub(x,x)]
end end
function gpu.set(x, y, value, vertical)
checkArg(1, x, "number")
checkArg(2, y, "number")
checkArg(3, value, "string")
checkArg(4, vertical, "boolean", "nil")
x = math.floor(x)
y = math.floor(y)
if not vertical then
if not tbuffer[y] then
native.log("GPU Set failed: under buffer")
return false
end
tbuffer[y] = insertString(tbuffer[y], value, x)
bbuffer[y] = insertString(bbuffer[y], background:rep(utf8.len(value)), x)
fbuffer[y] = insertString(fbuffer[y], foreground:rep(utf8.len(value)), x)
write("\x1b[" .. y .. ";" .. x .. "H" .. value)
else
--TODO: Buffers!
write("\x1b[" .. y .. ";" .. x .. "H")
value:gsub("([%z\1-\127\194-\244][\128-\191]*)", function(c)
write(c .. "\x1b[D\x1b[B")
end)
end
flush()
return true
end
function gpu.copy(x, y, w, h, tx, ty) --TODO: Check(check X multiple times)
checkArg(1, x, "number")
checkArg(2, y, "number")
checkArg(3, w, "number")
checkArg(4, h, "number")
checkArg(5, tx, "number")
checkArg(6, ty, "number")
local ttbuf = {}
local btbuf = {}
local ftbuf = {}
for i=1, h do
if i + y - 2 <= _height and i + y > 1 then
ttbuf[i] = tbuffer[y + i - 1] and usub(tbuffer[y + i - 1], x, x + w - 1) or (" "):rep(w)
btbuf[i] = bbuffer[y + i - 1] and bbuffer[y + i - 1]:sub(x, x + w - 1) or background:rep(w)
ftbuf[i] = fbuffer[y + i - 1] and fbuffer[y + i - 1]:sub(x, x + w - 1) or foreground:rep(w)
else
ttbuf[i] = (" "):rep(w)
btbuf[i] = background:rep(w)
ftbuf[i] = foreground:rep(w)
end
end
local bg = background
local fg = foreground
for i=1, h do function gpu.fill(x, y, W, H, c)
local line, linex checkArg(1, x, "number")
local lwrite = false checkArg(1, y, "number")
for j=1, w do checkArg(1, W, "number")
if btbuf[i]:sub(j,j) ~= bg then checkArg(1, H, "number")
lwrite = true checkArg(1, c, "string")
end c = unsub(c, 1, 1)
if ftbuf[i]:sub(j,j) ~= fg then if #c == 0 then return true end
lwrite = true local str = c:rep(W)
end for i=1, h, 1 do
if not line then linex = j end set(x, y + i - 1, str)
line = (line or "")
if lwrite then
local wx = (tx + x + linex - 1)|0
local wy = (ty + y + i - 1)|0
if tbuffer[wy] then
write("\x1b[48;5;" .. bg:byte() .. "m\x1b[38;5;" .. fg:byte() .. "m\x1b[" .. wy
.. ";" .. wx .. "H" .. line)
tbuffer[wy] = insertString(tbuffer[wy], line, wx)
bbuffer[wy] = insertString(bbuffer[wy], bg:rep(utf8.len(line)), wx)
fbuffer[wy] = insertString(fbuffer[wy], fg:rep(utf8.len(line)), wx)
end
bg = btbuf[i]:sub(j,j)
fg = ftbuf[i]:sub(j,j)
line = nil
linex = nil
lwrite = false
end
if not line then linex = j end
line = (line or "") .. usub(ttbuf[i], j,j)
end
if line then
local wx = (tx + x + linex - 1)|0
local wy = (ty + y + i - 1)|0
if tbuffer[wy] then
write("\x1b[48;5;" .. bg:byte() .. "m\x1b[38;5;" .. fg:byte() .. "m\x1b[" .. wy
.. ";" .. wx .. "H" .. line)
tbuffer[wy] = insertString(tbuffer[wy], line, wx)
bbuffer[wy] = insertString(bbuffer[wy], bg:rep(utf8.len(line)), wx)
fbuffer[wy] = insertString(fbuffer[wy], fg:rep(utf8.len(line)), wx)
end
line = nil
linex = nil
lwrite = false
end
end end
write("\x1b[48;5;" .. background:byte() .. "m")
write("\x1b[38;5;" .. foreground:byte() .. "m")
flush()
return true return true
end end
function gpu.fill(x, y, w, h, ch)
function gpu.copy(x, y, W, H, xd, yd)
checkArg(1, x, "number") checkArg(1, x, "number")
checkArg(2, y, "number") checkArg(2, y, "number")
checkArg(3, w, "number") checkArg(3, W, "number")
checkArg(4, h, "number") checkArg(4, H, "number")
checkArg(5, ch, "string") checkArg(5, xd, "number")
ch = usub(ch, 1, 1):rep(math.floor(w)) checkArg(6, yd, "number")
for i=1, h do local start, stop, step
if i + y - 1 <= _height and i + y > 1 then if yd > 0 then -- moving up - copy from the top down
gpu.set(x, y + i - 1, ch) start, stop, step = y, y + H, 1
end else -- moving down - copy from the bottom up
start, stop, step = y + H, y, -1
end 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 return true
end end
local screenAddr local screenAddr
function gpu.getScreen() function gpu.getScreen()
return screenAddr return screenAddr
end end
function gpu.bind() end
if not termutils.init() then if not termutils.init() then
return nil, "Cannot initialize terminal based gpu" return nil, "Cannot initialize terminal based gpu"
end end
write("\x1b[?25l") --Disable cursor write("\x1b[?25l") --Disable cursor
local w, h = gpu.getResolution()
_height = h w, h = gpu.getResolution()
prepareBuffers(w, h) prep(w, h)
gpu.setForeground(0xFFFFFF) gpu.setForeground(0xFFFFFF)
gpu.setBackground(0x000000) gpu.setBackground(0x000000)

View File

@ -1,319 +0,0 @@
local textgpu = {}
local write = io.write
local flush = io.flush
local background = 0x000000 -- "0"
local foreground = 0xFFFFFF -- "0"
local tbuffer = {}
local bbuffer = {}
local fbuffer = {}
local palette = {
[0] = 0x0f0f0f,
0x1e1e1e,
0x2d2d2d,
0x3c3c3c,
0x4b4b4b,
0x5a5a5a,
0x696969,
0x787878,
0x878787,
0x969696,
0xa5a5a5,
0xb4b4b4,
0xc3c3c3,
0xd2d2d2,
0xe1e1e1,
0xf0f0f0
}
local function prepareBuffers(w, h)
local tbline = (" "):rep(w)
local bbline = ("\0\0\0"):rep(w) -- ("0"):rep(w)
local fbline = ("\255\255\255"):rep(w) -- ("7"):rep(w)
for i=1, h do
tbuffer[i] = tbline
bbuffer[i] = bbline
fbuffer[i] = fbline
end
end
local function setForeground(c)
local r = c & 0xFF0000
local g = c & 0x00FF00
local b = c & 0x0000FF
io.write("\27[38;2;", r, ";", g, ";", b, "m")
end
local function setBackground(c)
local r = c & 0xFF0000
local g = c & 0x00FF00
local b = c & 0x0000FF
io.write("\27[48;2;", r, ";", g, ";", b, "m")
end
local usub
local function insertString(main, sub, at)
checkArg(1, main, "string")
checkArg(2, sub, "string")
checkArg(3, at, "number")
return (usub(main, 1, at - 1) or "")
.. sub .. (usub(main, at + (utf8.len(sub) or 0)))
end
function textgpu.start()
usub = modules.sandbox.unicode.sub
local _height = 0
local gpu = {}
function gpu.bind() return false, "This is static bound gpu" end
function gpu.setBackground(color, isPaletteIndex)
checkArg(1, color, "number")
checkArg(2, isPaletteIndex, "boolean", "nil")
if isPaletteIndex then
if color > 15 or color < 0 then
error("invalid palette index", 2)
end
return palette[color]
end
local old = background
background = color -- tostring(math.floor(modules.color.nearest(color, mapping)))
setBackground(background)
--write("\x1b[4" .. background .. "m")
flush()
return old -- mapping[old]
end
function gpu.setForeground(color, isPaletteIndex)
checkArg(1, color, "number")
checkArg(2, isPaletteIndex, "boolean", "nil")
if isPaletteIndex then
if color < 0 or color > 15 then
error("invalid palette index", 2)
end
return palette[color]
end
local old = foreground
foreground = color -- tostring(math.floor(modules.color.nearest(color, mapping)))
setForeground(foreground)
--write("\x1b[3" .. foreground .. "m")
flush()
return old -- mapping[old]
end
function gpu.getBackground()
return mapping[background], false
end
function gpu.getForeground()
return mapping[foreground], false
end
function gpu.getPaletteColor(index)
if index < 0 or index > 15 then
error("invalid palette index", 2)
end
return palette[index]
end
function gpu.setPaletteColor(index, value)
if index < 0 or index > 15 then
error("invalid palette index", 2)
end
palette[index] = value
return true
end
function gpu.maxDepth()
return 4
end
function gpu.setDepth()
return true
end
function gpu.getDepth()
return 4
end
function gpu.maxResolution()
return termutils.getSize()
end
function gpu.getResolution()
return termutils.getSize()
end
function gpu.getViewport()
return termutils.getSize()
end
function gpu.setViewport(w, h)
checkArg(1, w, "number")
checkArg(2, h, "number")
return false, "Viewport not supported for this gpu"
end
function gpu.setResolution(w, h)
checkArg(1, w, "number")
checkArg(2, h, "number")
return false, "Non resizeable gpu"
end
function gpu.get(x, y)
checkArg(1, x, "number")
checkArg(2, y, "number")
return tbuffer[y]:sub(x,x),
fbuffer[y]:sub(x*3-2,x*3):unpack(">I3"),
bbuffer[y]:sub(x*3-2,x*3):unpack(">I3")
end
function gpu.set(x, y, value, vertical)
checkArg(1, x, "number")
checkArg(2, y, "number")
checkArg(3, value, "string")
checkArg(4, vertical, "boolean", "nil")
x = math.floor(x)
y = math.floor(y)
if not vertical then
if not tbuffer[y] then
native.log("GPU Set failed: under buffer")
return false
end
tbuffer[y] = insertString(tbuffer[y], value, x)
bbuffer[y] = insertString(bbuffer[y],
string.pack(">I3", background):rep(utf8.len(value)), x*3-2)
fbuffer[y] = insertString(fbuffer[y],
string.pack(">I3", foreground):rep(utf8.len(value)), x*3-2)
write("\x1b[" .. y .. ";" .. x .. "H" .. value)
else
--TODO: Buffers!
write("\x1b[" .. y .. ";" .. x .. "H")
value:gsub("([%z\1-\127\194-\244][\128-\191]*)", function(c)
write(c .. "\x1b[D\x1b[B")
end)
end
flush()
return true
end
function gpu.copy(x, y, w, h, tx, ty) --TODO: Check(check X multiple times)
checkArg(1, x, "number")
checkArg(2, y, "number")
checkArg(3, w, "number")
checkArg(4, h, "number")
checkArg(5, tx, "number")
checkArg(6, ty, "number")
local ttbuf = {}
local btbuf = {}
local ftbuf = {}
for i=1, h do
if i + y - 2 <= _height and i + y > 1 then
ttbuf[i] = tbuffer[y + i - 1] and usub(tbuffer[y + i - 1], x, x + w - 1) or (" "):rep(w)
btbuf[i] = bbuffer[y + i - 1] and
bbuffer[y + i - 1]:sub(x*3-2, (x + w - 1)*3) or string.pack(">I3", background):rep(w)
ftbuf[i] = fbuffer[y + i - 1] and
fbuffer[y + i - 1]:sub(x*3-2, (x + w - 1)*3) or string.pack(">I3", foreground):rep(w)
else
ttbuf[i] = (" "):rep(w)
btbuf[i] = string.pack(">I3", background):rep(w)
ftbuf[i] = string.pack(">I3", foreground):rep(w)
end
end
local bg = background
local fg = foreground
for i=1, h do
local line, linex
local lwrite = false
for j=1, w do
if btbuf[i]:sub(j*3-2,j*3):unpack(">I3") ~= bg then
lwrite = true
end
if ftbuf[i]:sub(j*3-2,j*3):unpack(">I3") ~= fg then
lwrite = true
end
if not line then linex = j end
line = (line or "")
if lwrite then
local wx = (tx + x + linex - 1)|0
local wy = (ty + y + i - 1)|0
if tbuffer[wy] then
setBackground(bg)
setForeground(foreground)
--write("\x1b[4" .. bg .. "m\x1b[3" .. fg .. "m
write("\x1b[" .. wy .. ";" .. wx .. "H" .. line)
tbuffer[wy] = insertString(tbuffer[wy], line, wx)
bbuffer[wy] = insertString(bbuffer[wy],
string.pack(">I3", bg):rep(utf8.len(line)), wx*3-2)
fbuffer[wy] = insertString(fbuffer[wy],
string.pack(">I3", fg):rep(utf8.len(line)), wx*3-2)
end
bg = btbuf[i]:sub(j*3-2,j*3):unpack(">I3")
fg = ftbuf[i]:sub(j*3-2,j*3):unpack(">I3")
line = nil
linex = nil
lwrite = false
end
if not line then linex = j end
line = (line or "") .. usub(ttbuf[i], j,j)
end
if line then
local wx = (tx + x + linex - 1)|0
local wy = (ty + y + i - 1)|0
if tbuffer[wy] then
setBackground(bg)
setForeground(fg)
--write("\x1b[4" .. bg .. "m\x1b[3" .. fg .. "m
write("\x1b[" .. wy .. ";" .. wx .. "H" .. line)
tbuffer[wy] = insertString(tbuffer[wy], line, wx)
bbuffer[wy] = insertString(bbuffer[wy],
string.pack(">I3", bg):rep(utf8.len(line)), wx*3-2)
fbuffer[wy] = insertString(fbuffer[wy],
string.pack(">I3", fg):rep(utf8.len(line)), wx*3-2)
end
line = nil
linex = nil
lwrite = false
end
end
setBackground(background)
setForeground(foreground)
--write("\x1b[4" .. background .. "m")
--write("\x1b[3" .. foreground .. "m")
flush()
return true
end
function gpu.fill(x, y, w, h, ch)
checkArg(1, x, "number")
checkArg(2, y, "number")
checkArg(3, w, "number")
checkArg(4, h, "number")
checkArg(5, ch, "string")
ch = usub(ch, 1, 1):rep(math.floor(w))
for i=1, h do
if i + y - 1 <= _height and i + y > 1 then
gpu.set(x, y + i - 1, ch)
end
end
return true
end
local screenAddr
function gpu.getScreen()
return screenAddr
end
if not termutils.init() then
return nil, "Cannot initialize terminal based gpu"
end
write("\x1b[?25l") --Disable cursor
local w, h = gpu.getResolution()
_height = h
prepareBuffers(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