opus/sys/kernel.lua

256 lines
5.9 KiB
Lua
Raw Normal View History

_G.requireInjector()
local Util = require('util')
_G.kernel = {
2018-01-12 01:53:32 +00:00
UID = 0,
hooks = { },
routines = { },
terminal = _G.term.current(),
2018-01-14 04:40:53 +00:00
window = _G.term.current(),
}
2018-01-14 21:44:43 +00:00
local fs = _G.fs
local kernel = _G.kernel
2018-01-12 01:53:32 +00:00
local os = _G.os
2018-01-14 23:28:23 +00:00
local shell = _ENV.shell
2018-01-12 01:53:32 +00:00
local term = _G.term
2018-01-12 01:53:32 +00:00
local focusedRoutineEvents = Util.transpose {
'char', 'key', 'key_up',
'mouse_click', 'mouse_drag', 'mouse_scroll', 'mouse_up',
'paste', 'terminate',
}
2018-01-13 20:17:26 +00:00
_G.debug = function(pattern, ...)
2018-01-14 04:40:53 +00:00
local oldTerm = term.redirect(kernel.window)
2018-01-13 20:17:26 +00:00
Util.print(pattern, ...)
term.redirect(oldTerm)
end
-- any function that runs in a kernel hook does not run in
-- a separate coroutine or have a window. an error in a hook
-- function will crash the system.
function kernel.hook(event, fn)
if type(event) == 'table' then
for _,v in pairs(event) do
kernel.hook(v, fn)
end
else
if not kernel.hooks[event] then
kernel.hooks[event] = { }
end
table.insert(kernel.hooks[event], fn)
end
end
-- you can only unhook from within the function that hooked
function kernel.unhook(event, fn)
local eventHooks = kernel.hooks[event]
if eventHooks then
Util.removeByValue(eventHooks, fn)
if #eventHooks == 0 then
kernel.hooks[event] = nil
end
end
end
2018-01-12 01:53:32 +00:00
local Routine = { }
function Routine:resume(event, ...)
if not self.co or coroutine.status(self.co) == 'dead' then
return
end
if not self.filter or self.filter == event or event == "terminate" then
2018-01-14 04:40:53 +00:00
local previousTerm = term.redirect(self.terminal)
2018-01-12 01:53:32 +00:00
local previous = kernel.running
kernel.running = self -- stupid shell set title
local ok, result = coroutine.resume(self.co, event, ...)
kernel.running = previous
self.terminal = term.current()
2018-01-14 04:40:53 +00:00
term.redirect(previousTerm)
2018-01-12 01:53:32 +00:00
if ok then
self.filter = result
else
_G.printError(result)
end
if coroutine.status(self.co) == 'dead' then
Util.removeByValue(kernel.routines, self)
if #kernel.routines > 0 then
os.queueEvent('kernel_focus', kernel.routines[1].uid)
end
2018-01-14 21:44:43 +00:00
if self.haltOnExit then
kernel.halt()
end
2018-01-12 01:53:32 +00:00
end
return ok, result
end
end
function kernel.getFocused()
return kernel.routines[1]
end
function kernel.getCurrent()
return kernel.running
end
function kernel.newRoutine(args)
kernel.UID = kernel.UID + 1
args = args or { }
local routine = setmetatable(args, { __index = Routine })
routine.uid = kernel.UID
2018-01-14 04:40:53 +00:00
routine.timestamp = os.clock()
2018-01-14 23:28:23 +00:00
routine.env = args.env or Util.shallowCopy(shell.getEnv())
2018-01-14 04:40:53 +00:00
routine.terminal = args.terminal or kernel.terminal
routine.window = args.window or kernel.window
2018-01-12 01:53:32 +00:00
return routine
end
2018-01-14 04:40:53 +00:00
function kernel.launch(routine)
2018-01-12 01:53:32 +00:00
routine.co = routine.co or coroutine.create(function()
local result, err
if routine.fn then
result, err = Util.runFunction(routine.env, routine.fn, table.unpack(routine.args or { } ))
elseif routine.path then
result, err = Util.run(routine.env, routine.path, table.unpack(routine.args or { } ))
else
err = 'kernel: invalid routine'
end
if not result and err and err ~= 'Terminated' then
_G.printError(tostring(err))
end
end)
table.insert(kernel.routines, routine)
local s, m = routine:resume()
2018-01-14 04:40:53 +00:00
return not s and s or routine.uid, m
end
function kernel.run(args)
local routine = kernel.newRoutine(args)
kernel.launch(routine)
return routine
2018-01-12 01:53:32 +00:00
end
function kernel.raise(uid)
local routine = Util.find(kernel.routines, 'uid', uid)
if routine then
local previous = kernel.routines[1]
if routine ~= previous then
Util.removeByValue(kernel.routines, routine)
table.insert(kernel.routines, 1, routine)
end
os.queueEvent('kernel_focus', routine.uid, previous and previous.uid)
return true
end
return false
end
function kernel.lower(uid)
local routine = Util.find(kernel.routines, 'uid', uid)
if routine and #kernel.routines > 1 then
2018-01-13 20:17:26 +00:00
if routine == kernel.routines[1] then
local nextRoutine = kernel.routines[2]
if nextRoutine then
kernel.raise(nextRoutine.uid)
end
end
2018-01-12 01:53:32 +00:00
Util.removeByValue(kernel.routines, routine)
table.insert(kernel.routines, routine)
return true
end
return false
end
function kernel.find(uid)
return Util.find(kernel.routines, 'uid', uid)
end
function kernel.halt()
os.queueEvent('kernel_halt')
end
function kernel.event(event, eventData)
local stopPropagation
local eventHooks = kernel.hooks[event]
if eventHooks then
for i = #eventHooks, 1, -1 do
2018-01-13 20:17:26 +00:00
stopPropagation = eventHooks[i](event, eventData)
2018-01-12 01:53:32 +00:00
if stopPropagation then
break
end
end
end
2018-01-12 01:53:32 +00:00
if not stopPropagation then
if focusedRoutineEvents[event] then
local active = kernel.routines[1]
if active then
active:resume(event, table.unpack(eventData))
end
else
-- Passthrough to all processes
for _,routine in pairs(Util.shallowCopy(kernel.routines)) do
routine:resume(event, table.unpack(eventData))
end
end
end
end
2018-01-14 04:40:53 +00:00
function kernel.start()
2018-01-13 20:17:26 +00:00
local s, m = pcall(function()
repeat
local eventData = { os.pullEventRaw() }
local event = table.remove(eventData, 1)
kernel.event(event, eventData)
until event == 'kernel_halt' or not kernel.routines[1]
end)
kernel.window.setVisible(true)
if not s then
print('\nCrash detected\n')
_G.printError(m)
end
2018-01-14 04:40:53 +00:00
term.redirect(kernel.terminal)
end
2018-01-14 21:44:43 +00:00
2018-01-15 21:28:10 +00:00
local function init(runLevel)
runLevel = tonumber(runLevel) or error('Invalid run level')
2018-01-14 21:44:43 +00:00
local dir = 'sys/extensions'
local files = fs.list(dir)
table.sort(files)
for _,file in ipairs(files) do
local level, name = file:match('(%d).(%S+).lua')
if tonumber(level) <= runLevel then
local s, m = shell.run(fs.combine(dir, file))
if not s then
error(m)
end
end
end
2018-01-15 21:28:10 +00:00
os.queueEvent('kernel_ready')
2018-01-14 21:44:43 +00:00
end
2018-01-15 21:28:10 +00:00
kernel.run({
fn = init,
title = 'init',
args = { ... },
})