ALPHA 3.0.0 - Added Inter-Process Communication and fixed the bug where the shell would continue taking in input when a command was still running.

This commit is contained in:
WahPlus
2025-08-21 21:03:17 +03:00
parent 5276d2437b
commit ef0ffa1886
10 changed files with 208 additions and 73 deletions
+1
View File
@@ -0,0 +1 @@
.stfolder
+3 -3
View File
@@ -1,10 +1,10 @@
print("\27[44m".._VERSION.."\27[0m shell") print("\27[44m".._VERSION.."\27[0m shell")
print('Type "exit" to exit.') print('Type "exit" to exit.')
termlib.readHistory["lua"] = {""} terminal.readHistory["lua"] = {""}
local fs = require("filesystem") local fs = require("filesystem")
local loadedLibraries = "" local loadedLibraries = ""
local libList = fs.list("halyde/lib") local libList = fs.list("/lib/")
for _, lib in pairs(libList) do for _, lib in pairs(libList) do
if lib:match("(.+)%.lua") then if lib:match("(.+)%.lua") then
loadedLibraries = loadedLibraries .. "local " .. lib:match("(.+)%.lua") .. ' = require("' .. lib:match("(.+)%.lua") .. '")\n' loadedLibraries = loadedLibraries .. "local " .. lib:match("(.+)%.lua") .. ' = require("' .. lib:match("(.+)%.lua") .. '")\n'
@@ -12,7 +12,7 @@ for _, lib in pairs(libList) do
end end
while true do while true do
local command = read("lua", "\27[44mlua>\27[0m ") local command = terminal.read("lua", "\27[44mlua>\27[0m ")
if command == "exit" then if command == "exit" then
return return
else else
+1 -3
View File
@@ -3,7 +3,6 @@ local filesystem = assert(loadfile("/lib/filesystem.lua")(loadfile))
_G._OSVERSION = "HALYDE VERSION" -- TODO: Put this in a separate config file _G._OSVERSION = "HALYDE VERSION" -- TODO: Put this in a separate config file
_G._OSLOGO = "" _G._OSLOGO = ""
_G._PUBLIC = {} _G._PUBLIC = {}
local ocelot = component.proxy(component.list("ocelot")())
local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil
repeat repeat
@@ -16,7 +15,6 @@ _G.package = {["preloaded"] = {}}
loadfile("/halyde/kernel/modules/datatools.lua")() loadfile("/halyde/kernel/modules/datatools.lua")()
function _G.require(module, ...) function _G.require(module, ...)
ocelot.log("Requiring " .. module)
local args = table.pack(...) local args = table.pack(...)
if package.preloaded[module] then if package.preloaded[module] then
return package.preloaded[module] return package.preloaded[module]
@@ -50,7 +48,7 @@ function _G.package.preload(module)
_G[module] = nil _G[module] = nil
end 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/datatools.lua") -- If this is not imported BEFORE modload gets run, modload requires filesystem which requires computer which requires datatools. TODO: When VFS is implemented, make the pre-VFS loading of filesystem load a more basic version. And remove this.
require("/halyde/kernel/modload.lua") require("/halyde/kernel/modload.lua")
package.preload("component") package.preload("component")
+1 -1
View File
@@ -30,7 +30,7 @@ while true do
if print then if print then
print("\n\27[91mCoroutine "..tostring(#tsched.tasks).." killed.") print("\n\27[91mCoroutine "..tostring(#tsched.tasks).." killed.")
end end
tsched.tasks[#tsched.tasks] = nil table.remove(tsched.tasks, #tsched.tasks)
end end
elseif args[2] == "key_up" then elseif args[2] == "key_up" then
local keycode = args[5] local keycode = args[5]
+3 -8
View File
@@ -1,19 +1,16 @@
local fs = require("filesystem") local fs = require("filesystem")
local ocelot = require("component").ocelot
local modulePath = "/halyde/kernel/modules" local modulePath = "/halyde/kernel/modules"
local modules = fs.list(modulePath) local modules = assert(fs.list(modulePath))
local moduleTypes = {} local moduleTypes = {}
local function loadModule(modName) local function loadModule(modName)
ocelot.log("Checking module " .. modName) local moduleData = assert(require(fs.concat(modulePath, modName)), "Module did not return anything!") -- TODO: Make this not actually throw an error, rather put something in the log and move on
local moduleData = require(fs.concat(modulePath, modName))
table.remove(modules, table.find(modules, modName)) table.remove(modules, table.find(modules, modName))
if not moduleData.check() then if not moduleData.check() then
return return
end end
ocelot.log("Loading module " .. modName)
if moduleData.dependencies then if moduleData.dependencies then
for _, dependency in pairs(moduleData.dependencies) do for _, dependency in pairs(moduleData.dependencies) do
if table.find(modules, dependency) then if table.find(modules, dependency) then
@@ -37,7 +34,7 @@ local function loadModule(modName)
end end
for _, modName in pairs(modules) do -- Get all the module types for _, modName in pairs(modules) do -- Get all the module types
local moduleData = require(fs.concat(modulePath, modName)) local moduleData = assert(require(fs.concat(modulePath, modName)), "Module did not return anything!") -- TODO: Make this not actually throw an error, rather put something in the log and move on
if moduleData.type then if moduleData.type then
--print(moduleData.type) --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 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
@@ -49,5 +46,3 @@ while modules[1] do
loadModule(modules[1]) loadModule(modules[1])
end end
end end
ocelot.log("Finished loading modules!")
+112 -2
View File
@@ -1,15 +1,125 @@
local module = {} local module = {}
function module.check() function module.check()
return true -- This module should always be loaded return true -- IPC should always be loaded
end end
function module.init() function module.init()
_G.ipc = {}
_G.ipc.shared = {}
_PUBLIC.ipc = {}
function _PUBLIC.ipc.shareWithAll()
local shareTable = {}
setmetatable(shareTable, {["__newindex"] = function(_, key, value)
local currentPID = _PUBLIC.tsched.getCurrentTask().id
if not _G.ipc.shared[currentPID] then
_G.ipc.shared[currentPID] = {} -- TODO: Add some kind of cleanup routine since these IPC shares can just keep piling up
end
local globalTable
for _, tab in pairs(_G.ipc.shared[currentPID]) do
if tab.sharedWith == "all" then
globalTable = tab
end
end
if not globalTable then
globalTable = {["sharedWith"] = "all"}
table.insert(_G.ipc.shared[currentPID], globalTable)
end
if not globalTable.vars then
globalTable.vars = {}
end
globalTable.vars[key] = value
end, ["__index"] = function(_, key)
local currentPID = _PUBLIC.tsched.getCurrentTask().id
if not _G.ipc.shared[currentPID] then
return nil
end
local globalTable
for _, tab in pairs(_G.ipc.shared[currentPID]) do
if tab.sharedWith == "all" then
globalTable = tab
end
end
if not globalTable then
return nil
end
if not globalTable.vars then
return nil
end
return globalTable.vars[key]
end})
return shareTable
end
function _PUBLIC.ipc.shareWith(pid)
checkArg(1, pid, "number")
local shareTable = {}
setmetatable(shareTable, {["__newindex"] = function(_, key, value)
local currentPID = _PUBLIC.tsched.getCurrentTask().id
if not _G.ipc.shared[currentPID] then
_G.ipc.shared[currentPID] = {} -- TODO: Add some kind of cleanup routine since these IPC shares can just keep piling up
end
local globalTable
for _, tab in pairs(_G.ipc.shared[currentPID]) do
if tab.sharedWith == "all" then
globalTable = tab
end
end
if not globalTable then
globalTable = {["sharedWith"] = pid}
table.insert(_G.ipc.shared[currentPID], globalTable)
end
if not globalTable.vars then
globalTable.vars = {}
end
globalTable.vars[key] = value
end, ["__index"] = function(_, key)
local currentPID = _PUBLIC.tsched.getCurrentTask().id
if not _G.ipc.shared[currentPID] then
return nil
end
local globalTable
for _, tab in pairs(_G.ipc.shared[currentPID]) do
if tab.sharedWith == pid then
globalTable = tab
end
end
if not globalTable then
return nil
end
if not globalTable.vars then
return nil
end
return globalTable.vars[key]
end})
return shareTable
end
_PUBLIC.ipc.shared = {}
setmetatable(_PUBLIC.ipc.shared, {["__index"] = function(_, pid)
local currentPID = _PUBLIC.tsched.getCurrentTask().id
local returnTable = {}
for _, shareTable in pairs(ipc.shared[pid] or {}) do
if shareTable.sharedWith == currentPID then
for key, value in pairs(shareTable.vars) do
returnTable[key] = table.copy(value)
end
elseif shareTable.sharedWith == "all" then
for key, value in pairs(shareTable.vars) do
if not returnTable[key] then
returnTable[key] = table.copy(value)
end
end
end
end
return returnTable
end})
end end
function module.exit() function module.exit()
_G.ipc = nil
_PUBLIC.ipc = nil
end end
return module return module
+12 -6
View File
@@ -1,3 +1,5 @@
local idCounter = 1
_G._PUBLIC.tsched = {} _G._PUBLIC.tsched = {}
_G.tsched = {} _G.tsched = {}
_G.tsched.tasks = {} _G.tsched.tasks = {}
@@ -9,8 +11,6 @@ local computer = require("computer")
local filesystem = require("filesystem") local filesystem = require("filesystem")
local json = require("json") local json = require("json")
local gpu = component.gpu local gpu = component.gpu
local ocelot = component.ocelot
function _G._PUBLIC.tsched.runAsTask(path,...) function _G._PUBLIC.tsched.runAsTask(path,...)
local args = {...} local args = {...}
local function taskFunction() local function taskFunction()
@@ -48,15 +48,21 @@ function _G._PUBLIC.tsched.runAsTask(path,...)
end end
function _G._PUBLIC.tsched.addTask(func, name) function _G._PUBLIC.tsched.addTask(func, name)
ocelot.log("Added task " .. name)
local task = coroutine.create(func) local task = coroutine.create(func)
table.insert(tsched.tasks, {["task"] = task, ["name"] = name}) table.insert(tsched.tasks, {["task"] = task, ["name"] = name, ["id"] = idCounter})
idCounter = idCounter + 1
return task return task
end end
function _G._PUBLIC.tsched.removeTask(id) function _G._PUBLIC.tsched.removeTask(id)
-- TODO: Check for user permissions before running -- TODO: Check for user permissions before running
table.remove(_G.tsched.tasks, id) for index, task in pairs(tsched.tasks) do
if task.id == id then
table.remove(tsched.tasks, index)
return true
end
end
return false
end end
function handleError(errormsg) function handleError(errormsg)
@@ -76,7 +82,7 @@ local function runTasks()
handleError(errorMessage) handleError(errorMessage)
end end
if coroutine.status(tsched.tasks[i].task) == "dead" then if coroutine.status(tsched.tasks[i].task) == "dead" then
_PUBLIC.tsched.removeTask(i) _PUBLIC.tsched.removeTask(tsched.tasks[i].id)
--ocelot.log("Removed coroutine") --ocelot.log("Removed coroutine")
i = i - 1 i = i - 1
end end
+14 -4
View File
@@ -17,11 +17,21 @@ _G.shell.aliases = shellcfg["aliases"]
local function runAsTask(path, ...) local function runAsTask(path, ...)
--ocelot.log("running " .. path .. " as coroutine") --ocelot.log("running " .. path .. " as coroutine")
tsched.runAsTask(path, ...) tsched.runAsTask(path, ...)
local corIndex = #tsched.getTasks() local pid = tsched.getTasks()[#tsched.getTasks()].id
local task = tsched.getTasks()[#tsched.getTasks()] while true do
repeat local foundTask = false
for _, task in pairs(tsched.getTasks()) do
if task.id == pid then
foundTask = true
break
end
end
if foundTask then
coroutine.yield() coroutine.yield()
until tsched.getTasks()[corIndex] ~= task else
break
end
end
end end
function _G.shell.run(command) function _G.shell.run(command)
+6 -7
View File
@@ -1,7 +1,6 @@
local cor = coroutine.create(function() local pid = tsched.getCurrentTask().id
print("Hello World!") local shareTable = ipc.shareWith(pid)
end) shareTable.gabbagool = "Pigeon Pizza! Wow!"
print(shareTable.gabbagool)
print(coroutine.status(cor)) print(pid)
coroutine.resume(cor) print(ipc.shared[pid].gabbagool)
print(coroutine.status(cor))
+51 -35
View File
@@ -1,25 +1,6 @@
local gpu = component.proxy(component.list("gpu")()) local gpu = component.proxy(component.list("gpu")())
local resX, resY = gpu.getResolution() local resX, resY = gpu.getResolution()
-- Architecture check
local foundArchitecture = false
for _, arch in pairs(computer.getArchitectures()) do
if arch == "Lua 5.3" then
foundArchitecture = true
break
end
end
if foundArchitecture then
computer.setArchitecture("Lua 5.3")
else
gpu.set(1, 1, "Required architecture (Lua 5.3) is not supported.")
gpu.set(1, 2, "Halting.")
while true do
computer.pullSignal()
end
end
local function loadfile(file) local function loadfile(file)
checkArg(1, file, "string") checkArg(1, file, "string")
local handle = component.invoke(computer.getBootAddress(), "open", file, "r") local handle = component.invoke(computer.getBootAddress(), "open", file, "r")
@@ -37,6 +18,26 @@ local function handleError(errorMessage)
end end
function loadBoot() function loadBoot()
local foundArchitecture = false
for _, arch in pairs(computer.getArchitectures()) do
if arch == "Lua 5.3" then
foundArchitecture = true
break
end
end
if foundArchitecture then
local _, errorMesage = computer.setArchitecture("Lua 5.3")
if errorMessage then
error(errorMessage)
end
else
gpu.set(1, 1, "Required architecture (Lua 5.3) is not supported.")
gpu.set(1, 2, "Halting.")
while true do
computer.pullSignal()
end
end
loadfile("/halyde/kernel/boot.lua")(loadfile) loadfile("/halyde/kernel/boot.lua")(loadfile)
end end
@@ -45,28 +46,43 @@ gpu.fill(1, 1, resX, resY, " ")
-- Copying low-level functions in case of post-preload failure -- Copying low-level functions in case of post-preload failure
local pullSignal = computer.pullSignal local pullSignal = computer.pullSignal
local shutdown = computer.shutdown local beep = computer.beep
local result, reason = xpcall(loadBoot, handleError) local result, reason = xpcall(loadBoot, handleError)
if not result then if not result then
gpu.setBackground(0x000000) local bgColor
if gpu.getDepth() == 1 then
bgColor = 0x000000
else
bgColor = 0x000080
end
gpu.setBackground(bgColor)
gpu.fill(1, 1, resX, resY, " ") gpu.fill(1, 1, resX, resY, " ")
gpu.setBackground(0x800000) local function render()
gpu.setForeground(0xFFFFFF) gpu.setForeground(0xFFFFFF)
gpu.set(2,2,"A critical error has occurred.") local i = 2
local i = 4 reason = "A fatal error has occurred.\nHalyde cannot continue.\n \n" .. tostring(reason or "unknown error"):gsub("\t", " ")
reason = tostring(reason):gsub("\t", " ") for line in string.gmatch(reason, "([^\n]*)\n?") do
for line in string.gmatch(reason or "unknown error", "([^\n]*)\n?") do gpu.set(2, i, line)
gpu.set(2,i,line)
i = i + 1 i = i + 1
end end
gpu.set(2,i+1, "Press any key to restart.") gpu.set(1, resY - 1, string.rep("", resX))
local evname gpu.setForeground(bgColor)
repeat gpu.setBackground(0xFFFFFF)
evname = pullSignal() gpu.set(2, resY, "🠅 🠄 🠇 🠆")
until evname == "key_down" gpu.setForeground(0xFFFFFF)
shutdown(true) gpu.setBackground(bgColor)
while true do gpu.set(4, resY, " / ")
coroutine.yield() gpu.set(9, resY, " / ")
gpu.set(14, resY, " / ")
gpu.set(20, resY, "Scroll" .. string.rep(" ", resX - 21))
end
render()
beep(440, 0.2)
beep(465, 0.2)
beep(440, 0.2)
beep(370, 0.5)
while true do -- TODO: Make this scrollable
pullSignal()
end end
end end