2018-01-21 10:44:13 +00:00
|
|
|
_G.requireInjector(_ENV)
|
2018-01-10 21:46:37 +00:00
|
|
|
|
2019-06-28 17:50:02 +00:00
|
|
|
local Terminal = require('opus.terminal')
|
|
|
|
local Util = require('opus.util')
|
2018-01-10 21:46:37 +00:00
|
|
|
|
|
|
|
_G.kernel = {
|
2018-01-24 22:39:38 +00:00
|
|
|
UID = 0,
|
|
|
|
hooks = { },
|
|
|
|
routines = { },
|
2018-01-10 21:46:37 +00:00
|
|
|
}
|
|
|
|
|
2018-01-14 21:44:43 +00:00
|
|
|
local fs = _G.fs
|
2018-01-10 21:46:37 +00:00
|
|
|
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()
|
2018-01-10 21:46:37 +00:00
|
|
|
|
2019-02-06 04:03:57 +00:00
|
|
|
kernel.window = Terminal.window(kernel.terminal, 1, 1, w, h, false)
|
|
|
|
kernel.window.setMaxScroll(100)
|
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
|
|
|
}
|
2018-01-10 21:46:37 +00:00
|
|
|
|
2019-05-03 19:30:09 +00:00
|
|
|
_G._syslog = function(pattern, ...)
|
2018-01-24 22:39:38 +00:00
|
|
|
local oldTerm = term.redirect(kernel.window)
|
2019-02-06 04:03:57 +00:00
|
|
|
kernel.window.scrollBottom()
|
2018-01-24 22:39:38 +00:00
|
|
|
Util.print(pattern, ...)
|
|
|
|
term.redirect(oldTerm)
|
2018-01-13 20:17:26 +00:00
|
|
|
end
|
|
|
|
|
2018-01-10 21:46:37 +00:00
|
|
|
-- 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
|
2018-01-10 21:46:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- you can only unhook from within the function that hooked
|
|
|
|
function kernel.unhook(event, fn)
|
2018-01-24 22:39:38 +00:00
|
|
|
local eventHooks = kernel.hooks[event]
|
|
|
|
if eventHooks then
|
|
|
|
Util.removeByValue(eventHooks, fn)
|
|
|
|
if #eventHooks == 0 then
|
|
|
|
kernel.hooks[event] = nil
|
|
|
|
end
|
|
|
|
end
|
2018-01-10 21:46:37 +00:00
|
|
|
end
|
|
|
|
|
2018-01-12 01:53:32 +00:00
|
|
|
local Routine = { }
|
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
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
|
2019-03-18 03:54:44 +00:00
|
|
|
error(result, -1)
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
|
|
|
if coroutine.status(self.co) == 'dead' then
|
|
|
|
Util.removeByValue(kernel.routines, self)
|
|
|
|
if #kernel.routines > 0 then
|
2019-03-27 19:21:31 +00:00
|
|
|
switch(kernel.routines[1])
|
2018-01-24 22:39:38 +00:00
|
|
|
end
|
|
|
|
if self.haltOnExit then
|
|
|
|
kernel.halt()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return ok, result
|
|
|
|
end
|
2018-01-12 01:53:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2018-01-12 01:53:32 +00:00
|
|
|
function kernel.newRoutine(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(),
|
|
|
|
terminal = kernel.window,
|
|
|
|
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)
|
|
|
|
routine.env = args.env or Util.shallowCopy(shell.getEnv())
|
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
|
|
|
|
|
2018-01-14 04:40:53 +00:00
|
|
|
function kernel.launch(routine)
|
2018-01-24 22:39:38 +00:00
|
|
|
routine.co = routine.co or coroutine.create(function()
|
|
|
|
local result, err
|
2018-01-12 01:53:32 +00:00
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
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
|
2018-01-12 01:53:32 +00:00
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
if not result and err ~= 'Terminated' then
|
|
|
|
error(err or 'Error occurred', 2)
|
|
|
|
end
|
|
|
|
end)
|
2018-01-12 01:53:32 +00:00
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
table.insert(kernel.routines, routine)
|
2018-01-12 01:53:32 +00:00
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
local s, m = routine:resume()
|
2018-01-12 01:53:32 +00:00
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
return not s and s or routine.uid, m
|
2018-01-14 04:40:53 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function kernel.run(args)
|
2018-01-24 22:39:38 +00:00
|
|
|
local routine = kernel.newRoutine(args)
|
|
|
|
kernel.launch(routine)
|
|
|
|
return routine
|
2018-01-12 01:53:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function kernel.raise(uid)
|
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
|
|
|
|
Util.removeByValue(kernel.routines, routine)
|
|
|
|
table.insert(kernel.routines, 1, routine)
|
|
|
|
end
|
2019-03-27 19:21:31 +00:00
|
|
|
|
|
|
|
switch(routine, previous)
|
|
|
|
-- local previous = eventData[2]
|
|
|
|
-- local routine = kernel.find(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
|
|
|
|
|
|
|
|
Util.removeByValue(kernel.routines, routine)
|
|
|
|
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
|
|
|
|
|
|
|
|
function kernel.halt()
|
2018-01-24 22:39:38 +00:00
|
|
|
os.queueEvent('kernel_halt')
|
2018-01-12 01:53:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function kernel.event(event, eventData)
|
2018-01-24 22:39:38 +00:00
|
|
|
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
|
2018-01-12 01:53:32 +00:00
|
|
|
end
|
|
|
|
|
2018-01-14 04:40:53 +00:00
|
|
|
function kernel.start()
|
2018-01-24 22:39:38 +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'
|
|
|
|
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)
|
2018-01-10 21:46:37 +00:00
|
|
|
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')
|
2019-03-31 19:16:45 +00:00
|
|
|
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
|
|
|
|
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()
|
2019-03-27 19:21:31 +00:00
|
|
|
|
|
|
|
term.redirect(kernel.window)
|
|
|
|
shell.run('sys/apps/autorun.lua')
|
|
|
|
|
|
|
|
local shellWindow = window.create(kernel.terminal, 1, 1, w, h, false)
|
2018-01-24 22:39:38 +00:00
|
|
|
local s, m = kernel.run({
|
|
|
|
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,
|
|
|
|
haltOnExit = true,
|
|
|
|
haltOnError = true,
|
2019-03-27 19:21:31 +00:00
|
|
|
terminal = shellWindow,
|
|
|
|
window = shellWindow,
|
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
|
|
|
|
|
2018-01-15 21:28:10 +00:00
|
|
|
kernel.run({
|
2018-01-24 22:39:38 +00:00
|
|
|
fn = init,
|
|
|
|
title = 'init',
|
|
|
|
haltOnError = true,
|
|
|
|
args = { ... },
|
2018-01-15 21:28:10 +00:00
|
|
|
})
|
2018-01-16 01:38:30 +00:00
|
|
|
|
|
|
|
kernel.start()
|