local expect = require("cc.expect").expect local function todo() error("todo!") end local pretty = require "cc.pretty" local BigTerm = { write = function(self, text) local w = self.getSize() for i = 1, #text do if self._pos.x <= w then self._blitpixel( self._pos.x, self._pos.y, text:sub(i, i), self._colorChar(self._colorForeground), self._colorChar(self._colorBackground) ) self._pos.x = self._pos.x + 1 end end end, blit = function(self, text, foreground, background) local x, y = self._pos.x, self._pos.y local w = #text local LIMIT = 0 local ox = 1 while w > 0 and LIMIT < 20 do local mon, i, lx, ly = self._getMonitorForScreenPos(x, y) if not mon then break end local remaining = mon._w - lx mon.p.setCursorPos(lx + 1, ly + 1) mon.p.blit( text:sub(ox, ox + w), foreground:sub(ox, ox + w - 1), background:sub(ox, ox + w - 1) ) w = w - remaining x = x + remaining ox = ox + remaining end end, clear = function(self) self._forEachMonitor(function(mon) mon.p.clear() end) end, clearLine = function(self) todo() end, scroll = function(self, n) -- TODO: NOPE! store framebuffer and write lines onto other screens self._forEachMonitor(function(mon) mon.p.scroll(n) end) end, getCursorPos = function(self) return self._pos.x, self._pos.y end, setCursorPos = function(self, x, y) self._pos.x = x self._pos.y = y -- TODO: move cursor to the correct monitor and hide it from others end, setCursorBlink = function(self, state) todo() end, getCursorBlink = function(self) todo() end, isColor = function(self) return true end, getSize = function(self) local w, h = 0, 0 for ix = 1, self._w do local mon = self._findMonitor(ix, 1) w = w + mon._w end for iy = 1, self._h do local mon = self._findMonitor(1, iy) h = h + mon._h end return w, h end, setTextColor = function(self, fg) self._forEachMonitor(function(mon) mon.p.setTextColor(fg) end) self._colorForeground = fg end, getTextColor = function(self) todo() end, setBackgroundColor = function(self, bg) self._forEachMonitor(function(mon) mon.p.setBackgroundColor(bg) end) self._colorBackground = bg end, getBackgroundColor = function(self) todo() end, setTextScale = function(self, scale) self._scale = scale self._reset() end, getTextScale = function(self) return self._scale end, setPaletteColor = function(self, index, color, g, b) expect(1, index, "number") expect(2, color, "number") expect(3, g, "number", "nil") expect(4, b, "number", "nil") if index < 0 or index > 32768 or math.log(index, 2) % 1 ~= 0 then error("index out of range") end local r = color if g == nil or b == nil then if color < 0 or color > 0xFFFFFF then error("color out of range") end r = bit.band(0xFF, bit.brshift(color, 16)) / 255 g = bit.band(0xFF, bit.brshift(color, 8)) / 255 b = bit.band(0xFF, bit.brshift(color, 0)) / 255 else if r < 0 or r > 1.0 then error("red channel out of range") end if g < 0 or g > 1.0 then error("green channel out of range") end if b < 0 or b > 1.0 then error("blue channel out of range") end end self._palette[index] = bit.bor( bit.blshift(math.floor(r * 255), 16), bit.blshift(math.floor(g * 255), 8), bit.blshift(math.floor(b * 255), 0) ) self._forEachMonitor(function(mon) mon.p.setPaletteColor(index, r, g, b) end) end, getPaletteColor = function(self, index) todo() end, -- internals _colorChar = function(self, v) return string.format("%x", math.floor(math.log(v, 2))) end, _reset = function(self) self._w = 1 self._h = 1 self._forEachMonitor(function(mon, i) mon.p.setTextScale(self._scale) local w, h = mon.p.getSize() self._monitors[i]._w = w self._monitors[i]._h = h if mon.x > self._w then self._w = mon.x end if mon.y > self._h then self._h = mon.y end for id, color in pairs(self._palette) do mon.p.setPaletteColor(id, color) end end) end, _blitpixel = function(self, x, y, c, bg, fg) bg = bg or "0" fg = fg or "f" local mon = self._getMonitorForScreenPos(x, y) mon.p.setCursorPos(((x - 1) % mon._w) + 1, ((y - 1) % mon._h) + 1) mon.p.blit(c, bg, fg) end, _findMonitor = function(self, x, y) for i = 1, #self._monitors do local mon = self._monitors[i] if mon.x == x and mon.y == y then return mon, i end end end, _getMonitorForScreenPos = function(self, x, y) local oy = 1 for iy = 1, self._h do local ox = 1 for ix = 1, self._w do local mon, i = self._findMonitor(ix, iy) if x >= ox and x < (ox + mon._w) and y >= oy and y < (oy + mon._h) then return mon, i, x - ox, y - oy end ox = ox + mon._w end local mon, i = self._findMonitor(1, iy) oy = oy + mon._h end end, _forEachMonitor = function(self, fun) for i = 1, #self._monitors do fun(self._monitors[i], i) end end, } return function(monitors, args) args = args or {} local mon = setmetatable({ _monitors = monitors, _pos = { x = 1, y = 1 }, _blink = false, _colorForeground = colors.white, _colorBackground = colors.black, _scale = args.scale or 1.0, _palette = args.palette or {}, }, { __index = BigTerm }) for name, method in pairs(BigTerm) do if type(method) == "function" then mon[name] = function(...) return method(mon, ...) end end end mon._reset() return mon end