Compare commits

...

4 Commits

Author SHA1 Message Date
Gabriel Tofvesson
3d01e66364 Rename graphics buffer 2024-11-22 01:17:57 +01:00
Gabriel Tofvesson
609013a9fa Specify loglevel by sink 2024-11-22 01:17:32 +01:00
Gabriel Tofvesson
b0e5877417 Implement buffered windows to support transparancy 2024-11-19 18:57:36 +01:00
Gabriel Tofvesson
86c23b8433 Add bounds check to utils module 2024-11-19 18:57:11 +01:00
5 changed files with 308 additions and 44 deletions

0
gfx/render/init.lua Normal file
View File

243
gfx/render/windowbuffer.lua Normal file
View File

@ -0,0 +1,243 @@
local Util = require("util")
local DEFAULT_C = " "
local DEFAULT_FG = colors.white
local DEFAULT_BG = colors.black
local function ensureC(cVal)
return (cVal ~= nil and #cVal == 1 and cVal) or DEFAULT_C
end
local WindowBuffer = {}
-- Create a shallow wrapper for window object
function WindowBuffer.wrapWindow(w, defaultC, defaultFg, defaultBg, clearColor)
defaultC = ensureC(defaultC)
defaultFg = defaultFg or DEFAULT_FG
defaultBg = defaultBg or DEFAULT_BG
clearColor = clearColor or DEFAULT_BG
local obj = {}
function obj.blit(text, fg, bg)
w.blit(text, fg, bg)
end
function obj.flush() end
function obj.flushPartial() end
function obj.getSize()
return w.getSize()
end
function obj._setPixel(x, y, c, fg, bg)
w.setCursorPos(x, y)
w.setFgColor(fg or defaultFg)
w.setBgColor(bg or defaultBg)
w.write(c or defaultC)
end
function obj.setPixel(x, y, c, fg, bg)
local width, height = obj.getSize()
Util.boundCheck(x, y, width, height)
obj._setPixel(x, y, c, fg, bg)
end
-- Partial writes are not possible for native window access
obj._writePixel = obj._setPixel
obj.writePixel = obj.setPixel
function obj.setCursorPos(x, y)
w.setCursorPos(x, y)
end
function obj.getCursorPos()
return w.getCursorPos()
end
function obj.clear()
w.setBgColor(clearColor)
--w.setFgColor(clearColor) -- Shouldn't matter for native clear
w.clear()
end
return obj
end
function WindowBuffer.new(width, height, clearC, clearFg, clearBg)
clearC = ensureC(clearC)
clearFg = clearFg or DEFAULT_FG
clearBg = clearBg or DEFAULT_BG
local obj = {}
local function setState(o, c, fg, bg)
o.c = c
o.bg = bg
o.fg = fg
return o
end
local function writeState(o, c, fg, bg)
o.c = c or o.c
o.bg = bg or o.bg
o.fg = fg or o.fg
return o
end
local buffer = {}
for i=1,height do
local line = {}
for j=1,width do
line[j] = setState({})
end
buffer[i] = line
end
local pX, pY = 1, 1
function obj.blit(text, fg, bg)
assert(#text == #fg, "Foreground color blit length must match text length")
assert(#text == #bg, "Background color blit length must match text length")
local line = buffer[pY]
local baseX = pX
for i=1,math.min(#text, width - baseX - 1) do
local blitChar = line[i]
blitChar.c = text:sub(i, i)
blitChar.fg = fg:sub(i, i)
blitChar.bg = bg:sub(i, i)
end
pX = pX + #text
end
function obj._setPixel(x, y, c, fg, bg)
setState(buffer[y][x], c, fg, bg)
end
function obj.setPixel(x, y, c, fg, bg)
Util.boundCheck(x, y, width, height)
obj._setPixel(x, y, c, fg, bg)
end
function obj._writePixel(x, y, c, fg, bg)
writeState(buffer[y][x], c, fg, bg)
end
function obj.writePixel(x, y, c, fg, bg)
Util.boundCheck(x, y, width, height)
obj._writePixel(x, y, c, fg, bg)
end
function obj.setCursorPos(x, y)
pX, pY = x, y
end
function obj.getCursorPos()
return pX, pY
end
function obj.clear()
for i=1,height do
local line = buffer[i]
for j=1,width do
setState(line[j], clearC, clearFg, clearBg)
end
end
end
function obj.flush(win, offsetX, offsetY)
for i=1,height do
local line = buffer[i]
for j=1,width do
local entry = line[j]
win._writePixel(j + offsetX, i + offsetY, entry.c, entry.fg, entry.bg)
end
end
end
function obj.flushPartial(win, winOffsetX, winOffsetY, baseX, baseY, flushWidth, flushHeight)
for i=baseY,baseY+flushHeight do
local line = buffer[i]
for j=baseX,baseX+flushWidth do
local entry = line[j]
win._writePixel(j + winOffsetX, i + winOffsetY, entry.c, entry.fg, entry.bg)
end
end
end
return obj
end
function WindowBuffer.shallowChild(windowBuffer, width, height, offsetX, offsetY, clearC, clearFg, clearBg)
-- Assume static for now
local parentWidth, parentHeight = windowBuffer.getSize()
Util.boundCheck(offsetX, offsetY, parentWidth, parentHeight)
Util.boundCheck(offsetX + width, offsetY + height, parentWidth, parentHeight)
clearC = ensureC(clearC)
clearFg = clearFg or DEFAULT_FG
clearBg = clearBg or DEFAULT_BG
local obj = {}
local pX, pY = 1, 1
local function setCursorPos()
windowBuffer.setCursorPos(pX + offsetX, pY + offsetY)
end
function obj.blit(text, fg, bg)
setCursorPos()
windowBuffer.blit(text, fg, bg)
pX = pX + #text
end
function obj._setPixel(c, fg, bg)
setCursorPos()
windowBuffer._setPixel(c, fg, bg)
end
function obj.setPixel(c, fg, bg)
setCursorPos()
windowBuffer.setPixel(c, fg, bg)
end
function obj._writePixel(c, fg, bg)
setCursorPos()
windowBuffer._writePixel(c, fg, bg)
end
function obj.writePixel(c, fg, bg)
setCursorPos()
windowBuffer.writePixel(c, fg, bg)
end
function obj.setCursorPos(x, y)
pX, pY = x, y
end
function obj.getCursorPos()
return pX, pY
end
function obj.getSize()
return width, height
end
function obj.clear()
for i=1,height do
for j=1,width do
windowBuffer._setPixel(j + offsetX, i + offsetY, clearC, clearFg, clearBg)
end
end
end
-- Reconsider implementation?
obj.flush = windowBuffer.flush
obj.flushPartial = windowBuffer.flushPartial
return obj
end
return WindowBuffer

View File

@ -1,13 +1,12 @@
local Logging = require("logging") local Logging = require("logging")
local LogLevel = Logging.LogLevel
local CACHE_FILE = "/.storage.cache" local CACHE_FILE = "/.storage.cache"
local LOCK_FILE = "/.storage.lock" local LOCK_FILE = "/.storage.lock"
local LOG_FILE_PATH = "latest.log" local LOG_FILE_PATH = "latest.log"
local Logger = Logging.firstLoad( local Logger = Logging.firstLoad(
Logging.LogLevel.TRACE, Logging.OUTPUTS.file(LOG_FILE_PATH),
Logging.OUTPUTS.combine( Logging.OUTPUTS.stdout(LogLevel.CRITICAL)
Logging.OUTPUTS.file(LOG_FILE_PATH)
--Logging.OUTPUTS.stdout
)
) )
local Chest = require("storage.chest") local Chest = require("storage.chest")

View File

@ -9,6 +9,8 @@ local LogLevel = {
ERROR = 6 ERROR = 6
} }
LogLevel._DEFAULT = LogLevel.TRACE
for k,v in pairs(LogLevel) do for k,v in pairs(LogLevel) do
LogLevel[v] = k LogLevel[v] = k
end end
@ -30,18 +32,24 @@ local Logger = {
} }
Logger.__index = Logger Logger.__index = Logger
function Logger:new(o)
local logger = {} local function combineOutputs(...)
local output = print local args = {...}
if type(o) == "table" then if #args == 0 then
if type(o.level) == "number" and LogLevel.isValid(o.level) then return function() end
logger.level = o.level elseif #args == 1 then
end return args[1]
if type(o.output) == "function" then end
output = o.output return function(text, level)
for _,v in ipairs(args) do
v(text, level)
end end
end end
logger.output = output end
-- TODO: Split logger outputs and specify LogLevel for each
function Logger:new(...)
local logger = { output = combineOutputs(...) or function() end }
setmetatable(logger, self) setmetatable(logger, self)
logger.__index = logger logger.__index = logger
@ -216,13 +224,11 @@ local function deepStringify(value)
end end
function Logger:_doPrint(level, message, ...) function Logger:_doPrint(level, message, ...)
if LogLevel.isGreaterOrEqual(level, self.level) then local result = { tostring(LogLevel[level]), deepStringify(message) }
local result = { tostring(LogLevel[level]), deepStringify(message) } for _,v in ipairs({...}) do
for _,v in ipairs({...}) do table.insert(result, deepStringify(v))
table.insert(result, deepStringify(v))
end
self.output(table.concat(result, " "))
end end
self.output(table.concat(result, " "), level)
end end
function Logger:trace(message, ...) function Logger:trace(message, ...)
@ -249,14 +255,8 @@ function Logger:error(message, ...)
self:_doPrint(LogLevel.ERROR, message, ...) self:_doPrint(LogLevel.ERROR, message, ...)
end end
function Logger:setOutput(output) function Logger:setOutput(...)
self.output = output self.output = combineOutputs(...)
end
function Logger:setLevel(level)
if LogLevel.isValid(level) then
self.level = LogLevel.asNumber(level)
end
end end
function Logger.plain(value, deep) function Logger.plain(value, deep)
@ -268,28 +268,33 @@ Logging.LogLevel = LogLevel
function Logging.getGlobalLogger() function Logging.getGlobalLogger()
if _G._GLOBAL_LOGGER == nil then if _G._GLOBAL_LOGGER == nil then
_G._GLOBAL_LOGGER = Logger:new{ level = LogLevel.DEBUG, output = print } _G._GLOBAL_LOGGER = Logger:new{ output = function() end }
end end
return _G._GLOBAL_LOGGER return _G._GLOBAL_LOGGER
end end
function Logging.firstLoad(level, output) function Logging.firstLoad(...)
Logging.resetAll() Logging.resetAll()
local logger = Logging.getGlobalLogger() local logger = Logging.getGlobalLogger()
logger:setOutput(output) logger:setOutput(...)
logger:setLevel(level)
return logger return logger
end end
Logging.OUTPUTS = {} Logging.OUTPUTS = {}
function Logging.OUTPUTS.file(name) function Logging.OUTPUTS.file(name, logLevel)
logLevel = logLevel or LogLevel._DEFAULT
local doWrite = function(file, text) local doWrite = function(file, text)
file.write(text) file.write(text)
file.write("\n") file.write("\n")
file.flush() file.flush()
end end
local file = fs.open(name, "w+") local file = fs.open(name, "w+")
return function(text) return function(text, level)
if level < logLevel then
return
end
if not pcall(doWrite, file, text) then if not pcall(doWrite, file, text) then
local dRes, dRet = pcall(fs.delete, name) local dRes, dRet = pcall(fs.delete, name)
if not dRes then if not dRes then
@ -304,17 +309,20 @@ function Logging.OUTPUTS.file(name)
end end
end end
end end
Logging.OUTPUTS.stdout = print
function Logging.OUTPUTS.combine(...) function Logging.OUTPUTS.transmit(modem, channel, logLevel)
local args = {...} logLevel = logLevel or LogLevel._DEFAULT
if #args == 0 then end
return function() end
end function Logging.OUTPUTS.stdout(logLevel)
return function(text) logLevel = logLevel or LogLevel._DEFAULT
for _,v in ipairs(args) do
v(text) return function(text, level)
if level < logLevel then
return
end end
print(text)
end end
end end

View File

@ -4,4 +4,18 @@ function Util.toHexChar(v)
return ("0123456789ABCDEF"):sub(v, v) return ("0123456789ABCDEF"):sub(v, v)
end end
function Util.fromHexChar(c)
local index = ({("0123456789ABCDEF"):find(c)})[1]
if index ~= nil then
return index - 1
end
return nil
end
function Util.boundCheck(x, y, w, h)
if x < 1 or x > w or y < 1 or y > h then
error(("Index out of range: (%d %d) is not within ([1,%d], [1,%d])"):format(x, y, w, h))
end
end
return Util return Util