PRE-ALPHA 3.0.0 - Rewrote the kernel to use a more modular design, changed some terms, added process sandboxing for security.
COMING IN THE FULL RELEASE: - A user system - A functional IPC (Inter-Process Communication) system THINGS CAN AND WILL CHANGE FROM NOW UNTIL THE FINAL RELEASE.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
local packages = {...}
|
||||
local command = packages[1]
|
||||
table.remove(packages, 1)
|
||||
local fs = import("filesystem")
|
||||
local component = import("component")
|
||||
local agReg = import("/argentum/registry.cfg")
|
||||
local fs = require("filesystem")
|
||||
local component = require("component")
|
||||
local agReg = require("/argentum/registry.cfg")
|
||||
if not command then
|
||||
shell.run("help argentum")
|
||||
return
|
||||
@@ -346,7 +346,7 @@ if command == "install" then
|
||||
else
|
||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
||||
end
|
||||
agReg = import("/argentum/registry.cfg")
|
||||
agReg = require("/argentum/registry.cfg")
|
||||
while true do
|
||||
if not doChecks(packages[i]) then
|
||||
table.insert(fails, packages[i])
|
||||
@@ -497,7 +497,7 @@ elseif command == "update" then
|
||||
else
|
||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
||||
end
|
||||
agReg = import("/argentum/registry.cfg")
|
||||
agReg = require("/argentum/registry.cfg")
|
||||
if not packages[1] then
|
||||
local packagesInstalled = fs.list("/argentum/store/")
|
||||
for _, currentPackage in pairs(packagesInstalled) do
|
||||
@@ -632,7 +632,7 @@ elseif command == "info" then
|
||||
else
|
||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
||||
end
|
||||
agReg = import("/argentum/registry.cfg")
|
||||
agReg = require("/argentum/registry.cfg")
|
||||
if not agReg[packages[1]] and not source then
|
||||
print("\27[91mPackage " .. packages[1] .. " does not exist.")
|
||||
return
|
||||
@@ -656,7 +656,7 @@ elseif command == "search" then
|
||||
else
|
||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
||||
end
|
||||
agReg = import("/argentum/registry.cfg")
|
||||
agReg = require("/argentum/registry.cfg")
|
||||
local searchResults = {}
|
||||
for packageName, _ in pairs(agReg) do
|
||||
if packageName:find(packages[1], 1, true) then
|
||||
@@ -679,7 +679,7 @@ elseif command == "list" then
|
||||
else
|
||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
||||
end
|
||||
agReg = import("/argentum/registry.cfg")
|
||||
agReg = require("/argentum/registry.cfg")
|
||||
local sortedPackages = {}
|
||||
for packageName, _ in pairs(agReg) do
|
||||
table.insert(sortedPackages, packageName)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local component = import("component")
|
||||
local computer = import("computer")
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local args = {...}
|
||||
|
||||
local force = false
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
local files = {...}
|
||||
local fs = import("filesystem")
|
||||
local fs = require("filesystem")
|
||||
if not files or not files[1] then
|
||||
shell.run("help cat")
|
||||
return
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
local directory = ...
|
||||
local fs = import("filesystem")
|
||||
local fs = require("filesystem")
|
||||
|
||||
if not directory then
|
||||
return
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
local fromFile, toFile = ...
|
||||
local fs = import("filesystem")
|
||||
local fs = require("filesystem")
|
||||
|
||||
if not fromFile or not toFile then
|
||||
shell.run("help cp")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
local url = ...
|
||||
|
||||
local component = import("component")
|
||||
local fs = import("filesystem")
|
||||
local component = require("component")
|
||||
local fs = require("filesystem")
|
||||
|
||||
if not component.list("internet")() then
|
||||
print("\27[91mThis program requires an internet card to run.")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
local file = ...
|
||||
local fs = import("filesystem")
|
||||
local event = import("event")
|
||||
local component = import("component")
|
||||
local unicode = import("unicode")
|
||||
local fs = require("filesystem")
|
||||
local event = require("event")
|
||||
local component = require("component")
|
||||
local unicode = require("unicode")
|
||||
local gpu = component.gpu
|
||||
local width, height = gpu.getResolution()
|
||||
local scrollPosX, scrollPosY = 1, 1
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
local component = import("component")
|
||||
local computer = import("computer")
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
|
||||
local function printstat(text)
|
||||
termlib.cursorPosX = 35
|
||||
termlib.write(text .. "\n", false)
|
||||
terminal.cursorPosX = 35
|
||||
terminal.write(text .. "\n", false)
|
||||
end
|
||||
|
||||
termlib.write(_OSLOGO, false)
|
||||
termlib.cursorPosY = termlib.cursorPosY - 17
|
||||
terminal.write(_OSLOGO, false)
|
||||
terminal.cursorPosY = terminal.cursorPosY - 17
|
||||
printstat("\27[92mOS\27[0m: ".._OSVERSION)
|
||||
printstat("\27[92mArchitecture\27[0m: ".._VERSION)
|
||||
local componentCounter = 0
|
||||
@@ -15,7 +15,7 @@ for _, _ in component.list() do
|
||||
componentCounter = componentCounter + 1
|
||||
end
|
||||
printstat("\27[92mComponents\27[0m: "..tostring(componentCounter))
|
||||
printstat("\27[92mCoroutines\27[0m: "..tostring(#cormgr.corList))
|
||||
printstat("\27[92mCoroutines\27[0m: "..tostring(#tsched.getTasks()))
|
||||
printstat("\27[92mBattery\27[0m: "..tostring(math.floor(computer.energy() / computer.maxEnergy() * 1000 + 0.5) / 10).."%")
|
||||
local totalMemory = computer.totalMemory()
|
||||
local usedMemory = computer.totalMemory() - computer.freeMemory()
|
||||
@@ -67,4 +67,4 @@ local width, height = component.invoke(component.list("gpu")(), "getResolution")
|
||||
printstat("\27[92mResolution\27[0m: "..tostring(width).."x"..tostring(height).."\n")
|
||||
printstat("\27[40m \27[41m \27[42m \27[43m \27[44m \27[45m \27[46m \27[47m ")
|
||||
printstat("\27[100m \27[101m \27[102m \27[103m \27[104m \27[105m \27[106m \27[107m ")
|
||||
termlib.cursorPosY = termlib.cursorPosY + 5
|
||||
terminal.cursorPosY = terminal.cursorPosY + 5
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
local fs = import("filesystem")
|
||||
local fs = require("filesystem")
|
||||
local args = {...}
|
||||
local command = args[1]
|
||||
args = nil
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local component = import("component")
|
||||
local computer = import("computer")
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local args = {...}
|
||||
if not args then return print("\x1b[91mCannot get arguments.") end
|
||||
if not args[1] then
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
local args = {...}
|
||||
local target = args[1]
|
||||
args = nil
|
||||
local fs = import("filesystem")
|
||||
local unicode = import("unicode")
|
||||
local fs = require("filesystem")
|
||||
local unicode = require("unicode")
|
||||
local maxLength = 0
|
||||
local margin = 2 -- minimum space between filename and size
|
||||
local dirTable = {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
local serialize = import("serialize")
|
||||
local component = import("component")
|
||||
local computer = import("computer")
|
||||
local unicode = import("unicode")
|
||||
local serialize = require("serialize")
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local unicode = require("unicode")
|
||||
|
||||
local width,height = component.gpu.getResolution()
|
||||
|
||||
|
||||
+2
-2
@@ -1,13 +1,13 @@
|
||||
print("\27[44m".._VERSION.."\27[0m shell")
|
||||
print('Type "exit" to exit.')
|
||||
termlib.readHistory["lua"] = {""}
|
||||
local fs = import("filesystem")
|
||||
local fs = require("filesystem")
|
||||
|
||||
local loadedLibraries = ""
|
||||
local libList = fs.list("halyde/lib")
|
||||
for _, lib in pairs(libList) do
|
||||
if lib:match("(.+)%.lua") then
|
||||
loadedLibraries = loadedLibraries .. "local " .. lib:match("(.+)%.lua") .. ' = import("' .. lib:match("(.+)%.lua") .. '")\n'
|
||||
loadedLibraries = loadedLibraries .. "local " .. lib:match("(.+)%.lua") .. ' = require("' .. lib:match("(.+)%.lua") .. '")\n'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
local computer = import("computer")
|
||||
local computer = require("computer")
|
||||
|
||||
if type(computer)~="table" then
|
||||
return print("\x1b[91mComputer library returned '"..type(computer).."' type\x1b[39m")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local directory = ...
|
||||
local fs = import("filesystem")
|
||||
local fs = require("filesystem")
|
||||
|
||||
if not directory then
|
||||
shell.run("help mkdir")
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
local fromFile, toFile = ...
|
||||
local fs = import("filesystem")
|
||||
local fs = require("filesystem")
|
||||
|
||||
if not fromFile or not toFile then
|
||||
shell.run("help mv")
|
||||
|
||||
@@ -1 +1 @@
|
||||
import("computer").shutdown(true)
|
||||
require("computer").shutdown(true)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
local file = ...
|
||||
local fs = import("filesystem")
|
||||
local fs = require("filesystem")
|
||||
|
||||
if not file then
|
||||
shell.run("help rm")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
local raster = import("raster")
|
||||
local raster = require("raster")
|
||||
|
||||
raster.init()
|
||||
|
||||
@@ -15,7 +15,7 @@ end]]
|
||||
end
|
||||
end ]]
|
||||
|
||||
local event = import("event")
|
||||
local event = require("event")
|
||||
local x=0
|
||||
local y=0
|
||||
local vx=1
|
||||
@@ -88,4 +88,4 @@ end
|
||||
end ]]
|
||||
|
||||
raster.free()
|
||||
termlib.cursorPosY=1
|
||||
termlib.cursorPosY=1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
local component = import("component")
|
||||
local computer = import("computer")
|
||||
local raster = import("raster")
|
||||
local event = import("event")
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local raster = require("raster")
|
||||
local event = require("event")
|
||||
|
||||
-- Initialize the 3D renderer for a spinning cube
|
||||
-- Using the raster library for drawing
|
||||
|
||||
@@ -1 +1 @@
|
||||
import("computer").shutdown()
|
||||
require("computer").shutdown()
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"prompt":"\u001b[92m%s > \u001b[0m","aliases":{"move":"mv","copy":"cp","ag":"argentum","rename":"mv","..":"cd ..","man":"help","del":"rm","delete":"rm","ren":"mv","remove":"rm","list":"ls","wget":"download","dir":"ls","ps":"lscor","poweroff":"shutdown","restart":"reboot","lsblk":"lsdrv"},"splashMessages":["Made by John Haly- I mean Cerulean Blue.","Welcome! Type \"help\" to get started.","Also try KOCOS!","Welcome back, Commander. We have no idea what we're doing.","99.9% bug-free. The remaining 0.1% are features.","0 days since last error.","Everything is fine. The fire is decorative.","Please don't feed the background processes.","Also has fetch!","Anything red is no man's land. Trust me.","Machine...","Abort, Retry, Fail?","What's the deal with /argentum/store?","So cutting-edge you can't hold it in your hand.","Americans are the reason you see colors on-screen. I'm talking about ANSI escape codes, not politics.","Shoutout to Ponali!"],"path":["/halyde/apps/"],"startupMessage":"\n │\n │ %s\n │ %s\n │\n ","defaultWorkingDirectory":"/home/"}
|
||||
{"prompt":"\u001b[92m%s > \u001b[0m","aliases":{"move":"mv","copy":"cp","ag":"argentum","rename":"mv","..":"cd ..","man":"help","del":"rm","delete":"rm","ren":"mv","remove":"rm","list":"ls","wget":"download","dir":"ls","ps":"lscor","poweroff":"shutdown","restart":"reboot","lsblk":"lsdrv"},"splashMessages":["Made by John Haly- I mean Cerulean Blue.","Welcome! Type \"help\" to get started.","Also try KOCOS!","Welcome back, Commander. We have no idea what we're doing.","99.9% bug-free. The remaining 0.1% are features.","0 days since last error.","Everything is fine. The fire is decorative.","Please don't feed the background processes.","Also has fetch!","Anything red is no man's land. Trust me.","Machine...","Abort, Retry, Fail?","What's the deal with /argentum/store?","So cutting-edge you can't hold it in your hand.","Americans are the reason you see colors on-screen. I'm talking about ANSI escape codes, not politics.","Shoutout to Ponali!","Now i% more secure!"],"path":["/halyde/apps/"],"startupMessage":"\n │\n │ %s\n │ %s\n │\n ","defaultWorkingDirectory":"/home/"}
|
||||
|
||||
@@ -1 +1 @@
|
||||
["/halyde/core/fullkb.lua","/halyde/core/evmgr.lua","/halyde/core/drvload.lua","/halyde/core/shell.lua"]
|
||||
["/halyde/shell/shell.lua"]
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
local loadfile = ...
|
||||
local filesystem = loadfile("/halyde/lib/filesystem.lua")(loadfile)
|
||||
|
||||
_G._OSVERSION = "Halyde 2.8.1"
|
||||
_G._OSLOGO = ""
|
||||
local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge)
|
||||
_OSLOGO = _OSLOGO .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
|
||||
_G.package = {["preloaded"] = {}}
|
||||
|
||||
loadfile("/halyde/core/datatools.lua")()
|
||||
|
||||
function _G.import(module, ...)
|
||||
local args = table.pack(...)
|
||||
if package.preloaded[module] then
|
||||
return package.preloaded[module]
|
||||
end
|
||||
local modulepath
|
||||
if filesystem.exists(module) then
|
||||
modulepath = module
|
||||
elseif filesystem.exists("/halyde/lib/"..module..".lua") then
|
||||
modulepath = "/halyde/lib/"..module..".lua"
|
||||
elseif shell and shell.workingDirectory and filesystem.exists(shell.workingDirectory..module) then
|
||||
modulepath = shell.workingDirectory..module
|
||||
end
|
||||
assert(modulepath, "module not found\npossible locations:\n/halyde/lib/"..module..".lua")
|
||||
local handle, data, tmpdata = filesystem.open(modulepath), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
handle:close()
|
||||
return(assert(load(data, "="..modulepath))(table.unpack(args)))
|
||||
end
|
||||
|
||||
local function preload(module)
|
||||
local handle, data, tmpdata = assert(filesystem.open("/halyde/lib/" .. module .. ".lua", "r")), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
package.preloaded[module] = assert(load(data, "="..module))()
|
||||
_G[module] = nil
|
||||
end
|
||||
|
||||
preload("component")
|
||||
preload("computer")
|
||||
preload("filesystem")
|
||||
|
||||
local component = import("component")
|
||||
local gpu = component.gpu
|
||||
local screenAddress = component.list("screen")()
|
||||
--local screen = component.screen
|
||||
|
||||
gpu.bind(screenAddress)
|
||||
--local maxWidth, maxHeight = gpu.maxResolution()
|
||||
--local aspectX, aspectY = screen.getAspectRatio()
|
||||
--local screenRatio = aspectX * 2 / aspectY
|
||||
|
||||
-- Calculate potential dimensions
|
||||
--local widthLimited = math.floor(maxHeight * screenRatio)
|
||||
--local heightLimited = math.floor(maxWidth / screenRatio)
|
||||
|
||||
--local targetWidth, targetHeight
|
||||
|
||||
--if widthLimited <= maxWidth then
|
||||
-- height is the limiting factor
|
||||
-- targetWidth = widthLimited
|
||||
-- targetHeight = maxHeight
|
||||
--else
|
||||
-- width is the limiting factor
|
||||
-- targetWidth = maxWidth
|
||||
-- targetHeight = heightLimited
|
||||
--end
|
||||
|
||||
--targetWidth = math.min(targetWidth, maxWidth)
|
||||
--targetHeight = math.min(targetHeight, maxHeight)
|
||||
|
||||
--gpu.setResolution(targetWidth, targetHeight)
|
||||
gpu.setResolution(gpu.maxResolution())
|
||||
|
||||
--local handle = assert(filesystem.open("/bazinga.txt", "w"))
|
||||
--assert(handle:write("Bazinga!"))
|
||||
--handle:close()
|
||||
|
||||
local fs = import("filesystem")
|
||||
if not fs.exists("/halyde/config/shell.json") then -- Auto-generate configs
|
||||
fs.copy("/halyde/config/generate/shell.json", "/halyde/config/shell.json")
|
||||
end
|
||||
if not fs.exists("/halyde/config/startupapps.json") then
|
||||
fs.copy("/halyde/config/generate/startupapps.json", "/halyde/config/startupapps.json")
|
||||
end
|
||||
fs = nil
|
||||
|
||||
import("/halyde/core/cormgr.lua")
|
||||
@@ -1,97 +0,0 @@
|
||||
_G.cormgr = {}
|
||||
_G.cormgr.corList = {}
|
||||
_G.cormgr.labelList = {}
|
||||
|
||||
local component = import("component")
|
||||
local filesystem = import("filesystem")
|
||||
local json = import("json")
|
||||
local gpu = component.gpu
|
||||
--local ocelot = component.ocelot
|
||||
|
||||
function _G.cormgr.loadCoroutine(path,...)
|
||||
local args = {...}
|
||||
local function corFunction()
|
||||
local result, errorMessage = xpcall(function(...)
|
||||
import(...)
|
||||
end, function(errorMessage)
|
||||
return errorMessage .. "\n \n" .. debug.traceback()
|
||||
end, path, table.unpack(args))
|
||||
if not result then
|
||||
if print then
|
||||
gpu.freeAllBuffers()
|
||||
print("\n\27[91m" .. errorMessage)
|
||||
else
|
||||
error(errorMessage)
|
||||
end
|
||||
end
|
||||
--import(path, table.unpack(args))
|
||||
end
|
||||
cormgr.addCoroutine(corFunction, string.match(tostring(path), "([^/]+)%.lua$"))
|
||||
end
|
||||
|
||||
function _G.cormgr.addCoroutine(func, name)
|
||||
local cor = coroutine.create(func)
|
||||
table.insert(cormgr.corList, cor)
|
||||
table.insert(cormgr.labelList, name)
|
||||
return cor
|
||||
end
|
||||
|
||||
function _G.cormgr.removeCoroutine(name)
|
||||
local index = table.find(cormgr.labelList, cor)
|
||||
table.remove(cormgr.corList, index)
|
||||
table.remove(cormgr.labelList, index)
|
||||
--coroutine.close(cor)
|
||||
end
|
||||
|
||||
function handleError(errormsg)
|
||||
if errormsg == nil then
|
||||
error("unknown error")
|
||||
else
|
||||
error(tostring(errormsg).."\n \n"..debug.traceback())
|
||||
end
|
||||
end
|
||||
|
||||
local function runCoroutines()
|
||||
for i = 1, #_G.cormgr.corList do
|
||||
if cormgr.corList[i] then
|
||||
local result, errorMessage = coroutine.resume(cormgr.corList[i])
|
||||
if cormgr.corList[i] then
|
||||
if not result then
|
||||
handleError(errorMessage)
|
||||
end
|
||||
if coroutine.status(cormgr.corList[i]) == "dead" then
|
||||
table.remove(cormgr.corList, i)
|
||||
table.remove(cormgr.labelList, i)
|
||||
--ocelot.log("Removed coroutine")
|
||||
i = i - 1
|
||||
end
|
||||
--computer.pullSignal(0)
|
||||
--coroutine.yield()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local handle, data, tmpdata = filesystem.open("/halyde/config/startupapps.json", "r"), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
handle:close()
|
||||
for _, line in ipairs(json.decode(data)) do
|
||||
if line ~= "" then
|
||||
--[[ if _G.print then
|
||||
print(line)
|
||||
end ]]
|
||||
_G.cormgr.loadCoroutine(line)
|
||||
runCoroutines()
|
||||
end
|
||||
end
|
||||
-- _G.cormgr.loadCoroutine("/halyde/core/shell.lua")
|
||||
|
||||
while true do
|
||||
runCoroutines()
|
||||
if #_G.cormgr.corList == 0 then
|
||||
computer.shutdown()
|
||||
end
|
||||
end
|
||||
@@ -1,46 +0,0 @@
|
||||
local fs = import("filesystem")
|
||||
|
||||
local driverPath = "/halyde/drivers"
|
||||
|
||||
local drivers = fs.list(driverPath)
|
||||
local driverTypes = {}
|
||||
|
||||
local function loadDriver(drvName)
|
||||
local driverData = import(fs.concat(driverPath, drvName))
|
||||
table.remove(drivers, table.find(drivers, drvName))
|
||||
if driverData.dependencies then
|
||||
for _, dependency in pairs(driverData.dependencies) do
|
||||
if table.find(drivers, dependency) then
|
||||
loadDriver(dependency)
|
||||
elseif table.find(drivers, dependency .. ".lua") then
|
||||
loadDriver(dependency .. ".lua")
|
||||
else
|
||||
for typeLookupDrvName, typeLookupDrvType in pairs(driverTypes) do
|
||||
if typeLookupDrvType == dependency then
|
||||
loadDriver(typeLookupDrvName)
|
||||
-- Don't break, because there can be multiple drivers of the correct type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
--print(drvName)
|
||||
if driverData.onStartup then -- I have no idea why would this not exist, but it's a failsafe
|
||||
driverData.onStartup()
|
||||
end
|
||||
-- More functions to be implemented in the future
|
||||
end
|
||||
|
||||
for _, drvName in pairs(drivers) do -- Get all the driver types
|
||||
local driverData = import(fs.concat(driverPath, drvName))
|
||||
if driverData.type then
|
||||
--print(driverData.type)
|
||||
driverTypes[drvName] = driverData.type -- Not the other way around because there can be multiple drivers of the same type, but there can't be multiple entries with the same key
|
||||
end
|
||||
end
|
||||
|
||||
for _, drvName in pairs(drivers) do -- Load the drivers
|
||||
if drvName:sub(-1, -1) ~= "/" then -- Check if it's not a directory. Otherwise it might be driver config
|
||||
loadDriver(drvName)
|
||||
end
|
||||
end
|
||||
@@ -1,143 +0,0 @@
|
||||
_G.keyboard = {["keys"] = {}}
|
||||
|
||||
keyboard.keys["1"] = 0x02
|
||||
keyboard.keys["2"] = 0x03
|
||||
keyboard.keys["3"] = 0x04
|
||||
keyboard.keys["4"] = 0x05
|
||||
keyboard.keys["5"] = 0x06
|
||||
keyboard.keys["6"] = 0x07
|
||||
keyboard.keys["7"] = 0x08
|
||||
keyboard.keys["8"] = 0x09
|
||||
keyboard.keys["9"] = 0x0A
|
||||
keyboard.keys["0"] = 0x0B
|
||||
keyboard.keys.a = 0x1E
|
||||
keyboard.keys.b = 0x30
|
||||
keyboard.keys.c = 0x2E
|
||||
keyboard.keys.d = 0x20
|
||||
keyboard.keys.e = 0x12
|
||||
keyboard.keys.f = 0x21
|
||||
keyboard.keys.g = 0x22
|
||||
keyboard.keys.h = 0x23
|
||||
keyboard.keys.i = 0x17
|
||||
keyboard.keys.j = 0x24
|
||||
keyboard.keys.k = 0x25
|
||||
keyboard.keys.l = 0x26
|
||||
keyboard.keys.m = 0x32
|
||||
keyboard.keys.n = 0x31
|
||||
keyboard.keys.o = 0x18
|
||||
keyboard.keys.p = 0x19
|
||||
keyboard.keys.q = 0x10
|
||||
keyboard.keys.r = 0x13
|
||||
keyboard.keys.s = 0x1F
|
||||
keyboard.keys.t = 0x14
|
||||
keyboard.keys.u = 0x16
|
||||
keyboard.keys.v = 0x2F
|
||||
keyboard.keys.w = 0x11
|
||||
keyboard.keys.x = 0x2D
|
||||
keyboard.keys.y = 0x15
|
||||
keyboard.keys.z = 0x2C
|
||||
|
||||
keyboard.keys.apostrophe = 0x28
|
||||
keyboard.keys.at = 0x91
|
||||
keyboard.keys.back = 0x0E -- backspace
|
||||
keyboard.keys.backslash = 0x2B
|
||||
keyboard.keys.capital = 0x3A -- capslock
|
||||
keyboard.keys.colon = 0x92
|
||||
keyboard.keys.comma = 0x33
|
||||
keyboard.keys.enter = 0x1C
|
||||
keyboard.keys.equals = 0x0D
|
||||
keyboard.keys.grave = 0x29 -- accent grave
|
||||
keyboard.keys.lbracket = 0x1A
|
||||
keyboard.keys.lcontrol = 0x1D
|
||||
keyboard.keys.lmenu = 0x38 -- left Alt
|
||||
keyboard.keys.lshift = 0x2A
|
||||
keyboard.keys.minus = 0x0C
|
||||
keyboard.keys.numlock = 0x45
|
||||
keyboard.keys.pause = 0xC5
|
||||
keyboard.keys.period = 0x34
|
||||
keyboard.keys.rbracket = 0x1B
|
||||
keyboard.keys.rcontrol = 0x9D
|
||||
keyboard.keys.rmenu = 0xB8 -- right Alt
|
||||
keyboard.keys.rshift = 0x36
|
||||
keyboard.keys.scroll = 0x46 -- Scroll Lock
|
||||
keyboard.keys.semicolon = 0x27
|
||||
keyboard.keys.slash = 0x35 -- / on main keyboard
|
||||
keyboard.keys.space = 0x39
|
||||
keyboard.keys.stop = 0x95
|
||||
keyboard.keys.tab = 0x0F
|
||||
keyboard.keys.underline = 0x93
|
||||
|
||||
-- Keypad (and numpad with numlock off)
|
||||
keyboard.keys.up = 0xC8
|
||||
keyboard.keys.down = 0xD0
|
||||
keyboard.keys.left = 0xCB
|
||||
keyboard.keys.right = 0xCD
|
||||
keyboard.keys.home = 0xC7
|
||||
keyboard.keys["end"] = 0xCF
|
||||
keyboard.keys.pageUp = 0xC9
|
||||
keyboard.keys.pageDown = 0xD1
|
||||
keyboard.keys.insert = 0xD2
|
||||
keyboard.keys.delete = 0xD3
|
||||
|
||||
-- Function keys
|
||||
keyboard.keys.f1 = 0x3B
|
||||
keyboard.keys.f2 = 0x3C
|
||||
keyboard.keys.f3 = 0x3D
|
||||
keyboard.keys.f4 = 0x3E
|
||||
keyboard.keys.f5 = 0x3F
|
||||
keyboard.keys.f6 = 0x40
|
||||
keyboard.keys.f7 = 0x41
|
||||
keyboard.keys.f8 = 0x42
|
||||
keyboard.keys.f9 = 0x43
|
||||
keyboard.keys.f10 = 0x44
|
||||
keyboard.keys.f11 = 0x57
|
||||
keyboard.keys.f12 = 0x58
|
||||
keyboard.keys.f13 = 0x64
|
||||
keyboard.keys.f14 = 0x65
|
||||
keyboard.keys.f15 = 0x66
|
||||
keyboard.keys.f16 = 0x67
|
||||
keyboard.keys.f17 = 0x68
|
||||
keyboard.keys.f18 = 0x69
|
||||
keyboard.keys.f19 = 0x71
|
||||
|
||||
-- Japanese keyboards
|
||||
keyboard.keys.kana = 0x70
|
||||
keyboard.keys.kanji = 0x94
|
||||
keyboard.keys.convert = 0x79
|
||||
keyboard.keys.noconvert = 0x7B
|
||||
keyboard.keys.yen = 0x7D
|
||||
keyboard.keys.circumflex = 0x90
|
||||
keyboard.keys.ax = 0x96
|
||||
|
||||
-- Numpad
|
||||
keyboard.keys.numpad0 = 0x52
|
||||
keyboard.keys.numpad1 = 0x4F
|
||||
keyboard.keys.numpad2 = 0x50
|
||||
keyboard.keys.numpad3 = 0x51
|
||||
keyboard.keys.numpad4 = 0x4B
|
||||
keyboard.keys.numpad5 = 0x4C
|
||||
keyboard.keys.numpad6 = 0x4D
|
||||
keyboard.keys.numpad7 = 0x47
|
||||
keyboard.keys.numpad8 = 0x48
|
||||
keyboard.keys.numpad9 = 0x49
|
||||
keyboard.keys.numpadmul = 0x37
|
||||
keyboard.keys.numpaddiv = 0xB5
|
||||
keyboard.keys.numpadsub = 0x4A
|
||||
keyboard.keys.numpadadd = 0x4E
|
||||
keyboard.keys.numpaddecimal = 0x53
|
||||
keyboard.keys.numpadcomma = 0xB3
|
||||
keyboard.keys.numpadenter = 0x9C
|
||||
keyboard.keys.numpadequals = 0x8D
|
||||
|
||||
-- Create inverse mapping for name lookup.
|
||||
setmetatable(keyboard.keys,
|
||||
{
|
||||
__index = function(tbl, k)
|
||||
if type(k) ~= "number" then return end
|
||||
for name,value in pairs(tbl) do
|
||||
if value == k then
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
@@ -1,453 +0,0 @@
|
||||
local serialize = import("serialize")
|
||||
local unicode = import("unicode")
|
||||
local event = import("event")
|
||||
--local keyboard = import("keyboard")
|
||||
|
||||
--local ocelot = component.proxy(component.list("ocelot")())
|
||||
local component = import("component")
|
||||
local computer = import("computer")
|
||||
local gpu = component.gpu
|
||||
_G.termlib = {}
|
||||
termlib.cursorPosX = 1
|
||||
termlib.cursorPosY = 1
|
||||
termlib.readHistory = {}
|
||||
|
||||
local width, height = gpu.getResolution()
|
||||
|
||||
local ANSIColorPalette = {
|
||||
["dark"] = {
|
||||
[0] = 0x000000,
|
||||
[1] = 0x800000,
|
||||
[2] = 0x008000,
|
||||
[3] = 0x808000,
|
||||
[4] = 0x000080,
|
||||
[5] = 0x800080,
|
||||
[6] = 0x008080,
|
||||
[7] = 0xC0C0C0
|
||||
},
|
||||
["bright"] = {
|
||||
[0] = 0x808080,
|
||||
[1] = 0xFF0000,
|
||||
[2] = 0x00FF00,
|
||||
[3] = 0xFFFF00,
|
||||
[4] = 0x0000FF,
|
||||
[5] = 0xFF00FF,
|
||||
[6] = 0x00FFFF,
|
||||
[7] = 0xFFFFFF
|
||||
}
|
||||
}
|
||||
|
||||
defaultForegroundColor = ANSIColorPalette["bright"][7]
|
||||
defaultBackgroundColor = ANSIColorPalette["dark"][0]
|
||||
|
||||
gpu.setForeground(defaultForegroundColor)
|
||||
gpu.setBackground(defaultBackgroundColor)
|
||||
|
||||
local function scrollDown()
|
||||
local width, height = gpu.getResolution()
|
||||
if gpu.copy(1,1,width,height,0,-1) then
|
||||
local prevForeground = gpu.getForeground()
|
||||
local prevBackground = gpu.getBackground()
|
||||
gpu.setForeground(defaultForegroundColor)
|
||||
gpu.setBackground(defaultBackgroundColor)
|
||||
gpu.fill(1, height, width, 1, " ")
|
||||
gpu.setForeground(prevForeground)
|
||||
gpu.setBackground(prevBackground)
|
||||
termlib.cursorPosY=height
|
||||
end
|
||||
end
|
||||
|
||||
local function newLine()
|
||||
termlib.cursorPosX=1
|
||||
termlib.cursorPosY = termlib.cursorPosY + 1
|
||||
if termlib.cursorPosY>height then
|
||||
scrollDown()
|
||||
end
|
||||
end
|
||||
|
||||
local function parseCodeNumbers(code)
|
||||
local o = {}
|
||||
for num in code:sub(3,-2):gmatch("[^;]+") do
|
||||
table.insert(o,tonumber(num))
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
local function from8BitColor(num)
|
||||
num=math.floor(num)&255
|
||||
if num<16 then return 0x444444*((num>>3)&1)+(0xBB0000*((num>>2)&1)|0x00BB00*((num>>1)&1)|0x0000BB*(num&1)) end
|
||||
if num>=232 then return 0x10101*(8+(num-232)*10) end
|
||||
num=num-16
|
||||
local palette = {0,95,135,175,215,255}
|
||||
return (palette[(num//36)%6+1]<<16)|(palette[(num//6)%6+1]<<8)|palette[num%6+1]
|
||||
end
|
||||
|
||||
local function from24BitColor(r,g,b)
|
||||
r,g,b=math.floor(r)&255,math.floor(g)&255,math.floor(b)&255
|
||||
return (r<<16)|(g<<8)|b
|
||||
end
|
||||
|
||||
local function findCodeEnd(text,i)
|
||||
local function inRange(v,min,max)
|
||||
return v>=min and v<=max
|
||||
end
|
||||
i=i+2
|
||||
while i<=#text and not inRange(text:byte(i),0x40,0x7F) do i=i+1 end
|
||||
return i
|
||||
end
|
||||
|
||||
function termlib.write(text, textWrap)
|
||||
local width, height = gpu.getResolution()
|
||||
|
||||
-- you don't know how tiring this was just for ANSI escape code support
|
||||
|
||||
if textWrap == nil then
|
||||
textWrap = true
|
||||
end
|
||||
|
||||
if not text or not tostring(text) then
|
||||
return
|
||||
end
|
||||
if text:find("\a") then
|
||||
computer.beep()
|
||||
end
|
||||
text = tostring(text)
|
||||
text = "\27[0m" .. text:gsub("\t", " ")
|
||||
local readBreak = 0
|
||||
-- readBreak is for when, inside the for loop, there normally would have been an increase in the "i" variable because it has read more than one character.
|
||||
-- unfortunately, changing the "i" variable would have unpredictable effects, so to not risk anything, this workaround was done.
|
||||
local section = ""
|
||||
|
||||
local function printSection()
|
||||
if #section==0 then
|
||||
return
|
||||
end
|
||||
while true do
|
||||
gpu.set(termlib.cursorPosX,termlib.cursorPosY,section)
|
||||
if unicode.wlen(section) > width - termlib.cursorPosX + 1 and textWrap then
|
||||
section = section:sub(width - termlib.cursorPosX + 2)
|
||||
newLine()
|
||||
else
|
||||
termlib.cursorPosX = termlib.cursorPosX+unicode.wlen(section)
|
||||
break
|
||||
end
|
||||
end
|
||||
section = ""
|
||||
end
|
||||
|
||||
for i=1,#text do
|
||||
if readBreak>0 then
|
||||
readBreak = readBreak - 1
|
||||
goto continue
|
||||
end
|
||||
|
||||
if string.byte(text,i)==10 then
|
||||
printSection()
|
||||
newLine()
|
||||
elseif string.byte(text,i)==13 then
|
||||
printSection()
|
||||
termlib.cursorPosX=1
|
||||
elseif string.byte(text,i)==0x1b and i<=#text-2 then
|
||||
printSection()
|
||||
--ocelot.log("0x1b char detected")
|
||||
local codeType = string.sub(text,i+1,i+1)
|
||||
if codeType=="[" then
|
||||
-- Control Sequence Introducer
|
||||
--ocelot.log("Control Sequence Introducer")
|
||||
local codeEndIdx = findCodeEnd(text,i)
|
||||
-- codeEndIdx = string.find(text,"m",i)
|
||||
local code = string.sub(text,i,codeEndIdx)
|
||||
--ocelot.log("Code: "..code.." ("..i..", "..codeEndIdx..")")
|
||||
readBreak = readBreak + #code - 1
|
||||
local nums = parseCodeNumbers(code)
|
||||
local codeEnd = code:sub(-1)
|
||||
--ocelot.log("Code end: "..codeEnd..", "..#codeEnd)
|
||||
if codeEnd == "m" then
|
||||
-- Select Graphic Rendition
|
||||
--ocelot.log("Select Graphic Rendition, ID "..nums[1])
|
||||
if nums[1]>=30 and nums[1]<=37 then
|
||||
gpu.setForeground(ANSIColorPalette["dark"][nums[1]%10])
|
||||
end
|
||||
if nums[1]==38 and nums[2]==5 then
|
||||
gpu.setForeground(from8BitColor(nums[3]))
|
||||
end
|
||||
if nums[1]==38 and nums[2]==2 then
|
||||
gpu.setForeground(from24BitColor(nums[3],nums[4],nums[5]))
|
||||
end
|
||||
if nums[1]==39 or nums[1]==0 then
|
||||
gpu.setForeground(defaultForegroundColor)
|
||||
end
|
||||
if nums[1]>=40 and nums[1]<=47 then
|
||||
gpu.setBackground(ANSIColorPalette["dark"][nums[1]%10])
|
||||
end
|
||||
if nums[1]==48 and nums[2]==5 then
|
||||
gpu.setBackground(from8BitColor(nums[3]))
|
||||
end
|
||||
if nums[1]==48 and nums[2]==2 then
|
||||
gpu.setBackground(from24BitColor(nums[3],nums[4],nums[5]))
|
||||
end
|
||||
if nums[1]==49 or nums[1]==0 then
|
||||
gpu.setBackground(defaultBackgroundColor)
|
||||
end
|
||||
if nums[1]>=90 and nums[1]<=97 then
|
||||
gpu.setForeground(ANSIColorPalette["bright"][nums[1]%10])
|
||||
end
|
||||
if nums[1]>=100 and nums[1]<=107 then
|
||||
gpu.setBackground(ANSIColorPalette["bright"][nums[1]%10])
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
--gpu.set(termlib.cursorPosX,termlib.cursorPosY,string.sub(text,i,i))
|
||||
section = section..string.sub(text,i,i)
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
printSection()
|
||||
end
|
||||
|
||||
function _G.print(...)
|
||||
local args = {...}
|
||||
local stringArgs = {}
|
||||
for _, arg in pairs(args) do
|
||||
if type(arg)=="table" then
|
||||
table.insert(stringArgs, serialize.table(arg,true))
|
||||
elseif tostring(arg) then
|
||||
table.insert(stringArgs, tostring(arg))
|
||||
end
|
||||
end
|
||||
termlib.write(table.concat(stringArgs, " ") .. "\n")
|
||||
end
|
||||
|
||||
function _G.clear()
|
||||
width, height = gpu.getResolution()
|
||||
gpu.setForeground(defaultForegroundColor)
|
||||
gpu.setBackground(defaultBackgroundColor)
|
||||
gpu.fill(1,1,width,height," ")
|
||||
termlib.cursorPosX, termlib.cursorPosY = 1, 1
|
||||
end
|
||||
|
||||
function _G.read(readHistoryType, prefix, defaultText, maxChars)
|
||||
checkArg(1, readHistoryType, "string", "nil")
|
||||
checkArg(2, prefix, "string", "nil")
|
||||
checkArg(3, defaultText, "string", "nil")
|
||||
checkArg(4, maxChars, "number", "nil")
|
||||
maxChars = maxChars or math.huge
|
||||
|
||||
local text = defaultText or ""
|
||||
|
||||
local historyIdx
|
||||
if readHistoryType then
|
||||
if not termlib.readHistory[readHistoryType] then
|
||||
termlib.readHistory[readHistoryType] = {text}
|
||||
elseif termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType] ] ~= "" then
|
||||
table.insert(termlib.readHistory[readHistoryType], text)
|
||||
end
|
||||
historyIdx = #termlib.readHistory[readHistoryType]
|
||||
end
|
||||
|
||||
local function updateHistory()
|
||||
if not readHistoryType then return end
|
||||
termlib.readHistory[readHistoryType][historyIdx]=text
|
||||
end
|
||||
|
||||
local cur = unicode.len(text)+1
|
||||
if prefix then termlib.write(prefix) end
|
||||
local startX, startY = termlib.cursorPosX, termlib.cursorPosY
|
||||
local fg, bg = gpu.getForeground(), gpu.getBackground()
|
||||
local cursorBlink = true
|
||||
local function get(idx)
|
||||
idx=startX+idx-1
|
||||
return gpu.get(idx%width,startY+(idx//width))
|
||||
end
|
||||
local function checkScroll(y)
|
||||
for i=1,y-height do
|
||||
scrollDown()
|
||||
startY=startY-1
|
||||
end
|
||||
return math.min(y,height)
|
||||
end
|
||||
local function set(idx,chr,rev)
|
||||
if chr==nil or chr=="" then return end
|
||||
if rev then
|
||||
gpu.setForeground(bg)
|
||||
gpu.setBackground(fg)
|
||||
else
|
||||
gpu.setForeground(fg)
|
||||
gpu.setBackground(bg)
|
||||
end
|
||||
idx=startX+idx-1
|
||||
local setX, setY = (idx-1)%width+1, startY+((idx-1)//width+1)-1
|
||||
setY = checkScroll(setY)
|
||||
gpu.set(setX,setY,unicode.sub(chr,1,width-setX+1))
|
||||
for i=1,math.ceil((#chr+setX-1)/width)+1 do
|
||||
gpu.set(1,setY+i,unicode.sub(chr,2-setX+i*width,width+i*width-setX))
|
||||
setY = checkScroll(setY)
|
||||
end
|
||||
end
|
||||
local function strDef(a,b)
|
||||
if #a==0 then return b end
|
||||
return a
|
||||
end
|
||||
local function curPos(cur)
|
||||
return unicode.wlen(unicode.sub(text,1,cur-1))+1
|
||||
end
|
||||
local function add(chr)
|
||||
if type(chr)~="string" or #chr==0 then return end
|
||||
if unicode.len(text)>=maxChars then return end
|
||||
if maxChars<math.huge then
|
||||
chr=unicode.sub(chr,1,maxChars-unicode.len(text))
|
||||
end
|
||||
text=unicode.sub(text,1,cur-1)..chr..unicode.sub(text,cur)
|
||||
set(curPos(cur),chr,false)
|
||||
cur=math.min(cur+unicode.len(chr),maxChars+1)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
set(curPos(cur+1),unicode.sub(text,cur+1),false)
|
||||
end
|
||||
local function moveCur(dir)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
||||
cur=math.max(math.min(cur+dir,unicode.len(text)+1),1)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
end
|
||||
local function isLetter(chr)
|
||||
return not string.find("\x09 :@-./_~?&=%+#",chr,1,true)
|
||||
end
|
||||
local function nextCur(dir,chr,icur)
|
||||
if icur==nil then icur=cur end
|
||||
local next = math.max(math.min(icur+dir,unicode.len(text)+1),1)
|
||||
if chr then return unicode.sub(text,next,next) end
|
||||
return next
|
||||
end
|
||||
local function curAfterWord(dir)
|
||||
local ncur = cur
|
||||
while nextCur(dir,false,ncur)~=ncur and isLetter(nextCur(dir,true,ncur))==(dir==1) do
|
||||
ncur=nextCur(dir,false,ncur)
|
||||
end
|
||||
while nextCur(dir,false,ncur)~=ncur and isLetter(nextCur(dir,true,ncur))==(dir==-1) do
|
||||
ncur=nextCur(dir,false,ncur)
|
||||
end
|
||||
return ncur
|
||||
end
|
||||
local function moveWord(dir)
|
||||
if nextCur(dir)==cur then return end
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
||||
cur=curAfterWord(dir)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
end
|
||||
local function deleteWord(dir)
|
||||
local after = curAfterWord(dir)
|
||||
local lenb = unicode.wlen(text)
|
||||
if dir==1 then
|
||||
text=unicode.sub(text,1,cur-1)..unicode.sub(text,after)
|
||||
set(curPos(cur+1),unicode.sub(text,cur+1)..string.rep(" ",lenb-unicode.wlen(text)+1),false)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
else
|
||||
text = unicode.sub(text,1,after-1)..unicode.sub(text,cur)
|
||||
cur=after
|
||||
set(curPos(cur+1),unicode.sub(text,cur+1)..string.rep(" ",lenb-unicode.wlen(text)+1),false)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
end
|
||||
updateHistory()
|
||||
cursorBlink = true
|
||||
end
|
||||
local function isLine(chr)
|
||||
return chr=="\n" or chr=="\r"
|
||||
end
|
||||
--[[ gpu.set(startX,startY,unicode.sub(text,1,width-startX))
|
||||
for i=1,(#text+startX)//width-1 do
|
||||
gpu.set(startX,startY+i,unicode.sub(text,1+i*width,width-startX+i*width))
|
||||
end ]]
|
||||
set(1,text,false)
|
||||
set(curPos(cur)," ",true)
|
||||
|
||||
local function reprint(new)
|
||||
set(1,new..string.rep(" ",unicode.wlen(text)-unicode.wlen(new)+1),false)
|
||||
cur=unicode.len(new)+1
|
||||
text=new
|
||||
set(curPos(cur)," ",true)
|
||||
end
|
||||
|
||||
while true do
|
||||
local args = {event.pull("key_down", "clipboard", 0.5)}
|
||||
if args and args[1] == "key_down" and args[4] then
|
||||
local key = keyboard.keys[args[4]]
|
||||
if key=="up" and readHistoryType then
|
||||
historyIdx=math.max(historyIdx-1,1)
|
||||
reprint(termlib.readHistory[readHistoryType][historyIdx])
|
||||
elseif key=="down" and readHistoryType then
|
||||
historyIdx=math.min(historyIdx+1,#termlib.readHistory[readHistoryType])
|
||||
reprint(termlib.readHistory[readHistoryType][historyIdx])
|
||||
elseif key=="left" and keyboard.ctrlDown then
|
||||
moveWord(-1)
|
||||
elseif key=="right" and keyboard.ctrlDown then
|
||||
moveWord(1)
|
||||
elseif key=="left" then
|
||||
moveCur(-1)
|
||||
elseif key=="right" then
|
||||
moveCur(1)
|
||||
elseif key=="home" then
|
||||
moveCur(-math.huge)
|
||||
elseif key=="end" then
|
||||
moveCur(math.huge)
|
||||
elseif key=="back" and keyboard.ctrlDown then
|
||||
deleteWord(-1)
|
||||
elseif key=="delete" and keyboard.ctrlDown then
|
||||
deleteWord(1)
|
||||
elseif key=="back" and cur>1 then
|
||||
text=unicode.sub(text,1,cur-2)..unicode.sub(text,cur)
|
||||
cur=cur-1
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
set(curPos(cur)+1,unicode.sub(text,cur+1).." ",false)
|
||||
updateHistory()
|
||||
elseif key=="delete" then
|
||||
text = unicode.sub(text,1,cur-1)..unicode.sub(text,cur+1)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
if cur<=unicode.len(text) then
|
||||
set(curPos(cur+1),unicode.sub(text,cur+1).." ",false)
|
||||
end
|
||||
updateHistory()
|
||||
elseif key=="enter" then
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
||||
break
|
||||
elseif not (args[3]<32 or (args[3]>0x7F and args[3]<=0x9F)) then
|
||||
add(unicode.char(args[3]) or " ")
|
||||
updateHistory()
|
||||
end
|
||||
elseif args and args[1]=="clipboard" then
|
||||
local clip = args[3]
|
||||
if not args[3] then goto continue end
|
||||
while isLine(unicode.sub(clip,1,1)) do clip=unicode.sub(clip,2) end
|
||||
while isLine(unicode.sub(clip,-1)) do clip=unicode.sub(clip,1,-2) end
|
||||
add(clip)
|
||||
updateHistory()
|
||||
else
|
||||
cursorBlink=not cursorBlink
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),cursorBlink)
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
|
||||
if readHistoryType then
|
||||
if termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType]]=="" then
|
||||
table.remove(termlib.readHistory[readHistoryType],#termlib.readHistory[readHistoryType])
|
||||
end
|
||||
if historyIdx<#termlib.readHistory[readHistoryType] then
|
||||
table.remove(termlib.readHistory[readHistoryType],historyIdx)
|
||||
table.insert(termlib.readHistory[readHistoryType],text)
|
||||
end
|
||||
while #termlib.readHistory[readHistoryType] > 50 do
|
||||
table.remove(termlib.readHistory[readHistoryType], 1)
|
||||
end
|
||||
end
|
||||
|
||||
termlib.cursorPosX=1
|
||||
termlib.cursorPosY=termlib.cursorPosY+math.ceil((unicode.wlen(text)+startX-1)/width)
|
||||
if termlib.cursorPosY>height then scrollDown() end
|
||||
|
||||
return text
|
||||
end
|
||||
@@ -0,0 +1,73 @@
|
||||
local loadfile = ...
|
||||
local filesystem = assert(loadfile("/lib/filesystem.lua")(loadfile))
|
||||
_G._OSVERSION = "HALYDE VERSION" -- TODO: Put this in a separate config file
|
||||
_G._OSLOGO = ""
|
||||
_G._PUBLIC = {}
|
||||
local ocelot = component.proxy(component.list("ocelot")())
|
||||
|
||||
local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge)
|
||||
_OSLOGO = _OSLOGO .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
|
||||
_G.package = {["preloaded"] = {}}
|
||||
|
||||
loadfile("/halyde/kernel/modules/datatools.lua")()
|
||||
|
||||
function _G.require(module, ...)
|
||||
ocelot.log("Requiring " .. module)
|
||||
local args = table.pack(...)
|
||||
if package.preloaded[module] then
|
||||
return package.preloaded[module]
|
||||
end
|
||||
local modulepath
|
||||
if filesystem.exists(module) then
|
||||
modulepath = module
|
||||
elseif filesystem.exists("/lib/" .. module .. ".lua") then
|
||||
modulepath = "/lib/" .. module .. ".lua"
|
||||
elseif shell and shell.workingDirectory and filesystem.exists(filesystem.concat(shell.workingDirectory, module .. ".lua")) then
|
||||
modulepath = shell.workingDirectory .. module .. ".lua"
|
||||
end
|
||||
assert(modulepath, "Module not found\nPossible locations:\n/lib/" .. module .. ".lua")
|
||||
local handle, data, tmpdata = filesystem.open(modulepath), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
handle:close()
|
||||
return(assert(load(data, "="..modulepath))(table.unpack(args)))
|
||||
end
|
||||
|
||||
function _G.package.preload(module)
|
||||
local handle, data, tmpdata = assert(filesystem.open("/lib/" .. module .. ".lua", "r")), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
handle:close()
|
||||
package.preloaded[module] = assert(load(data, "="..module))()
|
||||
_G[module] = nil
|
||||
end
|
||||
|
||||
require("/halyde/kernel/datatools.lua") -- If this is not imported BEFORE modload gets run, modload requires filesystem which requires computer which requires datatools.
|
||||
require("/halyde/kernel/modload.lua")
|
||||
|
||||
package.preload("component")
|
||||
package.preload("computer")
|
||||
|
||||
local component = require("component")
|
||||
local gpu = component.gpu
|
||||
local screenAddress = component.list("screen")()
|
||||
|
||||
gpu.bind(screenAddress)
|
||||
gpu.setResolution(gpu.maxResolution())
|
||||
|
||||
if not filesystem.exists("/halyde/config/shell.json") then -- Auto-generate configs
|
||||
filesystem.copy("/halyde/config/generate/shell.json", "/halyde/config/shell.json")
|
||||
end
|
||||
if not filesystem.exists("/halyde/config/startupapps.json") then
|
||||
filesystem.copy("/halyde/config/generate/startupapps.json", "/halyde/config/startupapps.json")
|
||||
end
|
||||
|
||||
require("/halyde/kernel/tsched.lua")
|
||||
@@ -2,11 +2,11 @@ _G.evmgr = {}
|
||||
_G.evmgr.eventQueue = {}
|
||||
local maxEventQueueLength = 10 -- increase if events start getting dropped
|
||||
|
||||
local computer = import("computer")
|
||||
local computer = require("computer")
|
||||
|
||||
keyboard.ctrlDown = false
|
||||
keyboard.altDown = false
|
||||
keyboard.shiftDown = false
|
||||
_G._PUBLIC.keyboard.ctrlDown = false
|
||||
_G._PUBLIC.keyboard.altDown = false
|
||||
_G._PUBLIC.keyboard.shiftDown = false
|
||||
|
||||
--local ocelot = component.proxy(component.list("ocelot")())
|
||||
|
||||
@@ -16,31 +16,31 @@ while true do
|
||||
args = {computer.uptime(), computer.pullSignal(0)}
|
||||
if args and args[2] then
|
||||
table.insert(evmgr.eventQueue, args)
|
||||
if keyboard then
|
||||
if _PUBLIC.keyboard then
|
||||
if args[2] == "key_down" then
|
||||
local keycode = args[5]
|
||||
local key = keyboard.keys[keycode]
|
||||
local key = _PUBLIC.keyboard.keys[keycode]
|
||||
if key == "lcontrol" then
|
||||
keyboard.ctrlDown = true
|
||||
_PUBLIC.keyboard.ctrlDown = true
|
||||
elseif key == "lmenu" then
|
||||
keyboard.altDown = true
|
||||
_PUBLIC.keyboard.altDown = true
|
||||
elseif key == "lshift" then
|
||||
keyboard.shiftDown = true
|
||||
elseif key == "c" and keyboard.ctrlDown and keyboard.altDown then
|
||||
_PUBLIC.keyboard.shiftDown = true
|
||||
elseif key == "c" and _PUBLIC.keyboard.ctrlDown and _PUBLIC.keyboard.altDown then
|
||||
if print then
|
||||
print("\n\27[91mCoroutine "..tostring(#cormgr.corList).." killed.")
|
||||
print("\n\27[91mCoroutine "..tostring(#tsched.tasks).." killed.")
|
||||
end
|
||||
cormgr.corList[#cormgr.corList] = nil
|
||||
tsched.tasks[#tsched.tasks] = nil
|
||||
end
|
||||
elseif args[2] == "key_up" then
|
||||
local keycode = args[5]
|
||||
local key = keyboard.keys[keycode]
|
||||
local key = _PUBLIC.keyboard.keys[keycode]
|
||||
if key == "lcontrol" then
|
||||
keyboard.ctrlDown = false
|
||||
_PUBLIC.keyboard.ctrlDown = false
|
||||
elseif key == "lmenu" then
|
||||
keyboard.altDown = false
|
||||
_PUBLIC.keyboard.altDown = false
|
||||
elseif key == "lshift" then
|
||||
keyboard.shiftDown = true
|
||||
_PUBLIC.keyboard.shiftDown = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
local fs = require("filesystem")
|
||||
local ocelot = require("component").ocelot
|
||||
|
||||
local modulePath = "/halyde/kernel/modules"
|
||||
|
||||
local modules = fs.list(modulePath)
|
||||
local moduleTypes = {}
|
||||
|
||||
local function loadModule(modName)
|
||||
ocelot.log("Checking module " .. modName)
|
||||
local moduleData = require(fs.concat(modulePath, modName))
|
||||
table.remove(modules, table.find(modules, modName))
|
||||
if not moduleData.check() then
|
||||
return
|
||||
end
|
||||
ocelot.log("Loading module " .. modName)
|
||||
if moduleData.dependencies then
|
||||
for _, dependency in pairs(moduleData.dependencies) do
|
||||
if table.find(modules, dependency) then
|
||||
loadModule(dependency)
|
||||
elseif table.find(modules, dependency .. ".lua") then
|
||||
loadModule(dependency .. ".lua")
|
||||
else
|
||||
for typeLookupDrvName, typeLookupDrvType in pairs(moduleTypes) do
|
||||
if typeLookupDrvType == dependency then
|
||||
loadModule(typeLookupDrvName)
|
||||
-- Don't break, because there can be multiple modules of the correct type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
--print(modName)
|
||||
if moduleData.init then -- I have no idea why this would not exist, but it's a failsafe
|
||||
moduleData.init()
|
||||
end
|
||||
end
|
||||
|
||||
for _, modName in pairs(modules) do -- Get all the module types
|
||||
local moduleData = require(fs.concat(modulePath, modName))
|
||||
if moduleData.type then
|
||||
--print(moduleData.type)
|
||||
moduleTypes[modName] = moduleData.type -- Not the other way around because there can be multiple modules of the same type, but there can't be multiple entries with the same key
|
||||
end
|
||||
end
|
||||
|
||||
while modules[1] do
|
||||
if modules[1]:sub(-1, -1) ~= "/" then -- Check if it's not a directory. If it is, it might be module config
|
||||
loadModule(modules[1])
|
||||
end
|
||||
end
|
||||
|
||||
ocelot.log("Finished loading modules!")
|
||||
@@ -0,0 +1,53 @@
|
||||
local module = {}
|
||||
|
||||
module.dependencies = {"terminal"}
|
||||
|
||||
function module.check()
|
||||
return true -- This module should always be loaded
|
||||
end
|
||||
|
||||
function module.init()
|
||||
local publicTable = {
|
||||
"print",
|
||||
"require",
|
||||
"_VERSION",
|
||||
"_OSVERSION",
|
||||
"assert",
|
||||
"error",
|
||||
"getmetatable",
|
||||
"ipairs",
|
||||
"load",
|
||||
"next",
|
||||
"pairs",
|
||||
"pcall",
|
||||
"rawequal",
|
||||
"rawget",
|
||||
"rawlen",
|
||||
"rawset",
|
||||
"select",
|
||||
"setmetatable",
|
||||
"tonumber",
|
||||
"tostring",
|
||||
"type",
|
||||
"xpcall",
|
||||
"bit32",
|
||||
"coroutine",
|
||||
"debug",
|
||||
"math",
|
||||
"os",
|
||||
"string",
|
||||
"table",
|
||||
"checkArg",
|
||||
"utf8",
|
||||
"convert"
|
||||
}
|
||||
for _, value in ipairs(publicTable) do
|
||||
_G._PUBLIC[value] = table.copy(_G[value])
|
||||
end
|
||||
end
|
||||
|
||||
function module.exit()
|
||||
_G._PUBLIC = nil
|
||||
end
|
||||
|
||||
return module
|
||||
@@ -0,0 +1,15 @@
|
||||
local module = {}
|
||||
|
||||
function module.check()
|
||||
return true -- This module should always be loaded
|
||||
end
|
||||
|
||||
function module.init()
|
||||
|
||||
end
|
||||
|
||||
function module.exit()
|
||||
|
||||
end
|
||||
|
||||
return module
|
||||
@@ -0,0 +1,157 @@
|
||||
local module = {}
|
||||
|
||||
function module.check()
|
||||
return true -- This module should always be loaded
|
||||
end
|
||||
|
||||
function module.init()
|
||||
_G._PUBLIC.keyboard = {["keys"] = {}}
|
||||
|
||||
_PUBLIC.keyboard.keys["1"] = 0x02
|
||||
_PUBLIC.keyboard.keys["2"] = 0x03
|
||||
_PUBLIC.keyboard.keys["3"] = 0x04
|
||||
_PUBLIC.keyboard.keys["4"] = 0x05
|
||||
_PUBLIC.keyboard.keys["5"] = 0x06
|
||||
_PUBLIC.keyboard.keys["6"] = 0x07
|
||||
_PUBLIC.keyboard.keys["7"] = 0x08
|
||||
_PUBLIC.keyboard.keys["8"] = 0x09
|
||||
_PUBLIC.keyboard.keys["9"] = 0x0A
|
||||
_PUBLIC.keyboard.keys["0"] = 0x0B
|
||||
_PUBLIC.keyboard.keys.a = 0x1E
|
||||
_PUBLIC.keyboard.keys.b = 0x30
|
||||
_PUBLIC.keyboard.keys.c = 0x2E
|
||||
_PUBLIC.keyboard.keys.d = 0x20
|
||||
_PUBLIC.keyboard.keys.e = 0x12
|
||||
_PUBLIC.keyboard.keys.f = 0x21
|
||||
_PUBLIC.keyboard.keys.g = 0x22
|
||||
_PUBLIC.keyboard.keys.h = 0x23
|
||||
_PUBLIC.keyboard.keys.i = 0x17
|
||||
_PUBLIC.keyboard.keys.j = 0x24
|
||||
_PUBLIC.keyboard.keys.k = 0x25
|
||||
_PUBLIC.keyboard.keys.l = 0x26
|
||||
_PUBLIC.keyboard.keys.m = 0x32
|
||||
_PUBLIC.keyboard.keys.n = 0x31
|
||||
_PUBLIC.keyboard.keys.o = 0x18
|
||||
_PUBLIC.keyboard.keys.p = 0x19
|
||||
_PUBLIC.keyboard.keys.q = 0x10
|
||||
_PUBLIC.keyboard.keys.r = 0x13
|
||||
_PUBLIC.keyboard.keys.s = 0x1F
|
||||
_PUBLIC.keyboard.keys.t = 0x14
|
||||
_PUBLIC.keyboard.keys.u = 0x16
|
||||
_PUBLIC.keyboard.keys.v = 0x2F
|
||||
_PUBLIC.keyboard.keys.w = 0x11
|
||||
_PUBLIC.keyboard.keys.x = 0x2D
|
||||
_PUBLIC.keyboard.keys.y = 0x15
|
||||
_PUBLIC.keyboard.keys.z = 0x2C
|
||||
|
||||
_PUBLIC.keyboard.keys.apostrophe = 0x28
|
||||
_PUBLIC.keyboard.keys.at = 0x91
|
||||
_PUBLIC.keyboard.keys.back = 0x0E -- backspace
|
||||
_PUBLIC.keyboard.keys.backslash = 0x2B
|
||||
_PUBLIC.keyboard.keys.capital = 0x3A -- capslock
|
||||
_PUBLIC.keyboard.keys.colon = 0x92
|
||||
_PUBLIC.keyboard.keys.comma = 0x33
|
||||
_PUBLIC.keyboard.keys.enter = 0x1C
|
||||
_PUBLIC.keyboard.keys.equals = 0x0D
|
||||
_PUBLIC.keyboard.keys.grave = 0x29 -- accent grave
|
||||
_PUBLIC.keyboard.keys.lbracket = 0x1A
|
||||
_PUBLIC.keyboard.keys.lcontrol = 0x1D
|
||||
_PUBLIC.keyboard.keys.lmenu = 0x38 -- left Alt
|
||||
_PUBLIC.keyboard.keys.lshift = 0x2A
|
||||
_PUBLIC.keyboard.keys.minus = 0x0C
|
||||
_PUBLIC.keyboard.keys.numlock = 0x45
|
||||
_PUBLIC.keyboard.keys.pause = 0xC5
|
||||
_PUBLIC.keyboard.keys.period = 0x34
|
||||
_PUBLIC.keyboard.keys.rbracket = 0x1B
|
||||
_PUBLIC.keyboard.keys.rcontrol = 0x9D
|
||||
_PUBLIC.keyboard.keys.rmenu = 0xB8 -- right Alt
|
||||
_PUBLIC.keyboard.keys.rshift = 0x36
|
||||
_PUBLIC.keyboard.keys.scroll = 0x46 -- Scroll Lock
|
||||
_PUBLIC.keyboard.keys.semicolon = 0x27
|
||||
_PUBLIC.keyboard.keys.slash = 0x35 -- / on main _PUBLIC.keyboard
|
||||
_PUBLIC.keyboard.keys.space = 0x39
|
||||
_PUBLIC.keyboard.keys.stop = 0x95
|
||||
_PUBLIC.keyboard.keys.tab = 0x0F
|
||||
_PUBLIC.keyboard.keys.underline = 0x93
|
||||
|
||||
-- Keypad (and numpad with numlock off)
|
||||
_PUBLIC.keyboard.keys.up = 0xC8
|
||||
_PUBLIC.keyboard.keys.down = 0xD0
|
||||
_PUBLIC.keyboard.keys.left = 0xCB
|
||||
_PUBLIC.keyboard.keys.right = 0xCD
|
||||
_PUBLIC.keyboard.keys.home = 0xC7
|
||||
_PUBLIC.keyboard.keys["end"] = 0xCF
|
||||
_PUBLIC.keyboard.keys.pageUp = 0xC9
|
||||
_PUBLIC.keyboard.keys.pageDown = 0xD1
|
||||
_PUBLIC.keyboard.keys.insert = 0xD2
|
||||
_PUBLIC.keyboard.keys.delete = 0xD3
|
||||
|
||||
-- Function keys
|
||||
_PUBLIC.keyboard.keys.f1 = 0x3B
|
||||
_PUBLIC.keyboard.keys.f2 = 0x3C
|
||||
_PUBLIC.keyboard.keys.f3 = 0x3D
|
||||
_PUBLIC.keyboard.keys.f4 = 0x3E
|
||||
_PUBLIC.keyboard.keys.f5 = 0x3F
|
||||
_PUBLIC.keyboard.keys.f6 = 0x40
|
||||
_PUBLIC.keyboard.keys.f7 = 0x41
|
||||
_PUBLIC.keyboard.keys.f8 = 0x42
|
||||
_PUBLIC.keyboard.keys.f9 = 0x43
|
||||
_PUBLIC.keyboard.keys.f10 = 0x44
|
||||
_PUBLIC.keyboard.keys.f11 = 0x57
|
||||
_PUBLIC.keyboard.keys.f12 = 0x58
|
||||
_PUBLIC.keyboard.keys.f13 = 0x64
|
||||
_PUBLIC.keyboard.keys.f14 = 0x65
|
||||
_PUBLIC.keyboard.keys.f15 = 0x66
|
||||
_PUBLIC.keyboard.keys.f16 = 0x67
|
||||
_PUBLIC.keyboard.keys.f17 = 0x68
|
||||
_PUBLIC.keyboard.keys.f18 = 0x69
|
||||
_PUBLIC.keyboard.keys.f19 = 0x71
|
||||
|
||||
-- Japanese keyboards
|
||||
_PUBLIC.keyboard.keys.kana = 0x70
|
||||
_PUBLIC.keyboard.keys.kanji = 0x94
|
||||
_PUBLIC.keyboard.keys.convert = 0x79
|
||||
_PUBLIC.keyboard.keys.noconvert = 0x7B
|
||||
_PUBLIC.keyboard.keys.yen = 0x7D
|
||||
_PUBLIC.keyboard.keys.circumflex = 0x90
|
||||
_PUBLIC.keyboard.keys.ax = 0x96
|
||||
|
||||
-- Numpad
|
||||
_PUBLIC.keyboard.keys.numpad0 = 0x52
|
||||
_PUBLIC.keyboard.keys.numpad1 = 0x4F
|
||||
_PUBLIC.keyboard.keys.numpad2 = 0x50
|
||||
_PUBLIC.keyboard.keys.numpad3 = 0x51
|
||||
_PUBLIC.keyboard.keys.numpad4 = 0x4B
|
||||
_PUBLIC.keyboard.keys.numpad5 = 0x4C
|
||||
_PUBLIC.keyboard.keys.numpad6 = 0x4D
|
||||
_PUBLIC.keyboard.keys.numpad7 = 0x47
|
||||
_PUBLIC.keyboard.keys.numpad8 = 0x48
|
||||
_PUBLIC.keyboard.keys.numpad9 = 0x49
|
||||
_PUBLIC.keyboard.keys.numpadmul = 0x37
|
||||
_PUBLIC.keyboard.keys.numpaddiv = 0xB5
|
||||
_PUBLIC.keyboard.keys.numpadsub = 0x4A
|
||||
_PUBLIC.keyboard.keys.numpadadd = 0x4E
|
||||
_PUBLIC.keyboard.keys.numpaddecimal = 0x53
|
||||
_PUBLIC.keyboard.keys.numpadcomma = 0xB3
|
||||
_PUBLIC.keyboard.keys.numpadenter = 0x9C
|
||||
_PUBLIC.keyboard.keys.numpadequals = 0x8D
|
||||
|
||||
-- Create inverse mapping for name lookup.
|
||||
setmetatable(_PUBLIC.keyboard.keys,
|
||||
{
|
||||
__index = function(tbl, k)
|
||||
if type(k) ~= "number" then return end
|
||||
for name,value in pairs(tbl) do
|
||||
if value == k then
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function module.exit()
|
||||
_G._PUBLIC.keyboard = nil
|
||||
end
|
||||
|
||||
return module
|
||||
@@ -0,0 +1,466 @@
|
||||
local module = {}
|
||||
|
||||
function module.check()
|
||||
return true -- Usually always loaded, but maybe it would be worth it to check if the computer has a GPU or not? I'm not sure.
|
||||
end
|
||||
|
||||
function module.init()
|
||||
local serialize = require("serialize")
|
||||
local unicode = require("unicode")
|
||||
local event = require("event")
|
||||
|
||||
--local ocelot = component.proxy(component.list("ocelot")())
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local gpu = component.gpu
|
||||
_G._PUBLIC.terminal = {}
|
||||
_PUBLIC.terminal.cursorPosX = 1
|
||||
_PUBLIC.terminal.cursorPosY = 1
|
||||
_PUBLIC.terminal.readHistory = {}
|
||||
|
||||
local width, height = gpu.getResolution()
|
||||
|
||||
local ANSIColorPalette = {
|
||||
["dark"] = {
|
||||
[0] = 0x000000,
|
||||
[1] = 0x800000,
|
||||
[2] = 0x008000,
|
||||
[3] = 0x808000,
|
||||
[4] = 0x000080,
|
||||
[5] = 0x800080,
|
||||
[6] = 0x008080,
|
||||
[7] = 0xC0C0C0
|
||||
},
|
||||
["bright"] = {
|
||||
[0] = 0x808080,
|
||||
[1] = 0xFF0000,
|
||||
[2] = 0x00FF00,
|
||||
[3] = 0xFFFF00,
|
||||
[4] = 0x0000FF,
|
||||
[5] = 0xFF00FF,
|
||||
[6] = 0x00FFFF,
|
||||
[7] = 0xFFFFFF
|
||||
}
|
||||
}
|
||||
|
||||
defaultForegroundColor = ANSIColorPalette["bright"][7]
|
||||
defaultBackgroundColor = ANSIColorPalette["dark"][0]
|
||||
|
||||
gpu.setForeground(defaultForegroundColor)
|
||||
gpu.setBackground(defaultBackgroundColor)
|
||||
|
||||
local function scrollDown()
|
||||
local width, height = gpu.getResolution()
|
||||
if gpu.copy(1,1,width,height,0,-1) then
|
||||
local prevForeground = gpu.getForeground()
|
||||
local prevBackground = gpu.getBackground()
|
||||
gpu.setForeground(defaultForegroundColor)
|
||||
gpu.setBackground(defaultBackgroundColor)
|
||||
gpu.fill(1, height, width, 1, " ")
|
||||
gpu.setForeground(prevForeground)
|
||||
gpu.setBackground(prevBackground)
|
||||
_PUBLIC.terminal.cursorPosY=height
|
||||
end
|
||||
end
|
||||
|
||||
local function newLine()
|
||||
_PUBLIC.terminal.cursorPosX=1
|
||||
_PUBLIC.terminal.cursorPosY = _PUBLIC.terminal.cursorPosY + 1
|
||||
if _PUBLIC.terminal.cursorPosY>height then
|
||||
scrollDown()
|
||||
end
|
||||
end
|
||||
|
||||
local function parseCodeNumbers(code)
|
||||
local o = {}
|
||||
for num in code:sub(3,-2):gmatch("[^;]+") do
|
||||
table.insert(o,tonumber(num))
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
local function from8BitColor(num)
|
||||
num=math.floor(num)&255
|
||||
if num<16 then return 0x444444*((num>>3)&1)+(0xBB0000*((num>>2)&1)|0x00BB00*((num>>1)&1)|0x0000BB*(num&1)) end
|
||||
if num>=232 then return 0x10101*(8+(num-232)*10) end
|
||||
num=num-16
|
||||
local palette = {0,95,135,175,215,255}
|
||||
return (palette[(num//36)%6+1]<<16)|(palette[(num//6)%6+1]<<8)|palette[num%6+1]
|
||||
end
|
||||
|
||||
local function from24BitColor(r,g,b)
|
||||
r,g,b=math.floor(r)&255,math.floor(g)&255,math.floor(b)&255
|
||||
return (r<<16)|(g<<8)|b
|
||||
end
|
||||
|
||||
local function findCodeEnd(text,i)
|
||||
local function inRange(v,min,max)
|
||||
return v>=min and v<=max
|
||||
end
|
||||
i=i+2
|
||||
while i<=#text and not inRange(text:byte(i),0x40,0x7F) do i=i+1 end
|
||||
return i
|
||||
end
|
||||
|
||||
function _PUBLIC.terminal.write(text, textWrap)
|
||||
local width, height = gpu.getResolution()
|
||||
|
||||
-- you don't know how tiring this was just for ANSI escape code support
|
||||
|
||||
if textWrap == nil then
|
||||
textWrap = true
|
||||
end
|
||||
|
||||
if not text or not tostring(text) then
|
||||
return
|
||||
end
|
||||
if text:find("\a") then
|
||||
computer.beep()
|
||||
end
|
||||
text = tostring(text)
|
||||
text = "\27[0m" .. text:gsub("\t", " ")
|
||||
local readBreak = 0
|
||||
-- readBreak is for when, inside the for loop, there normally would have been an increase in the "i" variable because it has read more than one character.
|
||||
-- unfortunately, changing the "i" variable would have unpredictable effects, so to not risk anything, this workaround was done.
|
||||
local section = ""
|
||||
|
||||
local function printSection()
|
||||
if #section==0 then
|
||||
return
|
||||
end
|
||||
while true do
|
||||
gpu.set(_PUBLIC.terminal.cursorPosX,_PUBLIC.terminal.cursorPosY,section)
|
||||
if unicode.wlen(section) > width - _PUBLIC.terminal.cursorPosX + 1 and textWrap then
|
||||
section = section:sub(width - _PUBLIC.terminal.cursorPosX + 2)
|
||||
newLine()
|
||||
else
|
||||
_PUBLIC.terminal.cursorPosX = _PUBLIC.terminal.cursorPosX+unicode.wlen(section)
|
||||
break
|
||||
end
|
||||
end
|
||||
section = ""
|
||||
end
|
||||
|
||||
for i=1,#text do
|
||||
if readBreak>0 then
|
||||
readBreak = readBreak - 1
|
||||
goto continue
|
||||
end
|
||||
|
||||
if string.byte(text,i)==10 then
|
||||
printSection()
|
||||
newLine()
|
||||
elseif string.byte(text,i)==13 then
|
||||
printSection()
|
||||
_PUBLIC.terminal.cursorPosX=1
|
||||
elseif string.byte(text,i)==0x1b and i<=#text-2 then
|
||||
printSection()
|
||||
--ocelot.log("0x1b char detected")
|
||||
local codeType = string.sub(text,i+1,i+1)
|
||||
if codeType=="[" then
|
||||
-- Control Sequence Introducer
|
||||
--ocelot.log("Control Sequence Introducer")
|
||||
local codeEndIdx = findCodeEnd(text,i)
|
||||
-- codeEndIdx = string.find(text,"m",i)
|
||||
local code = string.sub(text,i,codeEndIdx)
|
||||
--ocelot.log("Code: "..code.." ("..i..", "..codeEndIdx..")")
|
||||
readBreak = readBreak + #code - 1
|
||||
local nums = parseCodeNumbers(code)
|
||||
local codeEnd = code:sub(-1)
|
||||
--ocelot.log("Code end: "..codeEnd..", "..#codeEnd)
|
||||
if codeEnd == "m" then
|
||||
-- Select Graphic Rendition
|
||||
--ocelot.log("Select Graphic Rendition, ID "..nums[1])
|
||||
if nums[1]>=30 and nums[1]<=37 then
|
||||
gpu.setForeground(ANSIColorPalette["dark"][nums[1]%10])
|
||||
end
|
||||
if nums[1]==38 and nums[2]==5 then
|
||||
gpu.setForeground(from8BitColor(nums[3]))
|
||||
end
|
||||
if nums[1]==38 and nums[2]==2 then
|
||||
gpu.setForeground(from24BitColor(nums[3],nums[4],nums[5]))
|
||||
end
|
||||
if nums[1]==39 or nums[1]==0 then
|
||||
gpu.setForeground(defaultForegroundColor)
|
||||
end
|
||||
if nums[1]>=40 and nums[1]<=47 then
|
||||
gpu.setBackground(ANSIColorPalette["dark"][nums[1]%10])
|
||||
end
|
||||
if nums[1]==48 and nums[2]==5 then
|
||||
gpu.setBackground(from8BitColor(nums[3]))
|
||||
end
|
||||
if nums[1]==48 and nums[2]==2 then
|
||||
gpu.setBackground(from24BitColor(nums[3],nums[4],nums[5]))
|
||||
end
|
||||
if nums[1]==49 or nums[1]==0 then
|
||||
gpu.setBackground(defaultBackgroundColor)
|
||||
end
|
||||
if nums[1]>=90 and nums[1]<=97 then
|
||||
gpu.setForeground(ANSIColorPalette["bright"][nums[1]%10])
|
||||
end
|
||||
if nums[1]>=100 and nums[1]<=107 then
|
||||
gpu.setBackground(ANSIColorPalette["bright"][nums[1]%10])
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
--gpu.set(_PUBLIC.terminal.cursorPosX,_PUBLIC.terminal.cursorPosY,string.sub(text,i,i))
|
||||
section = section..string.sub(text,i,i)
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
printSection()
|
||||
end
|
||||
|
||||
function _G.print(...)
|
||||
local args = {...}
|
||||
local stringArgs = {}
|
||||
for _, arg in pairs(args) do
|
||||
if type(arg)=="table" then
|
||||
table.insert(stringArgs, serialize.table(arg,true))
|
||||
elseif tostring(arg) then
|
||||
table.insert(stringArgs, tostring(arg))
|
||||
end
|
||||
end
|
||||
_PUBLIC.terminal.write(table.concat(stringArgs, " ") .. "\n")
|
||||
end
|
||||
|
||||
function _G._PUBLIC.terminal.clear()
|
||||
width, height = gpu.getResolution()
|
||||
gpu.setForeground(defaultForegroundColor)
|
||||
gpu.setBackground(defaultBackgroundColor)
|
||||
gpu.fill(1,1,width,height," ")
|
||||
_PUBLIC.terminal.cursorPosX, _PUBLIC.terminal.cursorPosY = 1, 1
|
||||
end
|
||||
|
||||
function _G._PUBLIC.terminal.read(readHistoryType, prefix, defaultText, maxChars)
|
||||
checkArg(1, readHistoryType, "string", "nil")
|
||||
checkArg(2, prefix, "string", "nil")
|
||||
checkArg(3, defaultText, "string", "nil")
|
||||
checkArg(4, maxChars, "number", "nil")
|
||||
maxChars = maxChars or math.huge
|
||||
|
||||
local text = defaultText or ""
|
||||
|
||||
local historyIdx
|
||||
if readHistoryType then
|
||||
if not _PUBLIC.terminal.readHistory[readHistoryType] then
|
||||
_PUBLIC.terminal.readHistory[readHistoryType] = {text}
|
||||
elseif _PUBLIC.terminal.readHistory[readHistoryType][#_PUBLIC.terminal.readHistory[readHistoryType] ] ~= "" then
|
||||
table.insert(_PUBLIC.terminal.readHistory[readHistoryType], text)
|
||||
end
|
||||
historyIdx = #_PUBLIC.terminal.readHistory[readHistoryType]
|
||||
end
|
||||
|
||||
local function updateHistory()
|
||||
if not readHistoryType then return end
|
||||
_PUBLIC.terminal.readHistory[readHistoryType][historyIdx]=text
|
||||
end
|
||||
|
||||
local cur = unicode.len(text)+1
|
||||
if prefix then _PUBLIC.terminal.write(prefix) end
|
||||
local startX, startY = _PUBLIC.terminal.cursorPosX, _PUBLIC.terminal.cursorPosY
|
||||
local fg, bg = gpu.getForeground(), gpu.getBackground()
|
||||
local cursorBlink = true
|
||||
local function get(idx)
|
||||
idx=startX+idx-1
|
||||
return gpu.get(idx%width,startY+(idx//width))
|
||||
end
|
||||
local function checkScroll(y)
|
||||
for i=1,y-height do
|
||||
scrollDown()
|
||||
startY=startY-1
|
||||
end
|
||||
return math.min(y,height)
|
||||
end
|
||||
local function set(idx,chr,rev)
|
||||
if chr==nil or chr=="" then return end
|
||||
if rev then
|
||||
gpu.setForeground(bg)
|
||||
gpu.setBackground(fg)
|
||||
else
|
||||
gpu.setForeground(fg)
|
||||
gpu.setBackground(bg)
|
||||
end
|
||||
idx=startX+idx-1
|
||||
local setX, setY = (idx-1)%width+1, startY+((idx-1)//width+1)-1
|
||||
setY = checkScroll(setY)
|
||||
gpu.set(setX,setY,unicode.sub(chr,1,width-setX+1))
|
||||
for i=1,math.ceil((#chr+setX-1)/width)+1 do
|
||||
gpu.set(1,setY+i,unicode.sub(chr,2-setX+i*width,width+i*width-setX))
|
||||
setY = checkScroll(setY)
|
||||
end
|
||||
end
|
||||
local function strDef(a,b)
|
||||
if #a==0 then return b end
|
||||
return a
|
||||
end
|
||||
local function curPos(cur)
|
||||
return unicode.wlen(unicode.sub(text,1,cur-1))+1
|
||||
end
|
||||
local function add(chr)
|
||||
if type(chr)~="string" or #chr==0 then return end
|
||||
if unicode.len(text)>=maxChars then return end
|
||||
if maxChars<math.huge then
|
||||
chr=unicode.sub(chr,1,maxChars-unicode.len(text))
|
||||
end
|
||||
text=unicode.sub(text,1,cur-1)..chr..unicode.sub(text,cur)
|
||||
set(curPos(cur),chr,false)
|
||||
cur=math.min(cur+unicode.len(chr),maxChars+1)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
set(curPos(cur+1),unicode.sub(text,cur+1),false)
|
||||
end
|
||||
local function moveCur(dir)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
||||
cur=math.max(math.min(cur+dir,unicode.len(text)+1),1)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
end
|
||||
local function isLetter(chr)
|
||||
return not string.find("\x09 :@-./_~?&=%+#",chr,1,true)
|
||||
end
|
||||
local function nextCur(dir,chr,icur)
|
||||
if icur==nil then icur=cur end
|
||||
local next = math.max(math.min(icur+dir,unicode.len(text)+1),1)
|
||||
if chr then return unicode.sub(text,next,next) end
|
||||
return next
|
||||
end
|
||||
local function curAfterWord(dir)
|
||||
local ncur = cur
|
||||
while nextCur(dir,false,ncur)~=ncur and isLetter(nextCur(dir,true,ncur))==(dir==1) do
|
||||
ncur=nextCur(dir,false,ncur)
|
||||
end
|
||||
while nextCur(dir,false,ncur)~=ncur and isLetter(nextCur(dir,true,ncur))==(dir==-1) do
|
||||
ncur=nextCur(dir,false,ncur)
|
||||
end
|
||||
return ncur
|
||||
end
|
||||
local function moveWord(dir)
|
||||
if nextCur(dir)==cur then return end
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
||||
cur=curAfterWord(dir)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
end
|
||||
local function deleteWord(dir)
|
||||
local after = curAfterWord(dir)
|
||||
local lenb = unicode.wlen(text)
|
||||
if dir==1 then
|
||||
text=unicode.sub(text,1,cur-1)..unicode.sub(text,after)
|
||||
set(curPos(cur+1),unicode.sub(text,cur+1)..string.rep(" ",lenb-unicode.wlen(text)+1),false)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
else
|
||||
text = unicode.sub(text,1,after-1)..unicode.sub(text,cur)
|
||||
cur=after
|
||||
set(curPos(cur+1),unicode.sub(text,cur+1)..string.rep(" ",lenb-unicode.wlen(text)+1),false)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
end
|
||||
updateHistory()
|
||||
cursorBlink = true
|
||||
end
|
||||
local function isLine(chr)
|
||||
return chr=="\n" or chr=="\r"
|
||||
end
|
||||
--[[ gpu.set(startX,startY,unicode.sub(text,1,width-startX))
|
||||
for i=1,(#text+startX)//width-1 do
|
||||
gpu.set(startX,startY+i,unicode.sub(text,1+i*width,width-startX+i*width))
|
||||
end ]]
|
||||
set(1,text,false)
|
||||
set(curPos(cur)," ",true)
|
||||
|
||||
local function reprint(new)
|
||||
set(1,new..string.rep(" ",unicode.wlen(text)-unicode.wlen(new)+1),false)
|
||||
cur=unicode.len(new)+1
|
||||
text=new
|
||||
set(curPos(cur)," ",true)
|
||||
end
|
||||
|
||||
while true do
|
||||
local args = {event.pull("key_down", "clipboard", 0.5)}
|
||||
if args and args[1] == "key_down" and args[4] then
|
||||
local key = _PUBLIC.keyboard.keys[args[4]]
|
||||
if key=="up" and readHistoryType then
|
||||
historyIdx=math.max(historyIdx-1,1)
|
||||
reprint(_PUBLIC.terminal.readHistory[readHistoryType][historyIdx])
|
||||
elseif key=="down" and readHistoryType then
|
||||
historyIdx=math.min(historyIdx+1,#_PUBLIC.terminal.readHistory[readHistoryType])
|
||||
reprint(_PUBLIC.terminal.readHistory[readHistoryType][historyIdx])
|
||||
elseif key=="left" and _PUBLIC.keyboard.ctrlDown then
|
||||
moveWord(-1)
|
||||
elseif key=="right" and _PUBLIC.keyboard.ctrlDown then
|
||||
moveWord(1)
|
||||
elseif key=="left" then
|
||||
moveCur(-1)
|
||||
elseif key=="right" then
|
||||
moveCur(1)
|
||||
elseif key=="home" then
|
||||
moveCur(-math.huge)
|
||||
elseif key=="end" then
|
||||
moveCur(math.huge)
|
||||
elseif key=="back" and _PUBLIC.keyboard.ctrlDown then
|
||||
deleteWord(-1)
|
||||
elseif key=="delete" and _PUBLIC.keyboard.ctrlDown then
|
||||
deleteWord(1)
|
||||
elseif key=="back" and cur>1 then
|
||||
text=unicode.sub(text,1,cur-2)..unicode.sub(text,cur)
|
||||
cur=cur-1
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
set(curPos(cur)+1,unicode.sub(text,cur+1).." ",false)
|
||||
updateHistory()
|
||||
elseif key=="delete" then
|
||||
text = unicode.sub(text,1,cur-1)..unicode.sub(text,cur+1)
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
||||
cursorBlink = true
|
||||
if cur<=unicode.len(text) then
|
||||
set(curPos(cur+1),unicode.sub(text,cur+1).." ",false)
|
||||
end
|
||||
updateHistory()
|
||||
elseif key=="enter" then
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
||||
break
|
||||
elseif not (args[3]<32 or (args[3]>0x7F and args[3]<=0x9F)) then
|
||||
add(unicode.char(args[3]) or " ")
|
||||
updateHistory()
|
||||
end
|
||||
elseif args and args[1]=="clipboard" then
|
||||
local clip = args[3]
|
||||
if not args[3] then goto continue end
|
||||
while isLine(unicode.sub(clip,1,1)) do clip=unicode.sub(clip,2) end
|
||||
while isLine(unicode.sub(clip,-1)) do clip=unicode.sub(clip,1,-2) end
|
||||
add(clip)
|
||||
updateHistory()
|
||||
else
|
||||
cursorBlink=not cursorBlink
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),cursorBlink)
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
|
||||
if readHistoryType then
|
||||
if _PUBLIC.terminal.readHistory[readHistoryType][#_PUBLIC.terminal.readHistory[readHistoryType]]=="" then
|
||||
table.remove(_PUBLIC.terminal.readHistory[readHistoryType],#_PUBLIC.terminal.readHistory[readHistoryType])
|
||||
end
|
||||
if historyIdx<#_PUBLIC.terminal.readHistory[readHistoryType] then
|
||||
table.remove(_PUBLIC.terminal.readHistory[readHistoryType],historyIdx)
|
||||
table.insert(_PUBLIC.terminal.readHistory[readHistoryType],text)
|
||||
end
|
||||
while #_PUBLIC.terminal.readHistory[readHistoryType] > 50 do
|
||||
table.remove(_PUBLIC.terminal.readHistory[readHistoryType], 1)
|
||||
end
|
||||
end
|
||||
|
||||
_PUBLIC.terminal.cursorPosX=1
|
||||
_PUBLIC.terminal.cursorPosY=_PUBLIC.terminal.cursorPosY+math.ceil((unicode.wlen(text)+startX-1)/width)
|
||||
if _PUBLIC.terminal.cursorPosY>height then scrollDown() end
|
||||
|
||||
return text
|
||||
end
|
||||
end
|
||||
|
||||
function module.exit()
|
||||
_G._PUBLIC.terminal = nil
|
||||
end
|
||||
|
||||
return module
|
||||
@@ -0,0 +1,149 @@
|
||||
_G._PUBLIC.tsched = {}
|
||||
_G.tsched = {}
|
||||
_G.tsched.tasks = {}
|
||||
|
||||
local currentTask
|
||||
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local filesystem = require("filesystem")
|
||||
local json = require("json")
|
||||
local gpu = component.gpu
|
||||
local ocelot = component.ocelot
|
||||
|
||||
function _G._PUBLIC.tsched.runAsTask(path,...)
|
||||
local args = {...}
|
||||
local function taskFunction()
|
||||
local result, errorMessage = xpcall(function(...)
|
||||
local args = table.pack(...)
|
||||
if not filesystem.exists(path) then
|
||||
error("No such file: " .. path)
|
||||
end
|
||||
local handle, data, tmpdata = filesystem.open(path), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
handle:close()
|
||||
|
||||
-- Userland environment definition
|
||||
local userland = table.copy(_PUBLIC)
|
||||
userland._G = userland
|
||||
|
||||
assert(load(data, "="..path, "t", userland))(table.unpack(args))
|
||||
end, function(errorMessage)
|
||||
return errorMessage .. "\n \n" .. debug.traceback()
|
||||
end, path, table.unpack(args))
|
||||
if not result then
|
||||
if print then
|
||||
gpu.freeAllBuffers()
|
||||
print("\n\27[91m" .. errorMessage)
|
||||
else
|
||||
error(errorMessage)
|
||||
end
|
||||
end
|
||||
--require(path, table.unpack(args))
|
||||
end
|
||||
_PUBLIC.tsched.addTask(taskFunction, string.match(tostring(path), "([^/]+)%.lua$"))
|
||||
end
|
||||
|
||||
function _G._PUBLIC.tsched.addTask(func, name)
|
||||
ocelot.log("Added task " .. name)
|
||||
local task = coroutine.create(func)
|
||||
table.insert(tsched.tasks, {["task"] = task, ["name"] = name})
|
||||
return task
|
||||
end
|
||||
|
||||
function _G._PUBLIC.tsched.removeTask(id)
|
||||
-- TODO: Check for user permissions before running
|
||||
table.remove(_G.tsched.tasks, id)
|
||||
end
|
||||
|
||||
function handleError(errormsg)
|
||||
if errormsg == nil then -- TODO: Replace with proper error handling (if this isn't considered proper..?)
|
||||
print("\27[91munknown error" .. "\n \n" .. debug.traceback())
|
||||
else
|
||||
print("\27[91m" .. tostring(errormsg) .. "\n \n" .. debug.traceback())
|
||||
end
|
||||
end
|
||||
|
||||
local function runTasks()
|
||||
for i = 1, #_G.tsched.tasks do
|
||||
if tsched.tasks[i] then
|
||||
currentTask = tsched.tasks[i]
|
||||
local result, errorMessage = coroutine.resume(tsched.tasks[i].task)
|
||||
if not result then
|
||||
handleError(errorMessage)
|
||||
end
|
||||
if coroutine.status(tsched.tasks[i].task) == "dead" then
|
||||
_PUBLIC.tsched.removeTask(i)
|
||||
--ocelot.log("Removed coroutine")
|
||||
i = i - 1
|
||||
end
|
||||
--computer.pullSignal(0)
|
||||
--coroutine.yield()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _G._PUBLIC.tsched.getCurrentTask()
|
||||
return table.copy(currentTask)
|
||||
end
|
||||
|
||||
function _G._PUBLIC.tsched.getTasks()
|
||||
return table.copy(tsched.tasks)
|
||||
end
|
||||
|
||||
local function taskFunction()
|
||||
local result, errorMessage = xpcall(function()
|
||||
if not filesystem.exists("/halyde/kernel/evmgr.lua") then
|
||||
error("No such file: /halyde/kernel/evmgr.lua")
|
||||
end
|
||||
local handle, data, tmpdata = filesystem.open("/halyde/kernel/evmgr.lua"), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
handle:close()
|
||||
assert(load(data, "=/halyde/kernel/evmgr.lua"))()
|
||||
end, function(errorMessage)
|
||||
return errorMessage .. "\n \n" .. debug.traceback()
|
||||
end, "/halyde/kernel/evmgr.lua")
|
||||
if not result then
|
||||
if print then
|
||||
gpu.freeAllBuffers()
|
||||
print("\n\27[91m" .. errorMessage)
|
||||
else
|
||||
error(errorMessage)
|
||||
end
|
||||
end
|
||||
end
|
||||
_PUBLIC.tsched.addTask(taskFunction, "evmgr")
|
||||
package.preload("event")
|
||||
|
||||
local handle, data, tmpdata = filesystem.open("/halyde/config/startupapps.json", "r"), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
handle:close()
|
||||
for _, line in ipairs(json.decode(data)) do
|
||||
if line ~= "" then
|
||||
--[[ if _G.print then
|
||||
print(line)
|
||||
end ]]
|
||||
_G._PUBLIC.tsched.runAsTask(line)
|
||||
runTasks()
|
||||
end
|
||||
end
|
||||
-- _G.cormgr.loadCoroutine("/halyde/core/shell.lua")
|
||||
|
||||
while true do
|
||||
runTasks()
|
||||
if #_G.tsched.tasks == 0 then
|
||||
computer.beep(1000, 0.5)
|
||||
while true do
|
||||
computer.pullSignal()
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,97 +0,0 @@
|
||||
local compLib
|
||||
local LLcomponent
|
||||
if table.copy then
|
||||
compLib = table.copy(component)
|
||||
LLcomponent = table.copy(component)
|
||||
else
|
||||
compLib = {}
|
||||
LLcomponent = component
|
||||
end
|
||||
|
||||
--local ocelot = LLcomponent.proxy(LLcomponent.list("ocelot")())
|
||||
--ocelot.log("loaded")
|
||||
|
||||
_G.componentlib = {["additions"] = {}, ["removals"] = {}}
|
||||
compLib.virtual = {}
|
||||
|
||||
function compLib.virtual.add(address, componentType, proxy)
|
||||
checkArg(1, address, "string")
|
||||
checkArg(2, componentType, "string")
|
||||
checkArg(3, proxy, "table")
|
||||
proxy["address"] = address
|
||||
componentlib.additions[address] = {["componentType"] = componentType, ["proxy"] = proxy}
|
||||
if componentlib.removals[address] then
|
||||
componentlib.removals[address] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function compLib.virtual.remove(address)
|
||||
checkArg(1, address, "string")
|
||||
if componentlib.additions[address] then
|
||||
componentlib.additions[address] = nil
|
||||
else
|
||||
table.insert(componentlib.removals, address)
|
||||
end
|
||||
end
|
||||
|
||||
function compLib.list(componentType)
|
||||
checkArg(1, componentType, "string", "nil")
|
||||
local componentList = table.copy(LLcomponent.list(componentType))
|
||||
for address, dataTable in pairs(componentlib.additions) do
|
||||
if dataTable.componentType == componentType or not componentType then
|
||||
componentList[address] = dataTable.componentType
|
||||
end
|
||||
end
|
||||
for _, address in pairs(componentlib.removals) do
|
||||
componentList[address] = nil
|
||||
end
|
||||
local i, value
|
||||
setmetatable(componentList, {__call = function(self)
|
||||
i, value = next(self, i)
|
||||
return i, value
|
||||
end})
|
||||
return componentList
|
||||
end
|
||||
|
||||
function compLib.proxy(address)
|
||||
if componentlib.additions[address] then
|
||||
--ocelot.log("vcomponent")
|
||||
return componentlib.additions[address].proxy
|
||||
else
|
||||
return LLcomponent.proxy(address)
|
||||
end
|
||||
end
|
||||
|
||||
function compLib.invoke(address, funcName, ...)
|
||||
--ocelot.log("Invoking " .. funcName .. " from " .. address)
|
||||
if componentlib.additions[address] then
|
||||
--ocelot.log("vcomponent")
|
||||
return componentlib.additions[address].proxy[funcName](...)
|
||||
else
|
||||
return LLcomponent.invoke(address, funcName, ...)
|
||||
end
|
||||
end
|
||||
|
||||
function compLib.get(address)
|
||||
checkArg(1, address, "string")
|
||||
if #address < 3 then
|
||||
return nil, "abbreviated address must be at least 3 characters long"
|
||||
end
|
||||
for currentAddress, name in compLib.list() do
|
||||
if currentAddress:find("^" .. address) then
|
||||
return(currentAddress)
|
||||
end
|
||||
end
|
||||
return nil, "full address not found"
|
||||
end
|
||||
|
||||
-- Add main component proxies to the library
|
||||
setmetatable(compLib, {["__index"] = function(_, item)
|
||||
if compLib.list(item)() then
|
||||
return compLib.proxy(compLib.list(item)())
|
||||
else
|
||||
return compLib[item]
|
||||
end
|
||||
end})
|
||||
|
||||
return compLib
|
||||
@@ -1,14 +0,0 @@
|
||||
local computerlib = table.copy(computer)
|
||||
local LLcomputer = table.copy(computer)
|
||||
|
||||
function computerlib.pullSignal(timeout)
|
||||
local startTime = LLcomputer.uptime()
|
||||
local result
|
||||
repeat
|
||||
result = {LLcomputer.pullSignal(0)}
|
||||
coroutine.yield()
|
||||
until result or timeout and LLcomputer.uptime() >= startTime + timeout
|
||||
return table.unpack(result)
|
||||
end
|
||||
|
||||
return computerlib
|
||||
@@ -1,55 +0,0 @@
|
||||
local computer = import("computer")
|
||||
local event = {}
|
||||
|
||||
local bufferTime = 0.1 -- A little bit of buffer time so events won't be skipped by accident.
|
||||
|
||||
--local ocelot = component.proxy(component.list("ocelot")())
|
||||
function event.pull(...)
|
||||
local args = {...}
|
||||
local evtypes, timeout = {}, nil
|
||||
|
||||
for _, arg in pairs(args) do
|
||||
if type(arg) == "number" and not timeout then -- It's a timeout
|
||||
timeout = arg
|
||||
else -- It's an event type
|
||||
table.insert(evtypes, tostring(arg))
|
||||
end
|
||||
end
|
||||
|
||||
local startTime = computer.uptime()
|
||||
|
||||
while true do
|
||||
-- Check event queue for matching event
|
||||
for i = 1, #evmgr.eventQueue do
|
||||
local foundevent = false
|
||||
if evtypes[1] then -- event type(s) specified
|
||||
for _, evtype in pairs(evtypes) do
|
||||
if evmgr.eventQueue[i][2] == evtype and evmgr.eventQueue[i][1] >= startTime - bufferTime then
|
||||
foundevent = true
|
||||
end
|
||||
end
|
||||
else
|
||||
if evmgr.eventQueue[i][1] >= startTime - bufferTime then
|
||||
foundevent = true
|
||||
end
|
||||
end
|
||||
if foundevent then
|
||||
-- Found matching event (or any event if no type specified)
|
||||
local result = table.copy(evmgr.eventQueue[i])
|
||||
table.remove(evmgr.eventQueue, i)
|
||||
table.remove(result, 1) -- remove the time of event argument
|
||||
return table.unpack(result)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if we've timed out
|
||||
if timeout and computer.uptime() >= startTime + timeout then
|
||||
return nil -- Timed out, return nil
|
||||
end
|
||||
|
||||
-- Yield to allow other processes to run and more events to be added
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
return event
|
||||
@@ -1,506 +0,0 @@
|
||||
local loadfile = ... -- raw loadfile from boot.lua
|
||||
local component, computer
|
||||
|
||||
if loadfile then
|
||||
unicode = loadfile("/halyde/lib/unicode.lua")(loadfile)
|
||||
component = loadfile("/halyde/lib/component.lua")(loadfile)
|
||||
computer = _G.computer
|
||||
elseif import then
|
||||
unicode = import("unicode")
|
||||
component = import("component")
|
||||
computer = import("computer")
|
||||
end
|
||||
|
||||
local filesystem = {}
|
||||
|
||||
function filesystem.canonical(path)
|
||||
checkArg(1, path, "string")
|
||||
local segList = {}
|
||||
if path:sub(1, 1) ~= "/" then
|
||||
path = "/" .. path
|
||||
end
|
||||
path = path:gsub("/+", "/")
|
||||
for segment in path:gmatch("[^/]+") do
|
||||
if segment == ".." and segList[1] then
|
||||
table.remove(segList, #segList)
|
||||
elseif segment ~= "." then
|
||||
table.insert(segList, segment)
|
||||
end
|
||||
end
|
||||
return "/" .. table.concat(segList, "/")
|
||||
end
|
||||
|
||||
function filesystem.concat(path1, path2)
|
||||
checkArg(1, path1, "string")
|
||||
checkArg(2, path2, "string")
|
||||
if path1:sub(-1, -1) == "/" then
|
||||
path1 = path1:sub(1, -2)
|
||||
end
|
||||
if path2:sub(1, 1) ~= "/" then
|
||||
path2 = "/" .. path2
|
||||
end
|
||||
return path1 .. path2
|
||||
end
|
||||
|
||||
function filesystem.absolutePath(path) -- returns the address and absolute path of an object
|
||||
checkArg(1, path, "string")
|
||||
path = filesystem.canonical(path)
|
||||
local address = nil
|
||||
if path:find("^/tmp") then
|
||||
address = computer.tmpAddress()
|
||||
path = path:sub(5)
|
||||
elseif path:find("^/mnt/...") then
|
||||
address = component.get(path:sub(6,8))
|
||||
if not address then
|
||||
address = computer.getBootAddress()
|
||||
else
|
||||
path = path:sub(9)
|
||||
end
|
||||
else
|
||||
address = computer.getBootAddress()
|
||||
end
|
||||
if not address then
|
||||
return nil, "no such component"
|
||||
end
|
||||
return address, path
|
||||
end
|
||||
|
||||
function filesystem.exists(path) -- check if path exists
|
||||
checkArg(1, path, "string")
|
||||
local address, absPath = filesystem.absolutePath(path)
|
||||
if not address then
|
||||
return false
|
||||
end
|
||||
if absPath:find("^/special/drive/...") then
|
||||
return not not (computer.getBootAddress() and component.get(absPath:sub(16,18)))
|
||||
end
|
||||
if absPath:find("^/special/eeprom/") then
|
||||
return table.find({"init.lua","data.bin","label.txt"},absPath:sub(17))
|
||||
end
|
||||
return component.invoke(address, "exists", absPath)
|
||||
end
|
||||
|
||||
local function readBytes(self,n)
|
||||
n = n or 1
|
||||
if n==1 then
|
||||
local byte = self:read(1)
|
||||
if byte==nil then return nil end
|
||||
return string.byte(byte)
|
||||
end
|
||||
local bytes, res = {string.byte(self:read(n),1,n)}, 0
|
||||
if self.littleEndian then
|
||||
for i=#bytes,1,-1 do
|
||||
res = (res<<8)&0xFFFFFFFF | bytes[i]
|
||||
end
|
||||
else
|
||||
for i=1,#bytes do
|
||||
res = (res<<8)&0xFFFFFFFF | bytes[i]
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local function readUnicodeChar(self)
|
||||
return unicode.readChar(function()
|
||||
return self:readBytes(1)
|
||||
end)
|
||||
end
|
||||
|
||||
local function iterateBytes(self)
|
||||
return function()
|
||||
local byte = readBytes(self,1)
|
||||
if byte==nil then self:close() end
|
||||
return byte
|
||||
end
|
||||
end
|
||||
|
||||
local function iterateUnicodeChars(self)
|
||||
return unicode.iterate(iterateBytes(self))
|
||||
end
|
||||
|
||||
function filesystem.makeReadStream(content)
|
||||
local properHandle = {}
|
||||
local readcursor = 1
|
||||
function properHandle.read(self, amount)
|
||||
checkArg(2, amount, "number")
|
||||
local limit = string.len(content)+1
|
||||
local out = nil
|
||||
if readcursor<limit then
|
||||
if amount==math.huge then
|
||||
out = string.sub(content,math.min(readcursor,limit))
|
||||
else
|
||||
out = string.sub(content,math.min(readcursor,limit),math.min(readcursor+amount-1,limit))
|
||||
end
|
||||
end
|
||||
readcursor=readcursor+amount
|
||||
if out=="" then
|
||||
return nil
|
||||
end
|
||||
return out
|
||||
end
|
||||
properHandle.readBytes = readBytes
|
||||
properHandle.readUnicodeChar = readUnicodeChar
|
||||
properHandle.iterateBytes = iterateBytes
|
||||
properHandle.iterateUnicodeChars = iterateUnicodeChars
|
||||
function properHandle.write()
|
||||
return nil
|
||||
end
|
||||
function properHandle.close()
|
||||
content=nil
|
||||
end
|
||||
return properHandle
|
||||
end
|
||||
|
||||
function filesystem.open(path, mode, buffered) -- opens a file and returns its handle
|
||||
checkArg(1, path, "string")
|
||||
checkArg(2, mode, "string", "nil")
|
||||
checkArg(3, buffered, "boolean", "nil")
|
||||
if not mode then
|
||||
mode = "r"
|
||||
end
|
||||
if buffered == nil then
|
||||
buffered = true
|
||||
end
|
||||
if not (mode == "r" or mode == "w" or mode == "rb" or mode == "wb" or mode == "a" or mode == "ab") then
|
||||
return nil, "invalid handle type"
|
||||
end
|
||||
if path:find("^/special") and not filesystem.exists(path) then
|
||||
return nil, "/special does not allow creating files"
|
||||
end
|
||||
local address, absPath = filesystem.absolutePath(path)
|
||||
local unmanagedDrive = address==computer.getBootAddress() and absPath:find("^/special/drive")
|
||||
local unmanagedProxy, sectorSize, sectorCount, handle
|
||||
if unmanagedDrive then
|
||||
unmanagedProxy = component.proxy(component.get(absPath:sub(16,18)))
|
||||
sectorSize = unmanagedProxy.getSectorSize()
|
||||
sectorCount = math.ceil(unmanagedProxy.getCapacity()/sectorSize)
|
||||
elseif not (address==computer.getBootAddress() and absPath:find("^/special/")) then
|
||||
local handleArgs = {component.invoke(address, "open", absPath, mode)}
|
||||
handle = handleArgs[1]
|
||||
if not handle then
|
||||
return table.unpack(handleArgs)
|
||||
end
|
||||
handleArgs = nil
|
||||
end
|
||||
local properHandle = {}
|
||||
properHandle.handle = handle
|
||||
properHandle.address = address
|
||||
local content = nil
|
||||
local readcursor = 1
|
||||
if buffered and mode:sub(1,1)=="r" then
|
||||
content=""
|
||||
repeat
|
||||
tmpdata = component.invoke(address, "read", handle, math.huge or math.maxinteger)
|
||||
content = content .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
component.invoke(address, "close", handle)
|
||||
end
|
||||
function properHandle.read(self, amount)
|
||||
checkArg(2, amount, "number")
|
||||
if unmanagedDrive then
|
||||
local sectorIdx = ((readcursor-1)//sectorSize)+1
|
||||
if sectorIdx>sectorCount then return nil end
|
||||
local sector = unmanagedProxy.readSector(sectorIdx)
|
||||
local data = sector:sub(((readcursor-1)%sectorSize)+1,((readcursor+math.min(amount,sectorSize)-2)%sectorSize)+1)
|
||||
readcursor=readcursor+#data
|
||||
if data=="" then return nil end
|
||||
return data
|
||||
else
|
||||
if buffered then
|
||||
local limit = string.len(content)+1
|
||||
local out = nil
|
||||
if readcursor<limit then
|
||||
if amount==math.huge then
|
||||
out = string.sub(content,math.min(readcursor,limit))
|
||||
else
|
||||
out = string.sub(content,math.min(readcursor,limit),math.min(readcursor+amount-1,limit))
|
||||
end
|
||||
end
|
||||
readcursor=readcursor+amount
|
||||
if out=="" then
|
||||
return nil
|
||||
end
|
||||
return out
|
||||
else
|
||||
return component.invoke(self.address, "read", self.handle, amount)
|
||||
end
|
||||
end
|
||||
end
|
||||
properHandle.readBytes = readBytes
|
||||
properHandle.readUnicodeChar = readUnicodeChar
|
||||
properHandle.iterateBytes = iterateBytes
|
||||
properHandle.iterateUnicodeChars = iterateUnicodeChars
|
||||
function properHandle.write(self, data)
|
||||
checkArg(2, data, "string")
|
||||
if unmanagedDrive then
|
||||
local startSector = ((readcursor-1)//sectorSize)+1
|
||||
if startSector>sectorCount then return nil, "not enough space" end
|
||||
local startSByte = ((readcursor-1)%sectorSize)+1
|
||||
local sect = unmanagedProxy.readSector(startSector)
|
||||
unmanagedProxy.writeSector(startSector,sect:sub(1,startSByte-1)..data:sub(1,sectorSize-startSByte+1))
|
||||
for i=2,(#data+startSByte)//sectorSize do
|
||||
if startSector+i-1>sectorCount then return nil, "not enough space" end
|
||||
unmanagedProxy.writeSector(startSector+i-1,data:sub(startSByte+sectorSize*(i-1),startSByte+sectorSize*i-1))
|
||||
end
|
||||
readcursor=readcursor+#data
|
||||
return true
|
||||
else
|
||||
return component.invoke(self.address, "write", self.handle, data)
|
||||
end
|
||||
end
|
||||
function properHandle.close(self)
|
||||
if buffered then
|
||||
content = nil
|
||||
else
|
||||
return component.invoke(self.address, "close", self.handle)
|
||||
end
|
||||
end
|
||||
if address==computer.getBootAddress() then
|
||||
local eeprom
|
||||
pcall(function()
|
||||
eeprom = component.eeprom
|
||||
end)
|
||||
if eeprom then
|
||||
local getFunc, setFunc
|
||||
if absPath=="/special/eeprom/init.lua" then
|
||||
getFunc,setFunc = "get","set"
|
||||
elseif absPath=="/special/eeprom/data.bin" then
|
||||
getFunc,setFunc = "getData","setData"
|
||||
elseif absPath=="/special/eeprom/label.txt" then
|
||||
getFunc,setFunc = "getLabel","setLabel"
|
||||
end
|
||||
if mode:sub(1,1)=="r" and getFunc then
|
||||
local stream = filesystem.makeReadStream(eeprom[getFunc]() or "")
|
||||
properHandle.read = stream.read
|
||||
properHandle.close = stream.close
|
||||
elseif mode:sub(1,1)=="w" and setFunc then
|
||||
local content = ""
|
||||
function properHandle.write(self, data)
|
||||
checkArg(2, data, "string")
|
||||
content=content..data
|
||||
end
|
||||
function properHandle.close(self)
|
||||
return eeprom[setFunc](content)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return properHandle
|
||||
end
|
||||
|
||||
function filesystem.list(path)
|
||||
checkArg(1, path, "string")
|
||||
path = filesystem.canonical(path)
|
||||
if path == "/mnt" then
|
||||
-- list drives
|
||||
local returnTable = {}
|
||||
local tmpAddress = computer.tmpAddress()
|
||||
for address, _ in component.list("filesystem") do
|
||||
if address~=tmpAddress then
|
||||
table.insert(returnTable, address:sub(1, 3) .. "/")
|
||||
end
|
||||
end
|
||||
return returnTable
|
||||
elseif path == "/special/drive" then
|
||||
local returnTable = {}
|
||||
local tmpAddress = computer.tmpAddress()
|
||||
for address, type in component.list("drive") do
|
||||
if address~=tmpAddress and type=="drive" then
|
||||
table.insert(returnTable, address:sub(1, 3))
|
||||
end
|
||||
end
|
||||
return returnTable
|
||||
elseif path=="/special/eeprom" then
|
||||
return {"init.lua","data.bin","label.txt"}
|
||||
else
|
||||
local address, absPath = filesystem.absolutePath(path)
|
||||
if not address then
|
||||
return false
|
||||
end
|
||||
return component.invoke(address, "list", absPath)
|
||||
end
|
||||
end
|
||||
|
||||
function filesystem.size(path)
|
||||
checkArg(1, path, "string")
|
||||
local address, absPath = filesystem.absolutePath(path)
|
||||
if not address then
|
||||
return false
|
||||
end
|
||||
if address==computer.getBootAddress() then
|
||||
if absPath:find("^/special/drive") then
|
||||
local drive = component.get(absPath:sub(16,18))
|
||||
if not drive then return false end
|
||||
return component.invoke(drive,"getCapacity")
|
||||
elseif absPath:find("^/special/eeprom") then
|
||||
local eeprom
|
||||
pcall(function()
|
||||
eeprom = component.eeprom
|
||||
end)
|
||||
if eeprom then
|
||||
local getFunc
|
||||
if absPath=="/special/eeprom/init.lua" then
|
||||
getFunc = "get"
|
||||
elseif absPath=="/special/eeprom/data.bin" then
|
||||
getFunc = "getData"
|
||||
elseif absPath=="/special/eeprom/label.txt" then
|
||||
getFunc = "getLabel"
|
||||
end
|
||||
return #(eeprom[getFunc]())
|
||||
end
|
||||
end
|
||||
end
|
||||
return component.invoke(address, "size", absPath)
|
||||
end
|
||||
|
||||
local function getRecursiveList(address,absPath)
|
||||
local list = component.invoke(address,"list",absPath)
|
||||
local dirList = {}
|
||||
local listChanged = true
|
||||
while listChanged do
|
||||
listChanged = false
|
||||
for i=1,#list do
|
||||
if component.invoke(address, "isDirectory", absPath.."/"..list[i]) then
|
||||
listChanged = true
|
||||
local dir = list[i]
|
||||
if dir:sub(-1)=="/" then dir=dir:sub(1,-2) end
|
||||
table.insert(dirList,dir)
|
||||
table.remove(list,i)
|
||||
local subDir = component.invoke(address,"list",absPath.."/"..dir)
|
||||
for j=1,#subDir do table.insert(list,dir.."/"..subDir[j]) end
|
||||
end
|
||||
end
|
||||
end
|
||||
return list,dirList
|
||||
end
|
||||
|
||||
local function copyContent(fromHandle,toHandle)
|
||||
if not (fromHandle and toHandle) then return end
|
||||
local memory = math.floor(computer.freeMemory()*0.8)
|
||||
local tmpdata
|
||||
while true do
|
||||
tmpdata = fromHandle:read(memory)
|
||||
if not tmpdata then break end
|
||||
local status,reason = toHandle:write(tmpdata)
|
||||
if status~=true then break end
|
||||
end
|
||||
fromHandle:close()
|
||||
toHandle:close()
|
||||
end
|
||||
|
||||
local function copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
|
||||
-- TODO: make this use copyContent
|
||||
if fromAbsPath:sub(-1)=="/" then fromAbsPath=fromAbsPath:sub(1,-2) end
|
||||
if toAbsPath:sub(-1)=="/" then toAbsPath=toAbsPath:sub(1,-2) end
|
||||
component.invoke(toAddress,"makeDirectory",toAbsPath)
|
||||
local fileList,dirList = getRecursiveList(fromAddress,fromAbsPath)
|
||||
for i=1,#dirList do
|
||||
component.invoke(toAddress,"makeDirectory",toAbsPath.."/"..dirList[i])
|
||||
end
|
||||
for i=1,#fileList do
|
||||
local fromFile, toFile = fromAbsPath.."/"..fileList[i], toAbsPath.."/"..fileList[i]
|
||||
--[[ local handle = component.invoke(fromAddress, "open", fromFile, "r")
|
||||
local data, tmpdata = "", nil
|
||||
repeat
|
||||
tmpdata = component.invoke(fromAddress, "read", handle, math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
tmpdata = component.invoke(fromAddress, "close", handle)
|
||||
local handle = component.invoke(toAddress, "open", toFile, "w")
|
||||
component.invoke(toAddress, "write", handle, data)
|
||||
component.invoke(toAddress, "close", handle) ]]
|
||||
local fromHandle = component.invoke(fromAddress, "open", fromFile, "r")
|
||||
local toHandle = component.invoke(toAddress, "open", toFile, "w")
|
||||
copyContent({
|
||||
["read"]=function(...) return component.invoke(fromAddress, "read", handle, ...) end,
|
||||
["close"]=function(...) return component.invoke(fromAddress, "close", handle, ...) end
|
||||
},{
|
||||
["write"]=function(...) return component.invoke(fromAddress, "write", handle, ...) end,
|
||||
["close"]=function(...) return component.invoke(fromAddress, "close", handle, ...) end
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function filesystem.isDirectory(path)
|
||||
checkArg(1, path, "string")
|
||||
local address, absPath = filesystem.absolutePath(path)
|
||||
if not address then
|
||||
return false
|
||||
end
|
||||
return component.invoke(address, "isDirectory", absPath)
|
||||
end
|
||||
|
||||
function filesystem.rename(fromPath, toPath)
|
||||
checkArg(1, fromPath, "string")
|
||||
checkArg(2, toPath, "string")
|
||||
local fromAddress, fromAbsPath = filesystem.absolutePath(fromPath)
|
||||
local toAddress, toAbsPath = filesystem.absolutePath(toPath)
|
||||
if not fromAddress or not toAddress then
|
||||
return false
|
||||
end
|
||||
if fromAddress == toAddress then
|
||||
return component.invoke(fromAddress, "rename", fromAbsPath, toAbsPath)
|
||||
elseif filesystem.isDirectory(fromPath) then -- component.invoke(fromAddress, "isDirectory", fromAbsPath) then
|
||||
copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
|
||||
filesystem.remove(fromPath) -- component.invoke(fromAddress,"remove", fromAbsPath)
|
||||
else
|
||||
local handle, data, tmpdata = filesystem.open(fromPath), "", nil -- component.invoke(fromAddress, "open", fromAbsPath, "r"), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger) -- component.invoke(fromAddress, "read", handle, math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
tmpdata = handle:close() -- component.invoke(fromAddress, "close", handle)
|
||||
local handle = filesystem.open(toPath) -- component.invoke(toAddress, "open", toAbsPath, "w")
|
||||
handle:write(data) -- component.invoke(toAddress, "write", handle, data)
|
||||
handle:close() -- component.invoke(toAddress, "close", handle)
|
||||
filesystem.remove(fromPath) -- component.invoke(fromAddress, "remove", fromAbsPath)
|
||||
end
|
||||
end
|
||||
|
||||
function filesystem.copy(fromPath, toPath)
|
||||
checkArg(1, fromPath, "string")
|
||||
checkArg(2, toPath, "string")
|
||||
local fromAddress, fromAbsPath = filesystem.absolutePath(fromPath)
|
||||
local toAddress, toAbsPath = filesystem.absolutePath(toPath)
|
||||
if not fromAddress or not toAddress then
|
||||
return false
|
||||
end
|
||||
if filesystem.isDirectory(fromPath) then -- component.invoke(fromAddress, "isDirectory", fromAbsPath)
|
||||
copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
|
||||
else
|
||||
--[[ local handle = filesystem.open(fromPath,"r")
|
||||
local data, tmpdata = "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
||||
data = data .. (tmpdata or "")
|
||||
until not tmpdata
|
||||
tmpdata = handle:close()
|
||||
local handle = filesystem.open(toPath,"w")
|
||||
handle:write(data)
|
||||
handle:close() ]]
|
||||
copyContent(filesystem.open(fromPath,"r"),filesystem.open(toPath,"w"))
|
||||
end
|
||||
end
|
||||
|
||||
function filesystem.remove(path)
|
||||
checkArg(1, path, "string")
|
||||
local address, absPath = filesystem.absolutePath(path)
|
||||
if not address then
|
||||
return false
|
||||
end
|
||||
if absPath:find("^/special") then return false end
|
||||
if absPath:find("^/tmp") then return false end
|
||||
if absPath:find("^/mnt") then return false end
|
||||
return component.invoke(address, "remove", absPath)
|
||||
end
|
||||
|
||||
function filesystem.makeDirectory(path)
|
||||
checkArg(1, path, "string")
|
||||
local address, absPath = filesystem.absolutePath(path)
|
||||
if not address then
|
||||
return false
|
||||
end
|
||||
return component.invoke(address, "makeDirectory", absPath)
|
||||
end
|
||||
|
||||
return(filesystem)
|
||||
@@ -1,4 +0,0 @@
|
||||
-- json.lua by rxi
|
||||
-- Minified with luamin
|
||||
-- Original: https://github.com/rxi/json.lua
|
||||
local a={_version="0.1.2"}local b;local c={["\\"]="\\",["\""]="\"",["\b"]="b",["\f"]="f",["\n"]="n",["\r"]="r",["\t"]="t"}local d={["/"]="/"}for e,f in pairs(c)do d[f]=e end;local function g(h)return"\\"..(c[h]or string.format("u%04x",h:byte()))end;local function i(j)return"null"end;local function k(j,l)local m={}l=l or{}if l[j]then error("circular reference")end;l[j]=true;if rawget(j,1)~=nil or next(j)==nil then local n=0;for e in pairs(j)do if type(e)~="number"then error("invalid table: mixed or invalid key types")end;n=n+1 end;if n~=#j then error("invalid table: sparse array")end;for o,f in ipairs(j)do table.insert(m,b(f,l))end;l[j]=nil;return"["..table.concat(m,",").."]"else for e,f in pairs(j)do if type(e)~="string"then error("invalid table: mixed or invalid key types")end;table.insert(m,b(e,l)..":"..b(f,l))end;l[j]=nil;return"{"..table.concat(m,",").."}"end end;local function p(j)return'"'..j:gsub('[%z\1-\31\\"]',g)..'"'end;local function q(j)if j~=j or j<=-math.huge or j>=math.huge then error("unexpected number value '"..tostring(j).."'")end;return string.format("%.14g",j)end;local r={["nil"]=i,["table"]=k,["string"]=p,["number"]=q,["boolean"]=tostring}b=function(j,l)local s=type(j)local t=r[s]if t then return t(j,l)end;error("unexpected type '"..s.."'")end;function a.encode(j)return b(j)end;local u;local function v(...)local m={}for o=1,select("#",...)do m[select(o,...)]=true end;return m end;local w=v(" ","\t","\r","\n")local x=v(" ","\t","\r","\n","]","}",",")local y=v("\\","/",'"',"b","f","n","r","t","u")local z=v("true","false","null")local A={["true"]=true,["false"]=false,["null"]=nil}local function B(C,D,E,F)for o=D,#C do if E[C:sub(o,o)]~=F then return o end end;return#C+1 end;local function G(C,D,H)local I=1;local J=1;for o=1,D-1 do J=J+1;if C:sub(o,o)=="\n"then I=I+1;J=1 end end;error(string.format("%s at line %d col %d",H,I,J))end;local function K(n)local t=math.floor;if n<=0x7f then return string.char(n)elseif n<=0x7ff then return string.char(t(n/64)+192,n%64+128)elseif n<=0xffff then return string.char(t(n/4096)+224,t(n%4096/64)+128,n%64+128)elseif n<=0x10ffff then return string.char(t(n/262144)+240,t(n%262144/4096)+128,t(n%4096/64)+128,n%64+128)end;error(string.format("invalid unicode codepoint '%x'",n))end;local function L(M)local N=tonumber(M:sub(1,4),16)local O=tonumber(M:sub(7,10),16)if O then return K((N-0xd800)*0x400+O-0xdc00+0x10000)else return K(N)end end;local function P(C,o)local m=""local Q=o+1;local e=Q;while Q<=#C do local R=C:byte(Q)if R<32 then G(C,Q,"control character in string")elseif R==92 then m=m..C:sub(e,Q-1)Q=Q+1;local h=C:sub(Q,Q)if h=="u"then local S=C:match("^[dD][89aAbB]%x%x\\u%x%x%x%x",Q+1)or C:match("^%x%x%x%x",Q+1)or G(C,Q-1,"invalid unicode escape in string")m=m..L(S)Q=Q+#S else if not y[h]then G(C,Q-1,"invalid escape char '"..h.."' in string")end;m=m..d[h]end;e=Q+1 elseif R==34 then m=m..C:sub(e,Q-1)return m,Q+1 end;Q=Q+1 end;G(C,o,"expected closing quote for string")end;local function T(C,o)local R=B(C,o,x)local M=C:sub(o,R-1)local n=tonumber(M)if not n then G(C,o,"invalid number '"..M.."'")end;return n,R end;local function U(C,o)local R=B(C,o,x)local V=C:sub(o,R-1)if not z[V]then G(C,o,"invalid literal '"..V.."'")end;return A[V],R end;local function W(C,o)local m={}local n=1;o=o+1;while 1 do local R;o=B(C,o,w,true)if C:sub(o,o)=="]"then o=o+1;break end;R,o=u(C,o)m[n]=R;n=n+1;o=B(C,o,w,true)local X=C:sub(o,o)o=o+1;if X=="]"then break end;if X~=","then G(C,o,"expected ']' or ','")end end;return m,o end;local function Y(C,o)local m={}o=o+1;while 1 do local Z,j;o=B(C,o,w,true)if C:sub(o,o)=="}"then o=o+1;break end;if C:sub(o,o)~='"'then G(C,o,"expected string for key")end;Z,o=u(C,o)o=B(C,o,w,true)if C:sub(o,o)~=":"then G(C,o,"expected ':' after key")end;o=B(C,o+1,w,true)j,o=u(C,o)m[Z]=j;o=B(C,o,w,true)local X=C:sub(o,o)o=o+1;if X=="}"then break end;if X~=","then G(C,o,"expected '}' or ','")end end;return m,o end;local _={['"']=P,["0"]=T,["1"]=T,["2"]=T,["3"]=T,["4"]=T,["5"]=T,["6"]=T,["7"]=T,["8"]=T,["9"]=T,["-"]=T,["t"]=U,["f"]=U,["n"]=U,["["]=W,["{"]=Y}u=function(C,D)local X=C:sub(D,D)local t=_[X]if t then return t(C,D)end;G(C,D,"unexpected character '"..X.."'")end;function a.decode(C)if type(C)~="string"then error("expected argument of type string, got "..type(C))end;local m,D=u(C,B(C,1,w,true))D=B(C,D,w,true)if D<=#C then G(C,D,"trailing garbage")end;return m end;return a
|
||||
@@ -1,432 +0,0 @@
|
||||
local raster = {
|
||||
["units"]={},
|
||||
["defaultBackgroundColor"]=0x000000,
|
||||
["defaultForegroundColor"]=0xFFFFFF,
|
||||
["displayWidth"]=0,
|
||||
["displayHeight"]=0,
|
||||
["charWidth"]=0,
|
||||
["charHeight"]=0,
|
||||
["backgroundColor"]=0xFFFFFF
|
||||
}
|
||||
|
||||
local component = import("component")
|
||||
-- local ocelot = component.proxy(component.list("ocelot")())
|
||||
local gpu = component.gpu
|
||||
|
||||
local display = {}
|
||||
local chunksAffected = {}
|
||||
|
||||
local renderBuffer = nil
|
||||
|
||||
-- braille rendering
|
||||
|
||||
function raster.units.charToBraille(x,y)
|
||||
return x*2,y*4
|
||||
end
|
||||
|
||||
function raster.units.brailleToChar(x,y)
|
||||
return math.ceil(x/2),math.ceil(y/4)
|
||||
end
|
||||
|
||||
function raster.init(width, height, bgcolor)
|
||||
-- NOTE: Width and height are in characters, not pixels in braille.
|
||||
-- If the width and height are nil, the entire screen will be used.
|
||||
if width==nil and height==nil then
|
||||
width, height = gpu.getResolution()
|
||||
end
|
||||
|
||||
raster.charWidth = width
|
||||
raster.charHeight = height
|
||||
|
||||
width, height = raster.units.charToBraille(width, height)
|
||||
|
||||
bgcolor = bgcolor or raster.defaultBackgroundColor
|
||||
if bgcolor~=0 then
|
||||
for i=1,width*height do
|
||||
display[i]=bgcolor
|
||||
end
|
||||
end
|
||||
|
||||
raster.displayWidth = width
|
||||
raster.displayHeight = height
|
||||
raster.backgroundColor = bgcolor
|
||||
|
||||
pcall(function()
|
||||
renderBuffer = gpu.allocateBuffer()
|
||||
end)
|
||||
|
||||
raster.clear()
|
||||
end
|
||||
|
||||
function raster.set(x, y, color)
|
||||
if x<1 or x>raster.displayWidth or y<1 or y>raster.displayHeight then
|
||||
return false
|
||||
end
|
||||
|
||||
color = color or raster.defaultForegroundColor
|
||||
local i = x+y*raster.displayWidth
|
||||
display[i] = color
|
||||
|
||||
local ci = math.floor((x-1)/2)+math.floor((y-1)/4)*raster.charWidth+1
|
||||
-- ocelot.log(x..","..y..":"..ci)
|
||||
chunksAffected[ci] = true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function raster.get(x, y)
|
||||
local i = x+y*raster.displayWidth
|
||||
return display[i] or raster.backgroundColor
|
||||
end
|
||||
|
||||
local function stats(arr)
|
||||
local out = {}
|
||||
for i=1,#arr do
|
||||
local v = arr[i]
|
||||
if out[v]==nil then
|
||||
out[v]=1
|
||||
else
|
||||
out[v] = out[v] + 1
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local function getKeys(t)
|
||||
local keys = {}
|
||||
for k,v in pairs(t) do
|
||||
table.insert(keys,{k,v})
|
||||
end
|
||||
table.sort(keys,function(a,b)
|
||||
return a[2]>b[2]
|
||||
end)
|
||||
for i=1,#keys do
|
||||
keys[i] = keys[i][1]
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
||||
local function colorDifference(a,b)
|
||||
return ((a>>16)&255)-((b>>16)&255)+((a>>8)&255)-((b>>8)&255)+(a&255)-(b&255)
|
||||
end
|
||||
|
||||
local function limitTwoColors(arr)
|
||||
local colors = getKeys(stats(arr))
|
||||
for i=1,#arr do
|
||||
local v=arr[i]
|
||||
if v==colors[1] then
|
||||
arr[i]=0
|
||||
goto continue
|
||||
elseif v==colors[2] then
|
||||
arr[i]=1
|
||||
goto continue
|
||||
else
|
||||
--error("Pixel is not in the two colors (raster.lua:90)")
|
||||
-- get closest color so atleast it kinda shows
|
||||
if colorDifference(v,colors[1])<colorDifference(v,colors[2]) then
|
||||
arr[i]=0
|
||||
else
|
||||
arr[i]=1
|
||||
end
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
return arr,colors[1] or 0,colors[2] or 0
|
||||
end
|
||||
|
||||
local function arrayToBraille(arr)
|
||||
local codePoint = 0x2800
|
||||
for i=1,8 do
|
||||
codePoint = codePoint | arr[i]<<(i-1)
|
||||
end
|
||||
if codePoint==0x2800 then return " " end
|
||||
return utf8.char(codePoint)
|
||||
end
|
||||
|
||||
function raster.update()
|
||||
if renderBuffer~=nil then
|
||||
gpu.setActiveBuffer(renderBuffer)
|
||||
end
|
||||
for y=1,raster.displayHeight,4 do
|
||||
-- gpu.set(0,0,tostring(y))
|
||||
for x=1,raster.displayWidth,2 do
|
||||
local ci = math.floor(x/2)+math.floor(y/4)*raster.charWidth+1
|
||||
if chunksAffected[ci] then
|
||||
local chunk = {
|
||||
raster.get(x,y),
|
||||
raster.get(x,y+1),
|
||||
raster.get(x,y+2),
|
||||
raster.get(x+1,y),
|
||||
raster.get(x+1,y+1),
|
||||
raster.get(x+1,y+2),
|
||||
raster.get(x,y+3),
|
||||
raster.get(x+1,y+3)
|
||||
}
|
||||
local colorA = nil
|
||||
local colorB = nil
|
||||
chunk,colorA,colorB = limitTwoColors(chunk)
|
||||
-- print(tostring(colorA)..","..tostring(colorB))
|
||||
cx,cy=raster.units.brailleToChar(x,y)
|
||||
gpu.setBackground(colorA)
|
||||
gpu.setForeground(colorB)
|
||||
-- gpu.set(cx,cy,tostring(colorB/0xFFFFFF))
|
||||
gpu.set(cx,cy,arrayToBraille(chunk))
|
||||
chunksAffected[ci] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if renderBuffer~=nil then
|
||||
gpu.bitblt()
|
||||
gpu.setActiveBuffer(0)
|
||||
end
|
||||
end
|
||||
|
||||
function raster.clear()
|
||||
if renderBuffer~=nil then
|
||||
gpu.setActiveBuffer(renderBuffer)
|
||||
end
|
||||
-- clear()
|
||||
local bgcolor = raster.backgroundColor
|
||||
gpu.setBackground(bgcolor)
|
||||
gpu.fill(1,1,raster.displayWidth,raster.displayHeight," ")
|
||||
display = {}
|
||||
end
|
||||
|
||||
function raster.free()
|
||||
if renderBuffer==nil then
|
||||
return true
|
||||
else
|
||||
return gpu.freeBuffer(renderBuffer)
|
||||
end
|
||||
end
|
||||
|
||||
-- advanced rendering
|
||||
|
||||
function raster.drawLine(x1, y1, x2, y2, color)
|
||||
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
|
||||
|
||||
local dx = math.abs(x2 - x1)
|
||||
local dy = math.abs(y2 - y1)
|
||||
|
||||
local sx = x1 < x2 and 1 or -1
|
||||
local sy = y1 < y2 and 1 or -1
|
||||
|
||||
local err = dx - dy
|
||||
|
||||
while true do
|
||||
raster.set(x1, y1, color)
|
||||
|
||||
if x1 == x2 and y1 == y2 then
|
||||
break
|
||||
end
|
||||
|
||||
local e2 = 2 * err
|
||||
|
||||
if e2 > -dy then
|
||||
err = err - dy
|
||||
x1 = x1 + sx
|
||||
end
|
||||
|
||||
if e2 < dx then
|
||||
err = err + dx
|
||||
y1 = y1 + sy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function raster.drawRect(x1,y1,x2,y2,col)
|
||||
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
|
||||
if x1 > x2 then x1, x2 = x2, x1 end
|
||||
if y1 > y2 then y1, y2 = y2, y1 end
|
||||
for x=x1,x2 do
|
||||
raster.set(x,y1,col)
|
||||
raster.set(x,y2,col)
|
||||
end
|
||||
for y=y1+1,y2-1 do
|
||||
raster.set(x1,y,col)
|
||||
raster.set(x2,y,col)
|
||||
end
|
||||
end
|
||||
|
||||
function raster.fillRect(x1,y1,x2,y2,col)
|
||||
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
|
||||
if x1 > x2 then x1, x2 = x2, x1 end
|
||||
if y1 > y2 then y1, y2 = y2, y1 end
|
||||
for x=x1,x2 do
|
||||
for y=y1,y2 do
|
||||
raster.set(x,y,col)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function raster.drawCircle(xc, yc, radius, color)
|
||||
xc=math.floor(xc)
|
||||
yc=math.floor(yc)
|
||||
radius=math.floor(radius)
|
||||
local x = 0
|
||||
local y = radius
|
||||
local d = 3 - 2 * radius
|
||||
|
||||
while y >= x do
|
||||
-- Draw 8 symmetric points
|
||||
raster.set(xc + x, yc + y, color)
|
||||
raster.set(xc - x, yc + y, color)
|
||||
raster.set(xc + x, yc - y, color)
|
||||
raster.set(xc - x, yc - y, color)
|
||||
raster.set(xc + y, yc + x, color)
|
||||
raster.set(xc - y, yc + x, color)
|
||||
raster.set(xc + y, yc - x, color)
|
||||
raster.set(xc - y, yc - x, color)
|
||||
|
||||
if d < 0 then
|
||||
d = d + 4 * x + 6
|
||||
else
|
||||
d = d + 4 * (x - y) + 10
|
||||
y = y - 1
|
||||
end
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
function raster.drawEllipse(x1, y1, x2, y2, color)
|
||||
if x1 > x2 then x1, x2 = x2, x1 end
|
||||
if y1 > y2 then y1, y2 = y2, y1 end
|
||||
|
||||
local xc = math.floor((x1 + x2) / 2)
|
||||
local yc = math.floor((y1 + y2) / 2)
|
||||
|
||||
local a = math.floor((x2 - x1) / 2)
|
||||
local b = math.floor((y2 - y1) / 2)
|
||||
|
||||
if a <= 0 or b <= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
if a == b then
|
||||
raster.drawCircle(xc, yc, a, color)
|
||||
return
|
||||
end
|
||||
|
||||
if a <= 1 and b <= 1 then
|
||||
raster.set(xc, yc, color)
|
||||
return
|
||||
elseif a <= 1 then
|
||||
for y = yc - b, yc + b do
|
||||
raster.set(xc, y, color)
|
||||
end
|
||||
return
|
||||
elseif b <= 1 then
|
||||
for x = xc - a, xc + a do
|
||||
raster.set(x, yc, color)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local x = 0
|
||||
local y = b
|
||||
local a2 = a * a
|
||||
local b2 = b * b
|
||||
|
||||
local d1 = b2 - (a2 * b) + (0.25 * a2)
|
||||
local dx = 2 * b2 * x
|
||||
local dy = 2 * a2 * y
|
||||
|
||||
while dx < dy do
|
||||
raster.set(xc + x, yc + y, color)
|
||||
raster.set(xc - x, yc + y, color)
|
||||
raster.set(xc + x, yc - y, color)
|
||||
|
||||
if d1 < 0 then
|
||||
x = x + 1
|
||||
dx = dx + (2 * b2)
|
||||
d1 = d1 + dx + b2
|
||||
else
|
||||
x = x + 1
|
||||
y = y - 1
|
||||
dx = dx + (2 * b2)
|
||||
dy = dy - (2 * a2)
|
||||
d1 = d1 + dx - dy + b2
|
||||
end
|
||||
end
|
||||
|
||||
local d2 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2
|
||||
|
||||
while y >= 0 do
|
||||
raster.set(xc + x, yc + y, color)
|
||||
raster.set(xc - x, yc + y, color)
|
||||
raster.set(xc + x, yc - y, color)
|
||||
raster.set(xc - x, yc - y, color)
|
||||
|
||||
if d2 > 0 then
|
||||
y = y - 1
|
||||
dy = dy - (2 * a2)
|
||||
d2 = d2 - dy + a2
|
||||
else
|
||||
y = y - 1
|
||||
x = x + 1
|
||||
dx = dx + (2 * b2)
|
||||
dy = dy - (2 * a2)
|
||||
d2 = d2 + dx - dy + a2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function raster.fillCircle(x, y, r, color)
|
||||
x, y = math.floor(x + 0.5), math.floor(y + 0.5)
|
||||
r = math.floor(r + 0.5)
|
||||
|
||||
if r <= 0 then return end
|
||||
|
||||
local minX, maxX = x - r, x + r
|
||||
local minY, maxY = y - r, y + r
|
||||
|
||||
for py = minY, maxY do
|
||||
for px = minX, maxX do
|
||||
local dx, dy = px - x, py - y
|
||||
local distSquared = dx*dx + dy*dy
|
||||
|
||||
if distSquared <= r*r then
|
||||
raster.set(px, py, color)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function raster.fillEllipse(x1, y1, x2, y2, color)
|
||||
local centerX = (x1 + x2) / 2
|
||||
local centerY = (y1 + y2) / 2
|
||||
|
||||
local a = math.abs(x2 - x1) / 2
|
||||
local b = math.abs(y2 - y1) / 2
|
||||
|
||||
centerX = math.floor(centerX + 0.5)
|
||||
centerY = math.floor(centerY + 0.5)
|
||||
a = math.floor(a + 0.5)
|
||||
b = math.floor(b + 0.5)
|
||||
|
||||
if a <= 0 or b <= 0 then return end
|
||||
|
||||
if a == b then
|
||||
raster.fillCircle(centerX, centerY, a, color)
|
||||
return
|
||||
end
|
||||
|
||||
local minX = centerX - a
|
||||
local maxX = centerX + a
|
||||
local minY = centerY - b
|
||||
local maxY = centerY + b
|
||||
|
||||
for y = minY, maxY do
|
||||
for x = minX, maxX do
|
||||
local dx = x - centerX
|
||||
local dy = y - centerY
|
||||
local value = (dx*dx)/(a*a) + (dy*dy)/(b*b)
|
||||
|
||||
if value <= 1 then
|
||||
raster.set(x, y, color)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return raster
|
||||
@@ -1,125 +0,0 @@
|
||||
local serialize = {}
|
||||
|
||||
function serialize.string(str)
|
||||
return '"'..str:gsub("[%z\1-\31\34\92\127-\159]",function(c)
|
||||
local byte = c:byte()
|
||||
if byte== 7 then return "\\a" end
|
||||
if byte== 8 then return "\\b" end
|
||||
if byte== 9 then return "\\t" end
|
||||
if byte==10 then return "\\n" end
|
||||
if byte==11 then return "\\v" end
|
||||
if byte==12 then return "\\f" end
|
||||
if byte==13 then return "\\r" end
|
||||
if byte==34 then return "\\\"" end
|
||||
if byte==92 then return "\\\\" end
|
||||
return string.format("\\x%02x",byte)
|
||||
end)..'"'
|
||||
end
|
||||
|
||||
function serialize.table(tbl,colors,stack)
|
||||
stack = table.copy(stack or {})
|
||||
table.insert(stack,tbl)
|
||||
local keyAmount = 0
|
||||
local keyNumber = true
|
||||
local out = ""
|
||||
local first = true
|
||||
|
||||
for key,val in pairs(tbl) do
|
||||
if not first then out=out..",\n" end
|
||||
first=false
|
||||
out=out.." "
|
||||
if type(key)=="string" then
|
||||
if key:match("^[%a_][%w_]*$") then
|
||||
out=out..key.."="
|
||||
else
|
||||
out=out..'['..serialize.string(key)..']='
|
||||
end
|
||||
else
|
||||
out=out.."["..tostring(key).."]="
|
||||
end
|
||||
if type(key)~="number" then
|
||||
keyNumber=false
|
||||
end
|
||||
|
||||
local success,reason = pcall(function()
|
||||
local valStr = ""
|
||||
if type(val)=="table" then
|
||||
if #stack>4 or table.find(stack,val) then
|
||||
valStr="..."
|
||||
else
|
||||
valStr=serialize.table(val,colors,stack)
|
||||
end
|
||||
elseif type(val)=="string" then
|
||||
local lines = {}
|
||||
for line in (val.."\n"):gmatch("([^\n]*)\n") do table.insert(lines,line) end
|
||||
if #lines[#lines]==0 then
|
||||
lines[#lines]=nil
|
||||
lines[#lines]=lines[#lines].."\n"
|
||||
end
|
||||
for i=1,#lines do
|
||||
if i<#lines then
|
||||
lines[i]=serialize.string(lines[i].."\n")
|
||||
else
|
||||
lines[i]=serialize.string(lines[i])
|
||||
end
|
||||
end
|
||||
valStr=table.concat(lines," ..\n ")
|
||||
else
|
||||
valStr=tostring(val)
|
||||
end
|
||||
local lines = {}
|
||||
for line in (valStr.."\n"):gmatch("([^\n]*)\n") do table.insert(lines,line) end
|
||||
out=out..table.concat(lines,"\n ")
|
||||
lines = nil
|
||||
keyAmount=keyAmount+1
|
||||
end)
|
||||
if not success then
|
||||
if colors then out=out.."\x1b[91m" end
|
||||
out=out.."["..tostring(reason).."]"
|
||||
if colors then out=out.."\x1b[39m" end
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
local metatbl = getmetatable(tbl)
|
||||
local metakeys = {}
|
||||
if type(metatbl)=="table" then
|
||||
for i,v in pairs(metatbl) do
|
||||
keyNumber=false
|
||||
table.insert(metakeys,i)
|
||||
end
|
||||
end
|
||||
if #metakeys>0 then
|
||||
out=out.."\n "
|
||||
if colors then out=out.."\x1b[92m" end
|
||||
if table.find(metakeys,"__tostring") then
|
||||
out=out.."tostring: "..serialize.string(tostring(tbl)).."\n "
|
||||
table.remove(metakeys,table.find(metakeys,"__tostring"))
|
||||
end
|
||||
out=out..table.concat(metakeys,", ")
|
||||
if colors then out=out.."\x1b[39m" end
|
||||
end
|
||||
|
||||
if keyAmount==0 then return "{}" end
|
||||
if keyNumber then
|
||||
-- fix strings not being serialised
|
||||
local vals = {}
|
||||
for _,v in pairs(tbl) do
|
||||
if #vals>=5 and #stack>1 then
|
||||
table.insert(vals,"...")
|
||||
break
|
||||
end
|
||||
if type(v)=="table" then
|
||||
table.insert(vals,serialize.table(v,colors,stack))
|
||||
elseif type(v)=="string" then
|
||||
table.insert(vals,serialize.string(v))
|
||||
else
|
||||
table.insert(vals,tostring(v))
|
||||
end
|
||||
end
|
||||
return "{"..table.concat(vals,", ").."}"
|
||||
end
|
||||
return "{\n"..out.."\n}"
|
||||
end
|
||||
|
||||
return serialize
|
||||
@@ -1,102 +0,0 @@
|
||||
local unicodeLib
|
||||
local LLunicode
|
||||
if table.copy then
|
||||
unicodeLib = table.copy(unicode)
|
||||
LLunicode = table.copy(unicode)
|
||||
else
|
||||
unicodeLib = {}
|
||||
LLunicode = unicode
|
||||
end
|
||||
|
||||
function unicodeLib.readCodePoint(readByte)
|
||||
checkArg(1,readByte,"function")
|
||||
|
||||
local function inRange(min,max,...)
|
||||
for _,v in ipairs({...}) do
|
||||
if not (v and v>=min and v<max) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local byte = readByte()
|
||||
if byte==nil then return end
|
||||
|
||||
if byte < 0x80 then
|
||||
-- ASCII character (0xxxxxxx)
|
||||
return byte
|
||||
elseif byte < 0xC0 then
|
||||
-- Continuation byte (10xxxxxx), invalid at start position
|
||||
return nil
|
||||
elseif byte < 0xE0 then
|
||||
-- 2-byte sequence (110xxxxx 10xxxxxx)
|
||||
local byte2 = readByte()
|
||||
if byte2==nil then return nil end
|
||||
if inRange(0x80,0xC0,byte2) then
|
||||
local code_point = ((byte & 0x1F) << 6) | (byte2 & 0x3F)
|
||||
return code_point
|
||||
end
|
||||
elseif byte < 0xF0 then
|
||||
-- 3-byte sequence (1110xxxx 10xxxxxx 10xxxxxx)
|
||||
local byte2, byte3 = readByte(), readByte()
|
||||
if byte2==nil and byte3==nil then return nil end
|
||||
if inRange(0x80,0xC0,byte2,byte3)then
|
||||
local code_point = ((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F)
|
||||
return code_point
|
||||
end
|
||||
elseif byte < 0xF8 then
|
||||
-- 4-byte sequence (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
|
||||
local byte2, byte3, byte4 = readByte(), readByte(), readByte()
|
||||
if byte2==nil and byte3==nil and byte4==nil then return nil end
|
||||
if inRange(0x80,0xC0,byte2,byte3,byte4) then
|
||||
local code_point = ((byte & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F)
|
||||
return code_point
|
||||
end
|
||||
end
|
||||
|
||||
-- Invalid UTF-8 byte sequence
|
||||
return nil
|
||||
end
|
||||
|
||||
function unicodeLib.readChar(readByte)
|
||||
checkArg(1,readByte,"function")
|
||||
return LLunicode.char(unicodeLib.readCodePoint(readByte))
|
||||
end
|
||||
|
||||
function unicodeLib.codepoint(chr)
|
||||
checkArg(1,chr,"string")
|
||||
local ptr = 1
|
||||
return unicode.readCodePoint(function()
|
||||
local byte = chr:byte(ptr)
|
||||
ptr=ptr+1
|
||||
return byte
|
||||
end),ptr-1
|
||||
end
|
||||
|
||||
function unicodeLib.iterate(readByte)
|
||||
checkArg(1,readByte,"string","function")
|
||||
if type(readByte)=="string" then
|
||||
local str,ptr = readByte,0
|
||||
readByte = function()
|
||||
ptr=ptr+1
|
||||
return str:byte(ptr)
|
||||
end
|
||||
end
|
||||
return function()
|
||||
local point = unicodeLib.readCodePoint(readByte)
|
||||
if point==nil then return nil end
|
||||
return LLunicode.char(point),point
|
||||
end
|
||||
end
|
||||
|
||||
unicodeLib.char = LLunicode.char
|
||||
unicodeLib.charWidth = LLunicode.charWidth
|
||||
unicodeLib.isWide = LLunicode.isWide
|
||||
unicodeLib.len = LLunicode.len
|
||||
unicodeLib.lower = LLunicode.lower
|
||||
unicodeLib.reverse = LLunicode.reverse
|
||||
unicodeLib.sub = LLunicode.sub
|
||||
unicodeLib.upper = LLunicode.upper
|
||||
unicodeLib.wlen = LLunicode.wlen
|
||||
unicodeLib.wtrunc = LLunicode.wtrunc
|
||||
|
||||
return unicodeLib
|
||||
@@ -1,5 +1,5 @@
|
||||
local fs = import("filesystem")
|
||||
local json = import("json")
|
||||
local fs = require("filesystem")
|
||||
local json = require("json")
|
||||
local handle, data, tmpdata = fs.open("/halyde/config/shell.json", "r"), "", nil
|
||||
repeat
|
||||
tmpdata = handle:read(math.huge)
|
||||
@@ -7,23 +7,21 @@ repeat
|
||||
until not tmpdata
|
||||
handle:close()
|
||||
local shellcfg = json.decode(data)
|
||||
import("/halyde/core/termlib.lua")
|
||||
local event = import("event")
|
||||
local component = import("component")
|
||||
local component = require("component")
|
||||
local gpu = component.gpu
|
||||
|
||||
_G.shell = {}
|
||||
_G.shell.workingDirectory = shellcfg["defaultWorkingDirectory"]
|
||||
_G.shell.aliases = shellcfg["aliases"]
|
||||
|
||||
local function runAsCoroutine(path, ...)
|
||||
local function runAsTask(path, ...)
|
||||
--ocelot.log("running " .. path .. " as coroutine")
|
||||
cormgr.loadCoroutine(path, ...)
|
||||
local corIndex = #cormgr.corList
|
||||
local cor = cormgr.corList[#cormgr.corList]
|
||||
tsched.runAsTask(path, ...)
|
||||
local corIndex = #tsched.getTasks()
|
||||
local task = tsched.getTasks()[#tsched.getTasks()]
|
||||
repeat
|
||||
coroutine.yield()
|
||||
until cormgr.corList[corIndex] ~= cor
|
||||
until tsched.getTasks()[corIndex] ~= task
|
||||
end
|
||||
|
||||
function _G.shell.run(command)
|
||||
@@ -66,14 +64,14 @@ function _G.shell.run(command)
|
||||
if fs.exists(args[1]) and not fs.isDirectory(args[1]) then
|
||||
local path = args[1]
|
||||
table.remove(args, 1)
|
||||
runAsCoroutine(path, table.unpack(args))
|
||||
runAsTask(path, table.unpack(args))
|
||||
return
|
||||
end
|
||||
for _, item in pairs(PATH) do
|
||||
if fs.exists(item..args[1]) and not fs.isDirectory(item .. args[1]) then
|
||||
local path = fs.concat(item, args[1])
|
||||
table.remove(args, 1)
|
||||
runAsCoroutine(path, table.unpack(args))
|
||||
runAsTask(path, table.unpack(args))
|
||||
return
|
||||
else -- try to look for it without the file extension
|
||||
local files = fs.list(item) or {}
|
||||
@@ -81,7 +79,7 @@ function _G.shell.run(command)
|
||||
-- previous pattern: (.+)%.[^%.]+$
|
||||
if args[1] == file:match("(.+)%.[^%.]+$") and not fs.isDirectory(item .. file) then
|
||||
table.remove(args, 1)
|
||||
runAsCoroutine(item .. file, table.unpack(args))
|
||||
runAsTask(item .. file, table.unpack(args))
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -100,7 +98,7 @@ while true do
|
||||
if shell.workingDirectory:sub(-1, -1) ~= "/" then
|
||||
shell.workingDirectory = shell.workingDirectory .. "/"
|
||||
end
|
||||
local shellCommand = read("shell", shellcfg.prompt:format(shell.workingDirectory))
|
||||
local shellCommand = terminal.read("shell", shellcfg.prompt:format(shell.workingDirectory))
|
||||
shell.run(shellCommand)
|
||||
gpu.freeAllBuffers()
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
[37m[00:00:04:12] Debug!
|
||||
[97m[00:00:04:19] Info!
|
||||
[93m[00:00:04:25] Warning!
|
||||
[91m[00:00:04:32] Error!
|
||||
Reference in New Issue
Block a user