mirror of
https://github.com/kepler155c/opus
synced 2024-10-31 22:26:16 +00:00
364 lines
7.7 KiB
Lua
364 lines
7.7 KiB
Lua
local Array = require('opus.array')
|
|
local Terminal = require('opus.terminal')
|
|
local trace = require('opus.trace')
|
|
local Util = require('opus.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 = Terminal.window(kernel.terminal, 1, 1, w, h, false)
|
|
kernel.window.setMaxScroll(200)
|
|
|
|
local focusedRoutineEvents = Util.transpose {
|
|
'char', 'key', 'key_up',
|
|
'mouse_click', 'mouse_drag', 'mouse_scroll', 'mouse_up',
|
|
'paste', 'terminate',
|
|
}
|
|
|
|
_G._syslog = function(pattern, ...)
|
|
kernel.window.scrollBottom()
|
|
kernel.window.print(Util.tostring(pattern, ...))
|
|
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 *should* only unhook from within the function that hooked
|
|
function kernel.unhook(event, fn)
|
|
if type(event) == 'table' then
|
|
for _,v in pairs(event) do
|
|
kernel.unhook(v, fn)
|
|
end
|
|
else
|
|
local eventHooks = kernel.hooks[event]
|
|
if eventHooks then
|
|
Array.removeByValue(eventHooks, fn)
|
|
if #eventHooks == 0 then
|
|
kernel.hooks[event] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function switch(routine, previous)
|
|
if routine then
|
|
if previous and previous.window then
|
|
previous.window.setVisible(false)
|
|
if previous.hidden then
|
|
kernel.lower(previous.uid)
|
|
end
|
|
end
|
|
|
|
if routine and routine.window then
|
|
routine.window.setVisible(true)
|
|
end
|
|
|
|
os.queueEvent('kernel_focus', routine.uid, previous and previous.uid)
|
|
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
|
|
local ok, result = coroutine.resume(self.co, event, ...)
|
|
kernel.running = previous
|
|
|
|
self.filter = result
|
|
self.terminal = term.current()
|
|
term.redirect(previousTerm)
|
|
|
|
return ok, result
|
|
end
|
|
end
|
|
|
|
function Routine:run()
|
|
self.co = self.co or coroutine.create(function()
|
|
local result, err, fn, stack
|
|
|
|
if self.fn then
|
|
fn = self.fn
|
|
_G.setfenv(fn, self.env)
|
|
elseif self.path then
|
|
fn, err = loadfile(self.path, self.env)
|
|
elseif self.chunk then
|
|
fn, err = load(self.chunk, self.title, nil, self.env)
|
|
end
|
|
|
|
if fn then
|
|
result, err, stack = trace(fn, table.unpack(self.args or { } ))
|
|
else
|
|
err = err or 'kernel: invalid routine'
|
|
end
|
|
|
|
pcall(self.onExit, self, result, err, stack)
|
|
self:cleanup()
|
|
|
|
if not result then
|
|
error(err)
|
|
end
|
|
end)
|
|
|
|
table.insert(kernel.routines, self)
|
|
|
|
return self:resume()
|
|
end
|
|
|
|
-- override if any post processing is required
|
|
function Routine:onExit(status, message) -- self, status, message
|
|
if not status and message ~= 'Terminated' then
|
|
_G.printError(message)
|
|
end
|
|
end
|
|
|
|
function Routine:cleanup()
|
|
Array.removeByValue(kernel.routines, self)
|
|
if #kernel.routines > 0 then
|
|
switch(kernel.routines[1])
|
|
end
|
|
end
|
|
|
|
function kernel.getFocused()
|
|
return kernel.routines[1]
|
|
end
|
|
|
|
function kernel.getCurrent()
|
|
return kernel.running
|
|
end
|
|
|
|
function kernel.getShell()
|
|
return shell
|
|
end
|
|
|
|
-- each routine inherits the parent's env
|
|
function kernel.makeEnv(env, dir)
|
|
env = setmetatable(Util.shallowCopy(env or _ENV), { __index = _G })
|
|
_G.requireInjector(env, dir)
|
|
return env
|
|
end
|
|
|
|
function kernel.newRoutine(env, args)
|
|
kernel.UID = kernel.UID + 1
|
|
|
|
local routine = setmetatable({
|
|
uid = kernel.UID,
|
|
timestamp = os.clock(),
|
|
window = kernel.window,
|
|
title = 'untitled',
|
|
}, { __index = Routine })
|
|
|
|
Util.merge(routine, args)
|
|
routine.env = args.env or kernel.makeEnv(env, routine.path and fs.getDir(routine.path))
|
|
routine.terminal = routine.terminal or routine.window
|
|
|
|
return routine
|
|
end
|
|
|
|
function kernel.run(env, args)
|
|
local routine = kernel.newRoutine(env, args)
|
|
local s, m = routine:run()
|
|
return s and routine, m
|
|
end
|
|
|
|
function kernel.raise(uid)
|
|
if kernel.getFocused() and kernel.getFocused().pinned then
|
|
return false
|
|
end
|
|
|
|
local routine = Util.find(kernel.routines, 'uid', uid)
|
|
|
|
if routine then
|
|
local previous = kernel.routines[1]
|
|
if routine ~= previous then
|
|
Array.removeByValue(kernel.routines, routine)
|
|
table.insert(kernel.routines, 1, routine)
|
|
end
|
|
|
|
switch(routine, previous)
|
|
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
|
|
|
|
Array.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(status, message)
|
|
os.queueEvent('kernel_halt', status, message)
|
|
end
|
|
|
|
function kernel.event(event, eventData)
|
|
local stopPropagation
|
|
|
|
local eventHooks = kernel.hooks['*']
|
|
if eventHooks then
|
|
for i = #eventHooks, 1, -1 do
|
|
stopPropagation = eventHooks[i](event, eventData)
|
|
if stopPropagation then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
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
|
|
local s2, m2 = pcall(function()
|
|
repeat
|
|
local eventData = { os.pullEventRaw() }
|
|
local event = table.remove(eventData, 1)
|
|
kernel.event(event, eventData)
|
|
if event == 'kernel_halt' then
|
|
s = eventData[1]
|
|
m = eventData[2]
|
|
end
|
|
until event == 'kernel_halt'
|
|
end)
|
|
|
|
if (not s and m) or (not s2 and m2) then
|
|
kernel.window.setVisible(true)
|
|
term.redirect(kernel.window)
|
|
print('\nCrash detected\n')
|
|
_G.printError(m or m2)
|
|
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/init'
|
|
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
|
|
-- All init programs run under the original shell
|
|
local s, m = shell.run(fs.combine(dir, file))
|
|
if not s then
|
|
error(m, -1)
|
|
end
|
|
os.sleep(0)
|
|
end
|
|
end
|
|
|
|
os.queueEvent('kernel_ready')
|
|
|
|
if args[1] then
|
|
kernel.hook('kernel_ready', function()
|
|
|
|
term.redirect(kernel.window)
|
|
shell.run('sys/apps/autorun.lua')
|
|
|
|
local win = window.create(kernel.terminal, 1, 1, w, h, true)
|
|
local s, m = kernel.run(_ENV, {
|
|
title = args[1],
|
|
path = 'sys/apps/shell.lua',
|
|
args = args,
|
|
window = win,
|
|
onExit = function(_, s, m)
|
|
kernel.halt(s, m)
|
|
end,
|
|
})
|
|
if s then
|
|
kernel.raise(s.uid)
|
|
else
|
|
error(m)
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
kernel.run(_ENV, {
|
|
fn = init,
|
|
title = 'init',
|
|
args = { ... },
|
|
onExit = function(_, status, message)
|
|
if not status then
|
|
kernel.halt(status, message)
|
|
end
|
|
end,
|
|
})
|
|
|
|
kernel.start()
|