slabOS/lib/basalt/main.lua
2025-06-02 10:23:40 +03:00

603 lines
15 KiB
Lua

local basaltEvent = require("basaltEvent")()
local baseObjects = require("loadObjects")
local moddedObjects
local pluginSystem = require("plugin")
local utils = require("utils")
local log = require("basaltLogs")
local uuid = utils.uuid
local wrapText = utils.wrapText
local count = utils.tableCount
local moveThrottle = 300
local dragThrottle = 0
local renderingThrottle = 0
local newObjects = {}
local baseTerm = term.current()
local version = "1.7.0"
local projectDirectory = fs.getDir(table.pack(...)[2] or "")
local activeKey, frames, monFrames, variables, schedules = {}, {}, {}, {}, {}
local mainFrame, activeFrame, focusedObject, updaterActive
local basalt = {}
if not term.isColor or not term.isColor() then
error('Basalt requires an advanced (golden) computer to run.', 0)
end
local defaultColors = {}
for k,v in pairs(colors)do
if(type(v)=="number")then
defaultColors[k] = {baseTerm.getPaletteColor(v)}
end
end
local function stop()
updaterActive = false
baseTerm.clear()
baseTerm.setCursorPos(1, 1)
for k,v in pairs(colors)do
if(type(v)=="number")then
baseTerm.setPaletteColor(v, colors.packRGB(table.unpack(defaultColors[k])))
end
end
end
local function schedule(f)
assert(f~="function", "Schedule needs a function in order to work!")
return function(...)
local co = coroutine.create(f)
local ok, result = coroutine.resume(co, ...)
if(ok)then
table.insert(schedules, co)
else
basalt.basaltError(result)
end
end
end
basalt.log = function(...)
log(...)
end
local setVariable = function(name, var)
variables[name] = var
end
local getVariable = function(name)
return variables[name]
end
local getObjects = function()
return moddedObjects
end
local getObject = function(objectName)
return getObjects()[objectName]
end
local createObject = function(basalt, objectName, id)
return getObject(objectName)(id, basalt)
end
local bInstance = {
getDynamicValueEventSetting = function()
return basalt.dynamicValueEvents
end,
getMainFrame = function()
return mainFrame
end,
setVariable = setVariable,
getVariable = getVariable,
setMainFrame = function(mFrame)
mainFrame = mFrame
end,
getActiveFrame = function()
return activeFrame
end,
setActiveFrame = function(aFrame)
activeFrame = aFrame
end,
getFocusedObject = function()
return focusedObject
end,
setFocusedObject = function(focused)
focusedObject = focused
end,
getMonitorFrame = function(name)
return monFrames[name] or monGroups[name][1]
end,
setMonitorFrame = function(name, frame, isGroupedMon)
if(mainFrame == frame)then mainFrame = nil end
if(isGroupedMon)then
monGroups[name] = {frame, sides}
else
monFrames[name] = frame
end
if(frame==nil)then
monGroups[name] = nil
end
end,
getTerm = function()
return baseTerm
end,
schedule = schedule,
stop = stop,
debug = basalt.debug,
log = basalt.log,
getObjects = getObjects,
getObject = getObject,
createObject = createObject,
getDirectory = function()
return projectDirectory
end
}
local function defaultErrorHandler(errMsg)
baseTerm.clear()
baseTerm.setBackgroundColor(colors.black)
baseTerm.setTextColor(colors.red)
local w,h = baseTerm.getSize()
if(basalt.logging)then
log(errMsg, "Error")
end
local text = wrapText("Basalt error: "..errMsg, w)
local yPos = 1
for _,v in pairs(text)do
baseTerm.setCursorPos(1,yPos)
baseTerm.write(v)
yPos = yPos + 1
end
baseTerm.setCursorPos(1,yPos+1)
updaterActive = false
end
local function handleSchedules(event, p1, p2, p3, p4)
if(#schedules>0)then
local finished = {}
for n=1,#schedules do
if(schedules[n]~=nil)then
if (coroutine.status(schedules[n]) == "suspended")then
local ok, result = coroutine.resume(schedules[n], event, p1, p2, p3, p4)
if not(ok)then
basalt.basaltError(result)
end
else
table.insert(finished, n)
end
end
end
for n=1,#finished do
table.remove(schedules, finished[n]-(n-1))
end
end
end
local function drawFrames()
if(updaterActive==false)then return end
if(mainFrame~=nil)then
mainFrame:render()
mainFrame:updateTerm()
end
for _,v in pairs(monFrames)do
v:render()
v:updateTerm()
end
end
local stopped, moveX, moveY = nil, nil, nil
local moveTimer = nil
local function mouseMoveEvent(_, stp, x, y)
stopped, moveX, moveY = stp, x, y
if(moveTimer==nil)then
moveTimer = os.startTimer(moveThrottle/1000)
end
end
local function moveHandlerTimer()
moveTimer = nil
mainFrame:hoverHandler(moveX, moveY, stopped)
activeFrame = mainFrame
end
local btn, dragX, dragY = nil, nil, nil
local dragTimer = nil
local function dragHandlerTimer()
dragTimer = nil
mainFrame:dragHandler(btn, dragX, dragY)
activeFrame = mainFrame
end
local function mouseDragEvent(_, b, x, y)
btn, dragX, dragY = b, x, y
if(dragThrottle<50)then
dragHandlerTimer()
else
if(dragTimer==nil)then
dragTimer = os.startTimer(dragThrottle/1000)
end
end
end
local renderingTimer = nil
local function renderingUpdateTimer()
renderingTimer = nil
drawFrames()
end
local function renderingUpdateEvent(timer)
if(renderingThrottle<50)then
drawFrames()
else
if(renderingTimer==nil)then
renderingTimer = os.startTimer(renderingThrottle/1000)
end
end
end
local function basaltUpdateEvent(event, ...)
local a = {...}
if(basaltEvent:sendEvent("basaltEventCycle", event, ...)==false)then return end
if(event=="terminate")then basalt.stop() end
if(mainFrame~=nil)then
local mouseEvents = {
mouse_click = mainFrame.mouseHandler,
mouse_up = mainFrame.mouseUpHandler,
mouse_scroll = mainFrame.scrollHandler,
mouse_drag = mouseDragEvent,
mouse_move = mouseMoveEvent,
}
local mouseEvent = mouseEvents[event]
if(mouseEvent~=nil)then
mouseEvent(mainFrame, ...)
handleSchedules(event, ...)
renderingUpdateEvent()
return
end
end
if(event == "monitor_touch") then
for k,v in pairs(monFrames)do
if(v:mouseHandler(1, a[2], a[3], true, a[1]))then
activeFrame = v
end
end
handleSchedules(event, ...)
renderingUpdateEvent()
return
end
if(activeFrame~=nil)then
local keyEvents = {
char = activeFrame.charHandler,
key = activeFrame.keyHandler,
key_up = activeFrame.keyUpHandler,
}
local keyEvent = keyEvents[event]
if(keyEvent~=nil)then
if(event == "key")then
activeKey[a[1]] = true
elseif(event == "key_up")then
activeKey[a[1]] = false
end
keyEvent(activeFrame, ...)
handleSchedules(event, ...)
renderingUpdateEvent()
return
end
end
if(event=="timer")and(a[1]==moveTimer)then
moveHandlerTimer()
elseif(event=="timer")and(a[1]==dragTimer)then
dragHandlerTimer()
elseif(event=="timer")and(a[1]==renderingTimer)then
renderingUpdateTimer()
else
for _, v in pairs(frames) do
v:eventHandler(event, ...)
end
for _, v in pairs(monFrames) do
v:eventHandler(event, ...)
end
handleSchedules(event, ...)
renderingUpdateEvent()
end
end
local loadedObjects = false
local loadedPlugins = false
local function InitializeBasalt()
if not(loadedObjects)then
for _,v in pairs(newObjects)do
if(fs.exists(v))then
if(fs.isDir(v))then
local files = fs.list(v)
for _,object in pairs(files)do
if not(fs.isDir(v.."/"..object))then
local name = object:gsub(".lua", "")
if(name~="example.lua")and not(name:find(".disabled"))then
if(baseObjects[name]==nil)then
baseObjects[name] = require(v.."."..object:gsub(".lua", ""))
else
error("Duplicate object name: "..name)
end
end
end
end
else
local name = v:gsub(".lua", "")
if(baseObjects[name]==nil)then
baseObjects[name] = require(name)
else
error("Duplicate object name: "..name)
end
end
end
end
loadedObjects = true
end
if not(loadedPlugins)then
moddedObjects = pluginSystem.loadPlugins(baseObjects, bInstance)
local basaltPlugins = pluginSystem.get("basalt")
if(basaltPlugins~=nil)then
for k,v in pairs(basaltPlugins)do
for a,b in pairs(v(basalt))do
basalt[a] = b
bInstance[a] = b
end
end
end
local basaltPlugins = pluginSystem.get("basaltInternal")
if(basaltPlugins~=nil)then
for _,v in pairs(basaltPlugins)do
for a,b in pairs(v(basalt))do
bInstance[a] = b
end
end
end
loadedPlugins = true
end
end
local function createFrame(name)
InitializeBasalt()
for _, v in pairs(frames) do
if (v:getName() == name) then
return nil
end
end
local newFrame = moddedObjects["BaseFrame"](name, bInstance)
newFrame:init()
newFrame:load()
newFrame:draw()
table.insert(frames, newFrame)
if(mainFrame==nil)and(newFrame:getName()~="basaltDebuggingFrame")then
newFrame:show()
end
return newFrame
end
basalt = {
basaltError = defaultErrorHandler,
logging = false,
dynamicValueEvents = false,
drawFrames = drawFrames,
log = log,
getVersion = function()
return version
end,
memory = function()
return math.floor(collectgarbage("count")+0.5).."KB"
end,
addObject = function(path)
if(fs.exists(path))then
table.insert(newObjects, path)
end
end,
addPlugin = function(path)
pluginSystem.addPlugin(path)
end,
getAvailablePlugins = function()
return pluginSystem.getAvailablePlugins()
end,
getAvailableObjects = function()
local objectNames = {}
for k,_ in pairs(baseObjects)do
table.insert(objectNames, k)
end
return objectNames
end,
setVariable = setVariable,
getVariable = getVariable,
getObjects = getObjects,
getObject = getObject,
createObject = createObject,
setBaseTerm = function(_baseTerm)
baseTerm = _baseTerm
end,
resetPalette = function()
for k,v in pairs(colors)do
if(type(v)=="number")then
--baseTerm.setPaletteColor(v, colors.packRGB(table.unpack(defaultColors[k])))
end
end
end,
setMouseMoveThrottle = function(amount)
if(_HOST:find("CraftOS%-PC"))then
if(config.get("mouse_move_throttle")~=10)then config.set("mouse_move_throttle", 10) end
if(amount<100)then
moveThrottle = 100
else
moveThrottle = amount
end
return true
end
return false
end,
setRenderingThrottle = function(amount)
if(amount<=0)then
renderingThrottle = 0
else
renderingTimer = nil
renderingThrottle = amount
end
end,
setMouseDragThrottle = function(amount)
if(amount<=0)then
dragThrottle = 0
else
dragTimer = nil
dragThrottle = amount
end
end,
autoUpdate = function(isActive)
updaterActive = isActive
if(isActive==nil)then updaterActive = true end
local function f()
drawFrames()
while updaterActive do
basaltUpdateEvent(os.pullEventRaw())
end
end
while updaterActive do
local ok, err = xpcall(f, debug.traceback)
if not(ok)then
basalt.basaltError(err)
end
end
end,
update = function(event, ...)
if (event ~= nil) then
local args = {...}
local ok, err = xpcall(function() basaltUpdateEvent(event, table.unpack(args)) end, debug.traceback)
if not(ok)then
basalt.basaltError(err)
return
end
end
end,
stop = stop,
stopUpdate = stop,
isKeyDown = function(key)
if(activeKey[key]==nil)then return false end
return activeKey[key];
end,
getFrame = function(name)
for _, value in pairs(frames) do
if (value.name == name) then
return value
end
end
end,
getActiveFrame = function()
return activeFrame
end,
setActiveFrame = function(frame)
if (frame:getType() == "Container") then
activeFrame = frame
return true
end
return false
end,
getMainFrame = function()
return mainFrame
end,
onEvent = function(...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
basaltEvent:registerEvent("basaltEventCycle", v)
end
end
end,
schedule = schedule,
addFrame = createFrame,
createFrame = createFrame,
addMonitor = function(name)
InitializeBasalt()
for _, v in pairs(frames) do
if (v:getName() == name) then
return nil
end
end
local newFrame = moddedObjects["MonitorFrame"](name, bInstance)
newFrame:init()
newFrame:load()
newFrame:draw()
table.insert(monFrames, newFrame)
return newFrame
end,
removeFrame = function(name)
frames[name] = nil
end,
setProjectDir = function(dir)
projectDirectory = dir
end,
}
local basaltPlugins = pluginSystem.get("basalt")
if(basaltPlugins~=nil)then
for k,v in pairs(basaltPlugins)do
for a,b in pairs(v(basalt))do
basalt[a] = b
bInstance[a] = b
end
end
end
local basaltPlugins = pluginSystem.get("basaltInternal")
if(basaltPlugins~=nil)then
for k,v in pairs(basaltPlugins)do
for a,b in pairs(v(basalt))do
bInstance[a] = b
end
end
end
return basalt