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