diff --git a/startup b/startup index e4561de..abc153a 100644 --- a/startup +++ b/startup @@ -1,6 +1,6 @@ local bootOptions = { { prompt = 'Default Shell', file = '/sys/boot/default.boot' }, - { prompt = 'Opus' , file = '/sys/boot/multishell.boot' }, + { prompt = 'Opus' , file = '/sys/boot/opus.boot' }, -- { prompt = 'TLCO' , file = '/sys/boot/tlco.boot' }, } local bootOption = 2 diff --git a/sys/apis/injector.lua b/sys/apis/injector.lua index 12c92ad..83f19aa 100644 --- a/sys/apis/injector.lua +++ b/sys/apis/injector.lua @@ -1,4 +1,4 @@ -local DEFAULT_UPATH = 'https://raw.githubusercontent.com/kepler155c/opus/develop/sys/apis' +local DEFAULT_UPATH = 'https://raw.githubusercontent.com/kepler155c/opus/develop-1.8/sys/apis' local PASTEBIN_URL = 'http://pastebin.com/raw' local GIT_URL = 'https://raw.githubusercontent.com' diff --git a/sys/apis/ui.lua b/sys/apis/ui.lua index f12ec8b..e2815a7 100644 --- a/sys/apis/ui.lua +++ b/sys/apis/ui.lua @@ -125,9 +125,9 @@ function Manager:init() focused = true }) elseif ch and self.currentPage then - if not self.currentPage.parent.device.side then + --if not self.currentPage.parent.device.side then self:click(ch, button, x, y) - end + --end end end, diff --git a/sys/apis/util.lua b/sys/apis/util.lua index ee5e926..251a6b6 100644 --- a/sys/apis/util.lua +++ b/sys/apis/util.lua @@ -412,9 +412,9 @@ function Util.httpGet(url, headers) end function Util.download(url, filename) - local contents = Util.httpGet(url) + local contents, msg = Util.httpGet(url) if not contents then - error('Failed to download ' .. url) + error(string.format('Failed to download %s\n%s', url, msg)) end if filename then @@ -441,6 +441,7 @@ function Util.runUrl(env, url, ...) -- os.run equivalent end function Util.run(env, path, ...) + if type(env) ~= 'table' then error('Util.run: env must be a table', 2) end setmetatable(env, { __index = _G }) local fn, m = loadfile(path, env) if fn then diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 073657b..3fb6fec 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -17,7 +17,9 @@ sandboxEnv.exit = function() Event.exitPullEvents() end sandboxEnv._echo = function( ... ) return { ... } end injector(sandboxEnv) -multishell.setTitle(multishell.getCurrent(), 'Lua') +if multishell and multishell.setTitle then + multishell.setTitle(multishell.getCurrent(), 'Lua') +end UI:configure('Lua', ...) local command = '' @@ -143,9 +145,6 @@ function page:eventHandler(event) self.prompt:updateCursor() elseif event.type == 'device' then - if not _G.device then - sandboxEnv.device = Peripheral.getList() - end self:setPrompt('device', true) self:executeStatement('device') diff --git a/sys/apps/Tabs.lua b/sys/apps/Tabs.lua index b0de4c4..3740105 100644 --- a/sys/apps/Tabs.lua +++ b/sys/apps/Tabs.lua @@ -6,8 +6,8 @@ local Util = require('util') local multishell = _ENV.multishell -multishell.setTitle(multishell.getCurrent(), 'Tabs') -UI:configure('Tabs', ...) +multishell.setTitle(multishell.getCurrent(), 'Tasks') +UI:configure('Tasks', ...) local page = UI.Page { menuBar = UI.MenuBar { @@ -19,7 +19,7 @@ local page = UI.Page { grid = UI.ScrollingGrid { y = 2, columns = { - { heading = 'ID', key = 'tabId', width = 4 }, + { heading = 'ID', key = 'uid', width = 4 }, { heading = 'Title', key = 'title' }, { heading = 'Status', key = 'status' }, { heading = 'Time', key = 'timestamp' }, @@ -39,9 +39,9 @@ function page:eventHandler(event) local t = self.grid:getSelected() if t then if event.type == 'activate' or event.type == 'grid_select' then - multishell.setFocus(t.tabId) + multishell.setFocus(t.uid) elseif event.type == 'terminate' then - multishell.terminate(t.tabId) + multishell.terminate(t.uid) end end if event.type == 'quit' then diff --git a/sys/apps/multishell b/sys/apps/multishell index e09ec09..b702aaa 100644 --- a/sys/apps/multishell +++ b/sys/apps/multishell @@ -1,8 +1,3 @@ -local sandboxEnv = { } -for k,v in pairs(_ENV) do - sandboxEnv[k] = v -end - _G.requireInjector() local Config = require('config') @@ -11,7 +6,6 @@ local Util = require('util') local colors = _G.colors local fs = _G.fs local kernel = _G.kernel -local keyboard = _G.device.keyboard local keys = _G.keys local multishell = _ENV.multishell local os = _G.os @@ -22,34 +16,15 @@ local window = _G.window local parentTerm = term.current() local w,h = parentTerm.getSize() -local tabs = { } -local currentTab -local _tabId = 0 local overviewId -local runningTab local tabsDirty = false -local closeInd = '*' -local downState = { } +local closeInd = Util.getVersion() >= 1.76 and '\215' or '*' -multishell.term = term.current() +multishell.term = term.current() --deprecated --- Default label -if not os.getComputerLabel() then - local id = os.getComputerID() - if _G.turtle then - os.setComputerLabel('turtle_' .. id) - elseif _G.pocket then - os.setComputerLabel('pocket_' .. id) - elseif _G.commands then - os.setComputerLabel('command_' .. id) - else - os.setComputerLabel('computer_' .. id) - end -end - -if Util.getVersion() >= 1.76 then - closeInd = '\215' -end +-- redirect kernel output to a window +kernel.window = window.create(parentTerm, 1, 2, w, h - 1, false) +kernel.terminal = kernel.window local config = { standard = { @@ -71,10 +46,7 @@ local config = { } Config.load('multishell', config) -local _colors = config.standard -if parentTerm.isColor() then - _colors = config.color -end +local _colors = parentTerm.isColor() and config.color or config.standard local function redrawMenu() if not tabsDirty then @@ -83,85 +55,73 @@ local function redrawMenu() end end -local function resumeTab(tab, event, eventData) - if not tab or coroutine.status(tab.co) == 'dead' then - return - end +function multishell.getFocus() + local currentTab = kernel.routines[1] + return currentTab.uid +end - if not tab.filter or tab.filter == event or event == "terminate" then - eventData = eventData or { } - term.redirect(tab.terminal) - local previousTab = runningTab - runningTab = tab - local ok, result = coroutine.resume(tab.co, event, unpack(eventData)) - tab.terminal = term.current() - if ok then - tab.filter = result - else - printError(result) +function multishell.setFocus(tabId) + return kernel.raise(tabId) +end + +function multishell.getTitle(tabId) + local tab = kernel.find(tabId) + return tab and tab.title +end + +function multishell.setTitle(tabId, title) + local tab = kernel.find(tabId) + if tab then + if not tab.isOverview then + tab.title = title or '' end - - runningTab = previousTab - - return ok, result + redrawMenu() end end -local function selectTab(tab) - if not tab then - for _,ftab in pairs(tabs) do - if not ftab.hidden then - tab = ftab - break - end - end - end - - if not tab then - tab = tabs[overviewId] - end - - if currentTab and currentTab ~= tab then - currentTab.window.setVisible(false) - --if coroutine.status(currentTab.co) == 'suspended' then - -- the process that opens a new tab won't get the lose focus event - -- os.queueEvent('multishell_notifyfocus', currentTab.tabId) - --resumeTab(currentTab, 'multishell_losefocus') - --end - if tab and not currentTab.hidden then - tab.previousTabId = currentTab.tabId - end - end - - if tab ~= currentTab then - currentTab = tab - tab.window.setVisible(true) - Util.clear(keyboard.state) --- reset keyboard state - -- why not just queue event with tab ID if we want to notify - --resumeTab(tab, 'multishell_focus') - end +function multishell.getCurrent() + local runningTab = kernel.getCurrent() + return runningTab and runningTab.uid end -local function nextTabId() - _tabId = _tabId + 1 - return _tabId +function multishell.getTab(tabId) + return kernel.find(tabId) end -local function launchProcess(tab) - tab.tabId = nextTabId() - tab.timestamp = os.clock() +function multishell.terminate(tabId) + os.queueEvent('multishell_terminate', tabId) +end + +function multishell.getTabs() + return kernel.routines +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 + tab.title = fs.getName(tab.path) + end + tab.title = tab.title or 'untitled' tab.window = window.create(parentTerm, 1, 2, w, h - 1, false) tab.terminal = tab.window - tab.env = Util.shallowCopy(tab.env or sandboxEnv) + + local routine = kernel.newRoutine(tab) tab.co = coroutine.create(function() - local result, err if tab.fn then - result, err = Util.runFunction(tab.env, tab.fn, table.unpack(tab.args or { } )) + result, err = Util.runFunction(routine.env, tab.fn, table.unpack(tab.args or { } )) elseif tab.path then - result, err = Util.run(tab.env, tab.path, table.unpack(tab.args or { } )) + result, err = Util.run(routine.env, tab.path, table.unpack(tab.args or { } )) else err = 'multishell: invalid tab' end @@ -179,119 +139,29 @@ local function launchProcess(tab) end end end - tabs[tab.tabId] = nil - if tab == currentTab then - local previousTab - if tab.previousTabId then - previousTab = tabs[tab.previousTabId] - if previousTab and previousTab.hidden then - previousTab = nil - end - end - selectTab(previousTab) - end - redrawMenu() end) - tabs[tab.tabId] = tab - resumeTab(tab) - return tab -end + kernel.run(routine) -function multishell.getFocus() - return currentTab.tabId -end - -function multishell.setFocus(tabId) - local tab = tabs[tabId] - if tab then - selectTab(tab) - redrawMenu() - return true - end - return false -end - -function multishell.getTitle(tabId) - local tab = tabs[tabId] - if tab then - return tab.title - end -end - -function multishell.setTitle(tabId, title) - local tab = tabs[tabId] - if tab then - if not tab.isOverview then - tab.title = title or '' - end - redrawMenu() - end -end - -function multishell.getCurrent() - if runningTab then - return runningTab.tabId - end -end - -function multishell.getTab(tabId) - return tabs[tabId] -end - -function multishell.terminate(tabId) - os.queueEvent('multishell_terminate', tabId) -end - -function multishell.getTabs() - return tabs -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 - tab.title = fs.getName(tab.path) - end - tab.title = tab.title or 'untitled' - - local previousTerm = term.current() - launchProcess(tab) - term.redirect(previousTerm) - - if tab.hidden then - if coroutine.status(tab.co) == 'dead' or tab.isDead then - tab.hidden = false - end - elseif tab.focused then - multishell.setFocus(tab.tabId) + if tab.focused then + multishell.setFocus(tab.uid) else redrawMenu() end - return tab.tabId + return tab.uid end function multishell.hideTab(tabId) - local tab = tabs[tabId] + local tab = kernel.find(tabId) if tab then tab.hidden = true - if currentTab.tabId == tabId then - selectTab(tabs[currentTab.previousTabId]) - end redrawMenu() end end function multishell.unhideTab(tabId) - local tab = tabs[tabId] + local tab = kernel.find(tabId) if tab then tab.hidden = false redrawMenu() @@ -299,16 +169,34 @@ function multishell.unhideTab(tabId) end function multishell.getCount() - return Util.size(tabs) + return #kernel.routines end -kernel.hook('multishell_terminate', function(_, eventData) - local tabId = eventData[1] or -1 - local tab = tabs[tabId] +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 + + redrawMenu() +end) + +kernel.hook('multishell_terminate', function(_, eventData) + local tab = kernel.find(eventData[1]) if tab and not tab.isOverview then if coroutine.status(tab.co) ~= 'dead' then - resumeTab(tab, "terminate") + tab:resume("terminate") end end return true @@ -330,10 +218,12 @@ kernel.hook('multishell_redraw', function() parentTerm.clearLine() local function compareTab(a, b) - return a.tabId < b.tabId + return a.uid < b.uid end - for _,tab in pairs(tabs) do + local currentTab = kernel.routines[1] + + for _,tab in pairs(kernel.routines) do if tab.hidden and tab ~= currentTab then tab.width = 0 else @@ -343,18 +233,18 @@ kernel.hook('multishell_redraw', function() local function width() local tw = 0 - Util.each(tabs, function(t) tw = tw + t.width end) + Util.each(kernel.routines, function(t) tw = tw + t.width end) return tw end while width() > w - 3 do local tab = select(2, - Util.spairs(tabs, function(a, b) return a.width > b.width end)()) + Util.spairs(kernel.routines, function(a, b) return a.width > b.width end)()) tab.width = tab.width - 1 end local tabX = 0 - for _,tab in Util.spairs(tabs, compareTab) do + for _,tab in Util.spairs(kernel.routines, compareTab) do if tab.width > 0 then tab.sx = tabX + 1 tab.ex = tabX + tab.width @@ -375,7 +265,7 @@ kernel.hook('multishell_redraw', function() end end - if currentTab then + if currentTab and currentTab.window then currentTab.window.restoreCursor() end @@ -388,8 +278,8 @@ kernel.hook('term_resize', function(_, eventData) local windowHeight = h-1 - for _,key in pairs(Util.keys(tabs)) do - local tab = tabs[key] + for _,key in pairs(Util.keys(kernel.routines)) do + local tab = kernel.routines[key] local x,y = tab.window.getCursorPos() if y > windowHeight then tab.window.scroll(y - windowHeight) @@ -402,69 +292,41 @@ kernel.hook('term_resize', function(_, eventData) end end) ---[[ -kernel.hook('key_up', function(_, eventData) - local code = eventData[1] - if not keyboard.state[code] then - return true - end -end) -]] - kernel.hook('mouse_click', function(_, eventData) local x, y = eventData[2], eventData[3] + local currentTab = kernel.routines[1] + if y == 1 then if x == 1 then multishell.setFocus(overviewId) elseif x == w then if currentTab then - multishell.terminate(currentTab.tabId) + multishell.terminate(currentTab.uid) end else - for _,tab in pairs(tabs) do + for _,tab in pairs(kernel.routines) do if not tab.hidden and tab.sx then if x >= tab.sx and x <= tab.ex then - multishell.setFocus(tab.tabId) + multishell.setFocus(tab.uid) break end end end end - downState.mouse = nil return true end - downState.mouse = currentTab eventData[3] = eventData[3] - 1 end) -kernel.hook({ 'mouse_up', 'mouse_drag' }, function(event, eventData) - if downState.mouse ~= currentTab then - -- don't send mouse up as the mouse click event was on another window - if event == 'mouse_up' then - downState.mouse = nil - end - - return true -- stop propagation - end +kernel.hook({ 'mouse_up', 'mouse_drag' }, function(_, eventData) eventData[3] = eventData[3] - 1 end) kernel.hook('mouse_scroll', function(_, eventData) - local dir, y = eventData[1], eventData[3] - - if y == 1 then + if eventData[3] == 1 then return true end - - if currentTab.terminal.scrollUp then - if dir == -1 then - currentTab.terminal.scrollUp() - else - currentTab.terminal.scrollDown() - end - end - - eventData[3] = y - 1 + eventData[3] = eventData[3] - 1 end) local function startup() @@ -518,44 +380,10 @@ overviewId = multishell.openTab({ path = 'sys/apps/Overview.lua', isOverview = true, }) -tabs[overviewId].title = '+' +kernel.find(overviewId).title = '+' multishell.openTab({ focused = true, fn = startup, title = 'Autorun', }) - -local currentTabEvents = Util.transpose { - 'char', 'key', 'key_up', - 'mouse_click', 'mouse_drag', 'mouse_scroll', 'mouse_up', - 'paste', 'terminate', -} - -while true do - local tEventData = { os.pullEventRaw() } - local sEvent = table.remove(tEventData, 1) - local stopPropagation - - local eventHooks = kernel.hooks[sEvent] - if eventHooks then - for i = #eventHooks, 1, -1 do - stopPropagation = eventHooks[i](sEvent, tEventData) - if stopPropagation then - break - end - end - end - - if not stopPropagation then - if currentTabEvents[sEvent] then - resumeTab(currentTab, sEvent, tEventData) - - else - -- Passthrough to all processes - for _,key in pairs(Util.keys(tabs)) do - resumeTab(tabs[key], sEvent, tEventData) - end - end - end -end diff --git a/sys/apps/shell b/sys/apps/shell index 8871493..6da9069 100644 --- a/sys/apps/shell +++ b/sys/apps/shell @@ -330,7 +330,7 @@ end local tArgs = { ... } if #tArgs > 0 then local env = setmetatable(Util.shallowCopy(sandboxEnv), { __index = _G }) - return run(env, ...) + return run(_ENV, ...) -- maybe _ENV end local Config = require('config') diff --git a/sys/autorun/hotkeys.lua b/sys/autorun/hotkeys.lua index 50ffb65..b6e32d9 100644 --- a/sys/autorun/hotkeys.lua +++ b/sys/autorun/hotkeys.lua @@ -2,6 +2,7 @@ _G.requireInjector() local Util = require('util') +local kernel = _G.kernel local keyboard = _G.device.keyboard local multishell = _ENV.multishell @@ -16,11 +17,10 @@ end) -- restart tab keyboard.addHotkey('control-backspace', function() - local tabs = multishell.getTabs() - local tabId = multishell.getFocus() - local tab = tabs[tabId] + local uid = multishell.getFocus() + local tab = kernel.find(uid) if not tab.isOverview then - multishell.terminate(tabId) + multishell.terminate(uid) tab = Util.shallowCopy(tab) tab.isDead = false tab.focused = true @@ -35,7 +35,7 @@ keyboard.addHotkey('control-tab', function() local currentTabId = multishell.getFocus() local function compareTab(a, b) - return a.tabId < b.tabId + return a.uid < b.uid end for _,tab in Util.spairs(tabs, compareTab) do if not tab.hidden then @@ -44,14 +44,14 @@ keyboard.addHotkey('control-tab', function() end for k,tab in ipairs(visibleTabs) do - if tab.tabId == currentTabId then + if tab.uid == currentTabId then if k < #visibleTabs then - multishell.setFocus(visibleTabs[k + 1].tabId) + multishell.setFocus(visibleTabs[k + 1].uid) return end end end if #visibleTabs > 0 then - multishell.setFocus(visibleTabs[1].tabId) + multishell.setFocus(visibleTabs[1].uid) end end) diff --git a/sys/boot/multishell.boot b/sys/boot/opus.boot similarity index 52% rename from sys/boot/multishell.boot rename to sys/boot/opus.boot index 9cf0e7e..3ccd0fe 100644 --- a/sys/boot/multishell.boot +++ b/sys/boot/opus.boot @@ -2,6 +2,8 @@ local colors = _G.colors local fs = _G.fs local http = _G.http +local os = _G.os +local shell = _ENV.shell local term = _G.term local w, h = term.getSize() @@ -32,7 +34,7 @@ term.setCursorPos((w - 18) / 2, h) term.write('Loading Opus...') term.setCursorPos(w, h) -local GIT_REPO = 'kepler155c/opus/develop' +local GIT_REPO = 'kepler155c/opus/develop-1.8' local BASE = 'https://raw.githubusercontent.com/' .. GIT_REPO local sandboxEnv = setmetatable({ }, { __index = _G }) @@ -53,7 +55,10 @@ end local function run(file, ...) local s, m = loadfile(file, makeEnv()) if s then - return s(...) + s, m = pcall(s, ...) + if s then + return m + end end error('Error loading ' .. file .. '\n' .. m) end @@ -80,13 +85,70 @@ if fs.exists('sys/apis/injector.lua') then else -- not local, run the file system directly from git _G.requireInjector = runUrl('/sys/apis/injector.lua') - runUrl('/sys/extensions/vfs.lua') + runUrl('sys/extensions/vfs.lua') -- install file system fs.mount('', 'gitfs', GIT_REPO) end -run('sys/kernel.lua') +_G.requireInjector() +local Util = require('util') + +-- Default label +if not os.getComputerLabel() then + local id = os.getComputerID() + if _G.turtle then + os.setComputerLabel('turtle_' .. id) + elseif _G.pocket then + os.setComputerLabel('pocket_' .. id) + elseif _G.commands then + os.setComputerLabel('command_' .. id) + else + os.setComputerLabel('computer_' .. id) + end +end + +-- user environment +if not fs.exists('usr/apps') then + fs.makeDir('usr/apps') +end +if not fs.exists('usr/autorun') then + fs.makeDir('usr/autorun') +end +if not fs.exists('usr/etc/fstab') then + Util.writeFile('usr/etc/fstab', 'usr gitfs kepler155c/opus-apps/develop-1.8') +end +if not fs.exists('usr/config/shell') then + Util.writeTable('usr/config/shell', { + aliases = shell.aliases(), + path = 'usr/apps:sys/apps:' .. shell.path(), + lua_path = '/sys/apis:/usr/apis', + }) +end + +-- shell environment +local config = Util.readTable('usr/config/shell') +if config.aliases then + for k in pairs(shell.aliases()) do + shell.clearAlias(k) + end + for k,v in pairs(config.aliases) do + shell.setAlias(k, v) + end +end +shell.setPath(config.path) +sandboxEnv.LUA_PATH = config.lua_path + +Util.run(makeEnv(), 'sys/kernel.lua') + +-- extensions +local dir = 'sys/extensions' +for _,file in ipairs(fs.list(dir)) do + local s, m = Util.run(makeEnv(), 'sys/apps/shell', fs.combine(dir, file)) + if not s then + error(m) + end +end -- install user file systems fs.loadTab('usr/etc/fstab') @@ -97,7 +159,25 @@ if args[1] then term.clear() term.setCursorPos(1, 1) end + +local cterm = term.current() + args[1] = args[1] or 'sys/apps/multishell' -run('sys/apps/shell', table.unpack(args)) +local routine = kernel.newRoutine({ + path = 'sys/apps/shell', + args = args + }) +kernel.run(routine) +local s, m = pcall(kernel.start) + +term.redirect(cterm) +term.setBackgroundColor(colors.black) +term.setTextColor(colors.white) +term.clear() +term.setCursorPos(1, 1) +if not s then + print('\nCrash detected\n') + _G.printError(m) +end fs.restore() diff --git a/sys/etc/app.db b/sys/etc/app.db index 00b6b27..f96b432 100644 --- a/sys/etc/app.db +++ b/sys/etc/app.db @@ -165,7 +165,7 @@ run = "Events.lua", }, [ "2a4d562b1d9a9c90bdede6fac8ce4f7402462b86" ] = { - title = "Tabs", + title = "Tasks", category = "System", icon = "\0307 \0303\0317__\0307\031 \ \0303 \ diff --git a/sys/extensions/device.lua b/sys/extensions/device.lua index d1c7f3f..28db335 100644 --- a/sys/extensions/device.lua +++ b/sys/extensions/device.lua @@ -30,6 +30,7 @@ local Util = require('util') local device = _G.device local kernel = _G.kernel local keyboard = _G.device.keyboard +local mouse = _G.device.mouse local os = _G.os kernel.hook('peripheral', function(_, eventData) @@ -74,6 +75,23 @@ kernel.hook({ 'key', 'key_up', 'char', 'paste' }, function(event, eventData) end end) +kernel.hook({ 'mouse_click', 'mouse_up', 'mouse_drag' }, function(event, eventData) + local button = eventData[1] + if event == 'mouse_click' then + mouse.state[button] = true + else + if not mouse.state[button] then + return true -- ensure mouse ups are only generated if a mouse down was sent + end + mouse.state[button] = nil + end +end) + +kernel.hook('kernel_focus', function() + Util.clear(keyboard.state) + Util.clear(mouse.state) +end) + function keyboard.addHotkey(code, fn) keyboard.hotkeys[code] = fn end diff --git a/sys/extensions/network.lua b/sys/extensions/network.lua index d6fce09..450be0b 100644 --- a/sys/extensions/network.lua +++ b/sys/extensions/network.lua @@ -1,16 +1,13 @@ local kernel = _G.kernel -local multishell = _ENV.multishell _G.network = { } kernel.hook('device_attach', function(_, eventData) if eventData[1] == 'wireless_modem' then - local s, m = multishell.openTab({ + local routine = kernel.newRoutine({ path = 'sys/services/network.lua', hidden = true }) - if not s and m then - debug(m) - end + kernel.run(routine) end end) diff --git a/sys/extensions/vfs.lua b/sys/extensions/vfs.lua index 5283403..8a29213 100644 --- a/sys/extensions/vfs.lua +++ b/sys/extensions/vfs.lua @@ -295,8 +295,9 @@ function fs.loadTab(path) fs.mount(table.unpack(Util.matches(l))) end) if not s then - printError('Mount failed') - printError(l) + _G.printError('Mount failed') + _G.printError(l) + _G.printError(m) end end end diff --git a/sys/kernel.lua b/sys/kernel.lua index e97a3d2..8b7f1ed 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -1,4 +1,4 @@ -local sandboxEnv = setmetatable({ }, { __index = _G }) +local sandboxEnv = { } for k,v in pairs(_ENV) do sandboxEnv[k] = v end @@ -8,43 +8,21 @@ _G.requireInjector() local Util = require('util') _G.kernel = { - hooks = { } + UID = 0, + hooks = { }, + routines = { }, + terminal = _G.term.current(), } local kernel = _G.kernel -local fs = _G.fs -local shell = _ENV.shell +local os = _G.os +local term = _G.term --- user environment -if not fs.exists('usr/apps') then - fs.makeDir('usr/apps') -end -if not fs.exists('usr/autorun') then - fs.makeDir('usr/autorun') -end -if not fs.exists('usr/etc/fstab') then - Util.writeFile('usr/etc/fstab', 'usr gitfs kepler155c/opus-apps/develop') -end -if not fs.exists('usr/config/shell') then - Util.writeTable('usr/config/shell', { - aliases = shell.aliases(), - path = 'usr/apps:sys/apps:' .. shell.path(), - lua_path = '/sys/apis:/usr/apis', - }) -end - --- shell environment -local config = Util.readTable('usr/config/shell') -if config.aliases then - for k in pairs(shell.aliases()) do - shell.clearAlias(k) - end - for k,v in pairs(config.aliases) do - shell.setAlias(k, v) - end -end -shell.setPath(config.path) -_G.LUA_PATH = config.lua_path +local focusedRoutineEvents = Util.transpose { + 'char', 'key', 'key_up', + 'mouse_click', 'mouse_drag', 'mouse_scroll', 'mouse_up', + 'paste', 'terminate', +} -- any function that runs in a kernel hook does not run in -- a separate coroutine or have a window. an error in a hook @@ -73,11 +51,158 @@ function kernel.unhook(event, fn) end end --- extensions -local dir = 'sys/extensions' -for _,file in ipairs(fs.list(dir)) do - local s, m = Util.run(sandboxEnv, 'sys/apps/shell', fs.combine(dir, file)) - if not s then - error(m) +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 + 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 + + self.terminal = term.current() + if ok then + self.filter = result + else + _G.printError(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 + end + return ok, result end end + +function kernel.getFocused() + return kernel.routines[1] +end + +function kernel.getCurrent() + return kernel.running +end + +function kernel.newRoutine(args) + kernel.UID = kernel.UID + 1 + + args = args or { } + + local routine = setmetatable(args, { __index = Routine }) + routine.uid = kernel.UID + + return routine +end + +function kernel.run(routine) + routine.timestamp = os.clock() + routine.terminal = routine.terminal or kernel.terminal + routine.window = routine.window or kernel.window + routine.env = Util.shallowCopy(routine.env or sandboxEnv) + + 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 and err ~= 'Terminated' then + _G.printError(tostring(err)) + end + end) + + table.insert(kernel.routines, routine) + + local previousTerm = term.current() + local s, m = routine:resume() + term.redirect(previousTerm) + + return s, m +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 + 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 + local s, m = pcall(function() + stopPropagation = eventHooks[i](event, eventData) + end) + if not s then + error(m) + end + 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() + repeat + local eventData = { os.pullEventRaw() } + local event = table.remove(eventData, 1) + kernel.event(event, eventData) + until event == 'kernel_halt' or not kernel.routines[1] +end diff --git a/sys/services/gpshost.lua b/sys/services/gpshost.lua index c4380f9..c06f4be 100644 --- a/sys/services/gpshost.lua +++ b/sys/services/gpshost.lua @@ -1,4 +1,4 @@ -if device.wireless_modem then +if _G.device.wireless_modem then _G.requireInjector() local Config = require('config') @@ -7,11 +7,8 @@ if device.wireless_modem then Config.load('gps', config) if config.host and type(config.host) == 'table' then - - multishell.setTitle(multishell.getCurrent(), 'GPS Daemon') - - os.run(getfenv(1), '/rom/programs/gps', 'host', config.host.x, config.host.y, config.host.z) - + _ENV._APP_TITLE = 'GPS Daemon' + os.run(_ENV, '/rom/programs/gps', 'host', config.host.x, config.host.y, config.host.z) print('GPS daemon stopped') end end diff --git a/sys/services/log.lua b/sys/services/log.lua index 7284da6..1ba194c 100644 --- a/sys/services/log.lua +++ b/sys/services/log.lua @@ -3,26 +3,41 @@ _G.requireInjector() local Terminal = require('terminal') local Util = require('util') +local kernel = _G.kernel local keyboard = _G.device.keyboard local multishell = _ENV.multishell local os = _G.os local term = _G.term -multishell.setTitle(multishell.getCurrent(), 'Debug') +_ENV._APP_TITLE = 'Debug' term.redirect(Terminal.scrollable(term.current(), 50)) local tabId = multishell.getCurrent() -local terminal = term.current() local previousId _G.debug = function(pattern, ...) local oldTerm = term.current() - term.redirect(terminal) + term.redirect(kernel.terminal) Util.print(pattern, ...) term.redirect(oldTerm) end +kernel.hook('mouse_scroll', function(_, eventData) + local dir, y = eventData[1], eventData[3] + + if y > 1 then + local currentTab = kernel.routines[1] + if currentTab.terminal.scrollUp then + if dir == -1 then + currentTab.terminal.scrollUp() + else + currentTab.terminal.scrollDown() + end + end + end +end) + print('Debug started') print('Press ^d to activate debug window') diff --git a/sys/services/network.lua b/sys/services/network.lua index 98420b1..3f32c0b 100644 --- a/sys/services/network.lua +++ b/sys/services/network.lua @@ -5,7 +5,6 @@ local Util = require('util') local device = _G.device local fs = _G.fs -local multishell = _ENV.multishell local network = _G.network local os = _G.os local printError = _G.printError @@ -14,7 +13,7 @@ if not device.wireless_modem then return end -multishell.setTitle(multishell.getCurrent(), 'Net Daemon') +_ENV._APP_TITLE = 'Net Daemon' print('Net daemon started') diff --git a/sys/services/transort.lua b/sys/services/transort.lua index c163509..9651504 100644 --- a/sys/services/transort.lua +++ b/sys/services/transort.lua @@ -6,10 +6,9 @@ * background read buffering ]]-- -local multishell = _ENV.multishell local os = _G.os -multishell.setTitle(multishell.getCurrent(), 'Net transport') +_ENV._APP_TITLE = 'Net transport' local computerId = os.getComputerID()