mirror of
https://github.com/kepler155c/opus
synced 2024-12-27 00:50:26 +00:00
304 lines
6.3 KiB
Lua
304 lines
6.3 KiB
Lua
_G.requireInjector(_ENV)
|
|
|
|
local Terminal = require('terminal')
|
|
local Util = require('util')
|
|
|
|
_G.kernel = {
|
|
UID = 0,
|
|
hooks = { },
|
|
routines = { },
|
|
}
|
|
|
|
local fs = _G.fs
|
|
local kernel = _G.kernel
|
|
local os = _G.os
|
|
local shell = _ENV.shell
|
|
local term = _G.term
|
|
local window = _G.window
|
|
|
|
local w, h = term.getSize()
|
|
kernel.terminal = term.current()
|
|
kernel.window = window.create(kernel.terminal, 1, 1, w, h, false)
|
|
|
|
Terminal.scrollable(kernel.window)
|
|
|
|
local focusedRoutineEvents = Util.transpose {
|
|
'char', 'key', 'key_up',
|
|
'mouse_click', 'mouse_drag', 'mouse_scroll', 'mouse_up',
|
|
'paste', 'terminate',
|
|
}
|
|
|
|
_G._debug = function(pattern, ...)
|
|
local oldTerm = term.redirect(kernel.window)
|
|
Util.print(pattern, ...)
|
|
term.redirect(oldTerm)
|
|
end
|
|
|
|
if not _G.debug then -- don't clobber lua debugger
|
|
function _G.debug(...)
|
|
_G._debug(...)
|
|
end
|
|
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
|
|
|
|
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
|
|
local previousTerm = term.redirect(self.terminal)
|
|
|
|
local previous = kernel.running
|
|
kernel.running = self -- stupid shell set title
|
|
local ok, result = coroutine.resume(self.co, event, ...)
|
|
kernel.running = previous
|
|
|
|
if ok then
|
|
self.filter = result
|
|
else
|
|
_G.printError(result)
|
|
end
|
|
|
|
self.terminal = term.current()
|
|
term.redirect(previousTerm)
|
|
|
|
if not ok and self.haltOnError then
|
|
error(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
|
|
if self.haltOnExit then
|
|
kernel.halt()
|
|
end
|
|
end
|
|
return ok, result
|
|
end
|
|
end
|
|
|
|
function kernel.getFocused()
|
|
return kernel.routines[1]
|
|
end
|
|
|
|
function kernel.getCurrent()
|
|
return kernel.running
|
|
end
|
|
|
|
function kernel.getShell()
|
|
return shell
|
|
end
|
|
|
|
function kernel.newRoutine(args)
|
|
kernel.UID = kernel.UID + 1
|
|
|
|
local routine = setmetatable({
|
|
uid = kernel.UID,
|
|
timestamp = os.clock(),
|
|
terminal = kernel.window,
|
|
window = kernel.window,
|
|
}, { __index = Routine })
|
|
|
|
Util.merge(routine, args)
|
|
routine.env = args.env or Util.shallowCopy(shell.getEnv())
|
|
|
|
return routine
|
|
end
|
|
|
|
function kernel.launch(routine)
|
|
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 ~= 'Terminated' then
|
|
error(err or 'Error occurred', 2)
|
|
end
|
|
end)
|
|
|
|
table.insert(kernel.routines, routine)
|
|
|
|
local s, m = routine:resume()
|
|
|
|
return not s and s or routine.uid, m
|
|
end
|
|
|
|
function kernel.run(args)
|
|
local routine = kernel.newRoutine(args)
|
|
kernel.launch(routine)
|
|
return routine
|
|
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
|
|
if routine == kernel.routines[1] then
|
|
local nextRoutine = kernel.routines[2]
|
|
if nextRoutine then
|
|
kernel.raise(nextRoutine.uid)
|
|
end
|
|
end
|
|
|
|
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
|
|
stopPropagation = eventHooks[i](event, eventData)
|
|
if stopPropagation then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
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
|
|
|
|
function kernel.start()
|
|
local s, m = pcall(function()
|
|
repeat
|
|
local eventData = { os.pullEventRaw() }
|
|
local event = table.remove(eventData, 1)
|
|
kernel.event(event, eventData)
|
|
until event == 'kernel_halt'
|
|
end)
|
|
|
|
if not s then
|
|
kernel.window.setVisible(true)
|
|
term.redirect(kernel.window)
|
|
print('\nCrash detected\n')
|
|
_G.printError(m)
|
|
end
|
|
term.redirect(kernel.terminal)
|
|
end
|
|
|
|
local function init(...)
|
|
local args = { ... }
|
|
|
|
local runLevel = #args > 0 and 6 or 7
|
|
|
|
print('Starting Opus OS')
|
|
local dir = 'sys/extensions'
|
|
local files = fs.list(dir)
|
|
table.sort(files)
|
|
for _,file in ipairs(files) do
|
|
local level = file:match('(%d).%S+.lua') or 99
|
|
if tonumber(level) <= runLevel then
|
|
local s, m = shell.run(fs.combine(dir, file))
|
|
if not s then
|
|
error(m)
|
|
end
|
|
os.sleep(0)
|
|
end
|
|
end
|
|
|
|
os.queueEvent('kernel_ready')
|
|
|
|
if args[1] then
|
|
kernel.hook('kernel_ready', function()
|
|
local s, m = kernel.run({
|
|
title = args[1],
|
|
path = 'sys/apps/shell',
|
|
args = args,
|
|
haltOnExit = true,
|
|
haltOnError = true,
|
|
terminal = kernel.terminal,
|
|
})
|
|
if s then
|
|
kernel.raise(s.uid)
|
|
else
|
|
error(m)
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
kernel.run({
|
|
fn = init,
|
|
title = 'init',
|
|
haltOnError = true,
|
|
args = { ... },
|
|
})
|
|
|
|
kernel.start()
|