opus/sys/kernel.lua

364 lines
7.7 KiB
Lua
Raw Normal View History

2019-11-08 01:00:54 +00:00
local Array = require('opus.array')
local Terminal = require('opus.terminal')
2020-05-09 04:32:44 +00:00
local trace = require('opus.trace')
local Util = require('opus.util')
_G.kernel = {
2018-01-24 22:39:38 +00:00
UID = 0,
hooks = { },
routines = { },
}
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
2019-03-27 19:21:31 +00:00
local window = _G.window
2018-01-16 01:38:30 +00:00
local w, h = term.getSize()
kernel.terminal = term.current()
2019-02-06 04:03:57 +00:00
kernel.window = Terminal.window(kernel.terminal, 1, 1, w, h, false)
kernel.window.setMaxScroll(200)
2018-01-20 12:18:13 +00:00
2018-01-12 01:53:32 +00:00
local focusedRoutineEvents = Util.transpose {
2018-01-24 22:39:38 +00:00
'char', 'key', 'key_up',
'mouse_click', 'mouse_drag', 'mouse_scroll', 'mouse_up',
'paste', 'terminate',
2018-01-12 01:53:32 +00:00
}
2019-05-03 19:30:09 +00:00
_G._syslog = function(pattern, ...)
2019-02-06 04:03:57 +00:00
kernel.window.scrollBottom()
2020-05-31 02:05:53 +00:00
kernel.window.print(Util.tostring(pattern, ...))
2018-01-13 20:17:26 +00:00
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)
2018-01-24 22:39:38 +00:00
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
2018-01-24 22:39:38 +00:00
end
end
end
2019-03-27 19:21:31 +00:00
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
2020-05-09 04:32:44 +00:00
local Routine = { }
2018-01-12 01:53:32 +00:00
function Routine:resume(event, ...)
2018-01-24 22:39:38 +00:00
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
2020-05-09 04:32:44 +00:00
kernel.running = self
2018-01-24 22:39:38 +00:00
local ok, result = coroutine.resume(self.co, event, ...)
kernel.running = previous
2020-05-09 04:32:44 +00:00
self.filter = result
2018-01-24 22:39:38 +00:00
self.terminal = term.current()
term.redirect(previousTerm)
return ok, result
end
2018-01-12 01:53:32 +00:00
end
function Routine:run()
self.co = self.co or coroutine.create(function()
2020-05-13 03:25:37 +00:00
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
2020-05-13 03:25:37 +00:00
result, err, stack = trace(fn, table.unpack(self.args or { } ))
else
err = err or 'kernel: invalid routine'
end
2020-05-13 03:25:37 +00:00
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
2020-05-09 04:32:44 +00:00
-- 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
2018-01-12 01:53:32 +00:00
function kernel.getFocused()
2018-01-24 22:39:38 +00:00
return kernel.routines[1]
2018-01-12 01:53:32 +00:00
end
function kernel.getCurrent()
2018-01-24 22:39:38 +00:00
return kernel.running
2018-01-12 01:53:32 +00:00
end
2018-12-17 03:17:19 +00:00
function kernel.getShell()
return shell
end
-- each routine inherits the parent's env
2020-06-11 19:28:43 +00:00
function kernel.makeEnv(env, dir)
env = setmetatable(Util.shallowCopy(env or _ENV), { __index = _G })
2020-06-11 19:28:43 +00:00
_G.requireInjector(env, dir)
return env
end
2020-05-05 23:25:58 +00:00
function kernel.newRoutine(env, args)
2018-01-24 22:39:38 +00:00
kernel.UID = kernel.UID + 1
2018-01-12 01:53:32 +00:00
2018-01-24 22:39:38 +00:00
local routine = setmetatable({
uid = kernel.UID,
timestamp = os.clock(),
window = kernel.window,
2019-04-18 15:13:28 +00:00
title = 'untitled',
2018-01-24 22:39:38 +00:00
}, { __index = Routine })
2018-01-12 01:53:32 +00:00
2018-01-24 22:39:38 +00:00
Util.merge(routine, args)
2020-06-11 19:28:43 +00:00
routine.env = args.env or kernel.makeEnv(env, routine.path and fs.getDir(routine.path))
2020-05-09 04:32:44 +00:00
routine.terminal = routine.terminal or routine.window
2018-01-12 01:53:32 +00:00
2018-01-24 22:39:38 +00:00
return routine
2018-01-12 01:53:32 +00:00
end
function kernel.run(env, args)
local routine = kernel.newRoutine(env, args)
local s, m = routine:run()
return s and routine, m
2018-01-12 01:53:32 +00:00
end
function kernel.raise(uid)
2019-11-01 22:35:58 +00:00
if kernel.getFocused() and kernel.getFocused().pinned then
return false
end
2018-01-24 22:39:38 +00:00
local routine = Util.find(kernel.routines, 'uid', uid)
if routine then
local previous = kernel.routines[1]
if routine ~= previous then
2019-11-08 01:00:54 +00:00
Array.removeByValue(kernel.routines, routine)
2018-01-24 22:39:38 +00:00
table.insert(kernel.routines, 1, routine)
end
2019-03-27 19:21:31 +00:00
switch(routine, previous)
2018-01-24 22:39:38 +00:00
return true
end
return false
2018-01-12 01:53:32 +00:00
end
function kernel.lower(uid)
2018-01-24 22:39:38 +00:00
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
2019-11-08 01:00:54 +00:00
Array.removeByValue(kernel.routines, routine)
2018-01-24 22:39:38 +00:00
table.insert(kernel.routines, routine)
return true
end
return false
2018-01-12 01:53:32 +00:00
end
function kernel.find(uid)
2018-01-24 22:39:38 +00:00
return Util.find(kernel.routines, 'uid', uid)
2018-01-12 01:53:32 +00:00
end
2020-05-09 04:32:44 +00:00
function kernel.halt(status, message)
os.queueEvent('kernel_halt', status, message)
2018-01-12 01:53:32 +00:00
end
function kernel.event(event, eventData)
2018-01-24 22:39:38 +00:00
local stopPropagation
2019-11-08 01:00:54 +00:00
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]
2018-01-24 22:39:38 +00:00
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
2018-01-12 01:53:32 +00:00
end
2018-01-14 04:40:53 +00:00
function kernel.start()
2020-05-09 04:32:44 +00:00
local s, m
2020-05-10 20:04:20 +00:00
local s2, m2 = pcall(function()
2018-01-24 22:39:38 +00:00
repeat
local eventData = { os.pullEventRaw() }
local event = table.remove(eventData, 1)
kernel.event(event, eventData)
2020-05-09 04:32:44 +00:00
if event == 'kernel_halt' then
s = eventData[1]
m = eventData[2]
end
2018-01-24 22:39:38 +00:00
until event == 'kernel_halt'
end)
2020-05-10 20:04:20 +00:00
if (not s and m) or (not s2 and m2) then
2018-01-24 22:39:38 +00:00
kernel.window.setVisible(true)
term.redirect(kernel.window)
print('\nCrash detected\n')
2020-05-10 20:04:20 +00:00
_G.printError(m or m2)
2018-01-24 22:39:38 +00:00
end
term.redirect(kernel.terminal)
end
2018-01-14 21:44:43 +00:00
2018-01-16 01:38:30 +00:00
local function init(...)
2018-01-24 22:39:38 +00:00
local args = { ... }
local runLevel = #args > 0 and 6 or 7
print('Starting Opus OS')
local dir = 'sys/init'
2018-01-24 22:39:38 +00:00
local files = fs.list(dir)
table.sort(files)
for _,file in ipairs(files) do
2018-03-30 17:12:46 +00:00
local level = file:match('(%d).%S+.lua') or 99
2018-01-24 22:39:38 +00:00
if tonumber(level) <= runLevel then
-- All init programs run under the original shell
2018-01-24 22:39:38 +00:00
local s, m = shell.run(fs.combine(dir, file))
if not s then
error(m, -1)
2018-01-24 22:39:38 +00:00
end
os.sleep(0)
end
end
os.queueEvent('kernel_ready')
if args[1] then
kernel.hook('kernel_ready', function()
2019-03-27 19:21:31 +00:00
term.redirect(kernel.window)
shell.run('sys/apps/autorun.lua')
2020-05-09 04:32:44 +00:00
local win = window.create(kernel.terminal, 1, 1, w, h, true)
local s, m = kernel.run(_ENV, {
2018-01-24 22:39:38 +00:00
title = args[1],
2019-02-12 22:03:50 +00:00
path = 'sys/apps/shell.lua',
2018-01-24 22:39:38 +00:00
args = args,
2020-05-09 04:32:44 +00:00
window = win,
onExit = function(_, s, m)
kernel.halt(s, m)
end,
2018-01-24 22:39:38 +00:00
})
if s then
kernel.raise(s.uid)
else
error(m)
end
end)
end
2018-01-14 21:44:43 +00:00
end
kernel.run(_ENV, {
2018-01-24 22:39:38 +00:00
fn = init,
title = 'init',
args = { ... },
2020-05-09 04:32:44 +00:00
onExit = function(_, status, message)
if not status then
kernel.halt(status, message)
end
end,
2018-01-15 21:28:10 +00:00
})
2018-01-16 01:38:30 +00:00
kernel.start()