1
0
Fork 0
cc-stuff/bigterm.lua

232 lines
7.0 KiB
Lua

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,
}
local lib = {}
function lib.fromFile(conf)
local fp = assert(io.open(conf, "r"))
local conf = textutils.unserializeJSON(fp:read("a"))
fp:close()
local monitors = {}
for addr, pos in pairs(conf.monitors) do
local p = assert(peripheral.wrap(addr))
table.insert(monitors, { p = p, x = pos.x, y = pos.y })
end
return lib.new(monitors, { palette = conf.palette, scale = conf.scale })
end
function lib.new(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
return lib