opus/sys/extensions/7.multishell.lua

392 lines
8.9 KiB
Lua
Raw Normal View History

2017-10-08 21:45:01 +00:00
_G.requireInjector()
2016-12-11 19:24:52 +00:00
local Config = require('config')
local Util = require('util')
2016-12-11 19:24:52 +00:00
2017-10-08 21:45:01 +00:00
local colors = _G.colors
local fs = _G.fs
local kernel = _G.kernel
2017-10-08 21:45:01 +00:00
local keys = _G.keys
local os = _G.os
local printError = _G.printError
2017-10-16 20:54:50 +00:00
local shell = _ENV.shell
2017-10-08 21:45:01 +00:00
local term = _G.term
local window = _G.window
2017-10-05 17:07:48 +00:00
2018-01-21 10:44:13 +00:00
local parentTerm = _G.device.terminal
2016-12-11 19:24:52 +00:00
local w,h = parentTerm.getSize()
2017-10-16 20:54:50 +00:00
local overviewId
2016-12-11 19:24:52 +00:00
local tabsDirty = false
2018-01-12 01:53:32 +00:00
local closeInd = Util.getVersion() >= 1.76 and '\215' or '*'
2018-01-14 23:28:23 +00:00
local multishell = { }
shell.setEnv('multishell', multishell)
2017-10-08 21:45:01 +00:00
2018-01-13 20:17:26 +00:00
multishell.term = parentTerm --deprecated
2016-12-11 19:24:52 +00:00
local config = {
standard = {
2017-09-26 05:40:02 +00:00
textColor = colors.lightGray,
tabBarTextColor = colors.lightGray,
2017-09-26 03:23:10 +00:00
focusTextColor = colors.white,
2017-09-26 05:40:02 +00:00
backgroundColor = colors.gray,
tabBarBackgroundColor = colors.gray,
2016-12-11 19:24:52 +00:00
focusBackgroundColor = colors.gray,
},
color = {
2017-09-26 02:49:44 +00:00
textColor = colors.lightGray,
2016-12-11 19:24:52 +00:00
tabBarTextColor = colors.lightGray,
2017-09-26 05:40:02 +00:00
focusTextColor = colors.white,
backgroundColor = colors.gray,
tabBarBackgroundColor = colors.gray,
focusBackgroundColor = colors.gray,
2016-12-11 19:24:52 +00:00
},
}
Config.load('multishell', config)
2018-01-12 01:53:32 +00:00
local _colors = parentTerm.isColor() and config.color or config.standard
2016-12-11 19:24:52 +00:00
local function redrawMenu()
if not tabsDirty then
2017-10-15 06:36:54 +00:00
os.queueEvent('multishell_redraw')
2016-12-11 19:24:52 +00:00
tabsDirty = true
end
end
function multishell.getFocus()
2018-01-20 12:18:13 +00:00
local currentTab = kernel.getFocused()
2018-01-12 01:53:32 +00:00
return currentTab.uid
2016-12-11 19:24:52 +00:00
end
function multishell.setFocus(tabId)
2018-01-12 01:53:32 +00:00
return kernel.raise(tabId)
2016-12-11 19:24:52 +00:00
end
function multishell.getTitle(tabId)
2018-01-12 01:53:32 +00:00
local tab = kernel.find(tabId)
return tab and tab.title
2016-12-11 19:24:52 +00:00
end
2017-10-15 23:55:05 +00:00
function multishell.setTitle(tabId, title)
2018-01-12 01:53:32 +00:00
local tab = kernel.find(tabId)
2016-12-11 19:24:52 +00:00
if tab then
2018-01-20 12:18:13 +00:00
tab.title = title
2016-12-11 19:24:52 +00:00
redrawMenu()
end
end
function multishell.getCurrent()
2018-01-12 01:53:32 +00:00
local runningTab = kernel.getCurrent()
return runningTab and runningTab.uid
2016-12-11 19:24:52 +00:00
end
function multishell.getTab(tabId)
2018-01-12 01:53:32 +00:00
return kernel.find(tabId)
2016-12-11 19:24:52 +00:00
end
function multishell.terminate(tabId)
2017-10-15 06:36:54 +00:00
os.queueEvent('multishell_terminate', tabId)
2016-12-11 19:24:52 +00:00
end
function multishell.getTabs()
2018-01-12 01:53:32 +00:00
return kernel.routines
2016-12-11 19:24:52 +00:00
end
function multishell.launch( tProgramEnv, sProgramPath, ... )
-- backwards compatibility
return multishell.openTab({
env = tProgramEnv,
path = sProgramPath,
args = { ... },
})
end
function multishell.openTab(tab)
if not tab.title and tab.path then
2018-01-20 12:18:13 +00:00
tab.title = fs.getName(tab.path):match('([^%.]+)')
2016-12-11 19:24:52 +00:00
end
tab.title = tab.title or 'untitled'
2018-01-20 12:18:13 +00:00
tab.window = tab.window or window.create(parentTerm, 1, 2, w, h - 1, false)
tab.terminal = tab.terminal or tab.window
2016-12-11 19:24:52 +00:00
2018-01-12 01:53:32 +00:00
local routine = kernel.newRoutine(tab)
2016-12-11 19:24:52 +00:00
2018-01-20 12:18:13 +00:00
routine.co = coroutine.create(function()
2018-01-12 01:53:32 +00:00
local result, err
if tab.fn then
result, err = Util.runFunction(routine.env, tab.fn, table.unpack(tab.args or { } ))
elseif tab.path then
result, err = Util.run(routine.env, tab.path, table.unpack(tab.args or { } ))
else
err = 'multishell: invalid tab'
2016-12-11 19:24:52 +00:00
end
2018-01-12 01:53:32 +00:00
if not result and err and err ~= 'Terminated' then
if err then
printError(tostring(err))
end
2018-01-20 12:18:13 +00:00
print('\nPress enter to close')
routine.isDead = true
routine.hidden = false
2018-01-12 01:53:32 +00:00
while true do
local e, code = os.pullEventRaw('key')
if e == 'terminate' or e == 'key' and code == keys.enter then
break
end
end
end
end)
2018-01-14 04:40:53 +00:00
kernel.launch(routine)
2018-01-12 01:53:32 +00:00
if tab.focused then
2018-01-14 23:28:23 +00:00
multishell.setFocus(routine.uid)
2016-12-11 19:24:52 +00:00
else
redrawMenu()
end
2018-01-20 12:18:13 +00:00
return routine.uid
2016-12-11 19:24:52 +00:00
end
function multishell.hideTab(tabId)
2018-01-12 01:53:32 +00:00
local tab = kernel.find(tabId)
2016-12-11 19:24:52 +00:00
if tab then
tab.hidden = true
2018-01-13 20:17:26 +00:00
kernel.lower(tab.uid)
2016-12-11 19:24:52 +00:00
redrawMenu()
end
end
function multishell.unhideTab(tabId)
2018-01-12 01:53:32 +00:00
local tab = kernel.find(tabId)
2016-12-11 19:24:52 +00:00
if tab then
tab.hidden = false
redrawMenu()
end
end
function multishell.getCount()
2018-01-12 01:53:32 +00:00
return #kernel.routines
2016-12-11 19:24:52 +00:00
end
2018-01-12 01:53:32 +00:00
kernel.hook('kernel_focus', function(_, eventData)
local previous = eventData[2]
if previous then
local routine = kernel.find(previous)
if routine and routine.window then
routine.window.setVisible(false)
if routine.hidden then
kernel.lower(previous)
end
end
end
local focused = kernel.find(eventData[1])
if focused and focused.window then
focused.window.setVisible(true)
end
2017-10-15 06:36:54 +00:00
2018-01-12 01:53:32 +00:00
redrawMenu()
end)
kernel.hook('multishell_terminate', function(_, eventData)
local tab = kernel.find(eventData[1])
2017-10-15 06:36:54 +00:00
if tab and not tab.isOverview then
if coroutine.status(tab.co) ~= 'dead' then
2018-01-12 01:53:32 +00:00
tab:resume("terminate")
2017-10-15 06:36:54 +00:00
end
2016-12-11 19:24:52 +00:00
end
2017-10-15 06:36:54 +00:00
return true
2016-12-11 19:24:52 +00:00
end)
kernel.hook('multishell_redraw', function()
2017-10-15 06:36:54 +00:00
tabsDirty = false
2017-10-15 23:55:05 +00:00
local function write(x, text, bg, fg)
parentTerm.setBackgroundColor(bg)
parentTerm.setTextColor(fg)
parentTerm.setCursorPos(x, 1)
parentTerm.write(text)
end
local bg = _colors.tabBarBackgroundColor
parentTerm.setBackgroundColor(bg)
parentTerm.setCursorPos(1, 1)
parentTerm.clearLine()
2018-01-20 12:18:13 +00:00
local currentTab = kernel.getFocused()
2018-01-12 01:53:32 +00:00
for _,tab in pairs(kernel.routines) do
2017-10-16 20:54:50 +00:00
if tab.hidden and tab ~= currentTab then
tab.width = 0
2017-10-15 06:36:54 +00:00
else
2017-10-16 20:54:50 +00:00
tab.width = #tab.title + 1
2016-12-11 19:24:52 +00:00
end
end
2017-10-16 20:54:50 +00:00
local function width()
local tw = 0
2018-01-12 01:53:32 +00:00
Util.each(kernel.routines, function(t) tw = tw + t.width end)
2017-10-16 20:54:50 +00:00
return tw
end
while width() > w - 3 do
local tab = select(2,
2018-01-12 01:53:32 +00:00
Util.spairs(kernel.routines, function(a, b) return a.width > b.width end)())
2017-10-16 20:54:50 +00:00
tab.width = tab.width - 1
end
2018-01-14 23:48:18 +00:00
local function compareTab(a, b)
2018-01-20 12:18:13 +00:00
if a.hidden then return false end
return b.hidden or a.uid < b.uid
2018-01-14 23:48:18 +00:00
end
2017-10-16 20:54:50 +00:00
local tabX = 0
2018-01-12 01:53:32 +00:00
for _,tab in Util.spairs(kernel.routines, compareTab) do
2017-10-16 20:54:50 +00:00
if tab.width > 0 then
tab.sx = tabX + 1
tab.ex = tabX + tab.width
tabX = tabX + tab.width
if tab ~= currentTab then
write(tab.sx, tab.title:sub(1, tab.width - 1),
_colors.backgroundColor, _colors.textColor)
2017-10-15 06:36:54 +00:00
end
end
end
2017-10-15 23:55:05 +00:00
2017-10-16 20:54:50 +00:00
if currentTab then
write(currentTab.sx - 1,
' ' .. currentTab.title:sub(1, currentTab.width - 1) .. ' ',
_colors.focusBackgroundColor, _colors.focusTextColor)
if not currentTab.isOverview then
write(w, closeInd, _colors.backgroundColor, _colors.focusTextColor)
end
2017-10-15 06:36:54 +00:00
end
2018-01-12 01:53:32 +00:00
if currentTab and currentTab.window then
2017-10-15 06:36:54 +00:00
currentTab.window.restoreCursor()
end
return true
end)
kernel.hook('term_resize', function(_, eventData)
2017-10-15 06:36:54 +00:00
if not eventData[1] then --- TEST
w,h = parentTerm.getSize()
local windowHeight = h-1
2018-01-12 01:53:32 +00:00
for _,key in pairs(Util.keys(kernel.routines)) do
local tab = kernel.routines[key]
2017-10-15 06:36:54 +00:00
local x,y = tab.window.getCursorPos()
if y > windowHeight then
2017-10-16 20:54:50 +00:00
tab.window.scroll(y - windowHeight)
tab.window.setCursorPos(x, windowHeight)
2016-12-11 19:24:52 +00:00
end
2017-10-16 20:54:50 +00:00
tab.window.reposition(1, 2, w, windowHeight)
2016-12-11 19:24:52 +00:00
end
2017-10-15 06:36:54 +00:00
redrawMenu()
end
end)
kernel.hook('mouse_click', function(_, eventData)
2017-10-15 06:36:54 +00:00
local x, y = eventData[2], eventData[3]
2018-01-12 01:53:32 +00:00
2017-10-15 06:36:54 +00:00
if y == 1 then
if x == 1 then
2017-10-16 20:54:50 +00:00
multishell.setFocus(overviewId)
2017-10-15 06:36:54 +00:00
elseif x == w then
2018-01-20 12:18:13 +00:00
local currentTab = kernel.getFocused()
2017-10-15 06:36:54 +00:00
if currentTab then
2018-01-12 01:53:32 +00:00
multishell.terminate(currentTab.uid)
2017-10-15 06:36:54 +00:00
end
else
2018-01-12 01:53:32 +00:00
for _,tab in pairs(kernel.routines) do
2017-10-15 06:36:54 +00:00
if not tab.hidden and tab.sx then
if x >= tab.sx and x <= tab.ex then
2018-01-12 01:53:32 +00:00
multishell.setFocus(tab.uid)
2017-10-15 06:36:54 +00:00
break
end
end
end
end
return true
end
eventData[3] = eventData[3] - 1
end)
2018-01-12 01:53:32 +00:00
kernel.hook({ 'mouse_up', 'mouse_drag' }, function(_, eventData)
2017-10-15 06:36:54 +00:00
eventData[3] = eventData[3] - 1
end)
kernel.hook('mouse_scroll', function(_, eventData)
2018-01-12 01:53:32 +00:00
if eventData[3] == 1 then
2017-10-15 06:36:54 +00:00
return true
end
2018-01-12 01:53:32 +00:00
eventData[3] = eventData[3] - 1
2017-10-15 06:36:54 +00:00
end)
2016-12-11 19:24:52 +00:00
local function startup()
2017-10-16 20:54:50 +00:00
local success = true
2016-12-11 19:24:52 +00:00
2017-10-16 20:54:50 +00:00
local function runDir(directory, open)
if not fs.exists(directory) then
return true
end
2017-10-16 20:54:50 +00:00
local files = fs.list(directory)
table.sort(files)
2016-12-11 19:24:52 +00:00
2017-10-16 20:54:50 +00:00
for _,file in ipairs(files) do
os.sleep(0)
local result, err = open(directory .. '/' .. file)
2018-01-20 12:18:13 +00:00
2017-10-16 20:54:50 +00:00
if result then
if term.isColor() then
term.setTextColor(colors.green)
end
term.write('[PASS] ')
term.setTextColor(colors.white)
term.write(fs.combine(directory, file))
2018-01-14 23:28:23 +00:00
print()
2017-10-16 20:54:50 +00:00
else
if term.isColor() then
term.setTextColor(colors.red)
end
term.write('[FAIL] ')
term.setTextColor(colors.white)
term.write(fs.combine(directory, file))
if err then
2018-01-14 23:28:23 +00:00
_G.printError('\n' .. err)
2017-10-16 20:54:50 +00:00
end
2018-01-20 12:18:13 +00:00
print()
2017-10-16 20:54:50 +00:00
success = false
end
end
2017-09-06 01:21:43 +00:00
end
2016-12-11 19:24:52 +00:00
2017-10-16 20:54:50 +00:00
runDir('sys/autorun', shell.run)
runDir('usr/autorun', shell.run)
if not success then
2018-01-14 23:28:23 +00:00
multishell.setFocus(multishell.getCurrent())
printError('\nA startup program has errored')
os.pullEvent('terminate')
2016-12-11 19:24:52 +00:00
end
end
2018-01-15 21:28:10 +00:00
kernel.hook('kernel_ready', function()
overviewId = multishell.openTab({
path = 'sys/apps/Overview.lua',
isOverview = true,
focused = true,
2018-01-20 12:18:13 +00:00
title = '+',
2018-01-15 21:28:10 +00:00
})
multishell.openTab({
fn = startup,
title = 'Autorun',
})
end)