mirror of
https://github.com/kepler155c/opus
synced 2025-05-01 15:04:13 +00:00
move multishell functionality to kernel
This commit is contained in:
parent
13ec8ea04f
commit
d224f5df25
@ -58,33 +58,37 @@ local function nextUID()
|
||||
return Event.uid - 1
|
||||
end
|
||||
|
||||
function Event.on(event, fn)
|
||||
function Event.on(events, fn)
|
||||
events = type(events) == 'table' and events or { events }
|
||||
|
||||
local handler = setmetatable({
|
||||
uid = nextUID(),
|
||||
event = events,
|
||||
fn = fn,
|
||||
}, { __index = Routine })
|
||||
|
||||
for _,event in pairs(events) do
|
||||
local handlers = Event.types[event]
|
||||
if not handlers then
|
||||
handlers = { }
|
||||
Event.types[event] = handlers
|
||||
end
|
||||
|
||||
local handler = {
|
||||
uid = nextUID(),
|
||||
event = event,
|
||||
fn = fn,
|
||||
}
|
||||
handlers[handler.uid] = handler
|
||||
setmetatable(handler, { __index = Routine })
|
||||
end
|
||||
|
||||
return handler
|
||||
end
|
||||
|
||||
function Event.off(h)
|
||||
if h and h.event then
|
||||
Event.types[h.event][h.uid] = nil
|
||||
for _,event in pairs(h.event) do
|
||||
Event.types[event][h.uid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function addTimer(interval, recurring, fn)
|
||||
|
||||
local timerId = os.startTimer(interval)
|
||||
local handler
|
||||
|
||||
@ -133,20 +137,18 @@ function Event.waitForEvent(event, timeout)
|
||||
end
|
||||
|
||||
function Event.addRoutine(fn)
|
||||
local r = {
|
||||
local r = setmetatable({
|
||||
co = coroutine.create(fn),
|
||||
uid = nextUID()
|
||||
}
|
||||
setmetatable(r, { __index = Routine })
|
||||
Event.routines[r.uid] = r
|
||||
}, { __index = Routine })
|
||||
|
||||
Event.routines[r.uid] = r
|
||||
r:resume()
|
||||
|
||||
return r
|
||||
end
|
||||
|
||||
function Event.pullEvents(...)
|
||||
|
||||
for _, fn in ipairs({ ... }) do
|
||||
Event.addRoutine(fn)
|
||||
end
|
||||
@ -162,7 +164,6 @@ function Event.exitPullEvents()
|
||||
end
|
||||
|
||||
local function processHandlers(event)
|
||||
|
||||
local handlers = Event.types[event]
|
||||
if handlers then
|
||||
for _,h in pairs(handlers) do
|
||||
@ -184,7 +185,6 @@ local function tokeys(t)
|
||||
end
|
||||
|
||||
local function processRoutines(...)
|
||||
|
||||
local keys = tokeys(Event.routines)
|
||||
for _,key in ipairs(keys) do
|
||||
local r = Event.routines[key]
|
||||
@ -200,7 +200,6 @@ function Event.processEvent(e)
|
||||
end
|
||||
|
||||
function Event.pullEvent(eventType)
|
||||
|
||||
while true do
|
||||
local e = { os.pullEventRaw() }
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
local Util = require('util')
|
||||
|
||||
local keyboard = _G.device.keyboard
|
||||
local keys = _G.keys
|
||||
local os = _G.os
|
||||
|
||||
@ -10,28 +11,31 @@ local modifiers = Util.transpose {
|
||||
}
|
||||
|
||||
local input = {
|
||||
pressed = { },
|
||||
state = { },
|
||||
}
|
||||
|
||||
function input:modifierPressed()
|
||||
return self.pressed[keys.leftCtrl] or
|
||||
self.pressed[keys.rightCtrl] or
|
||||
self.pressed[keys.leftAlt] or
|
||||
self.pressed[keys.rightAlt]
|
||||
return keyboard.state[keys.leftCtrl] or
|
||||
keyboard.state[keys.rightCtrl] or
|
||||
keyboard.state[keys.leftAlt] or
|
||||
keyboard.state[keys.rightAlt]
|
||||
end
|
||||
|
||||
function input:toCode(ch, code)
|
||||
local result = { }
|
||||
|
||||
if self.pressed[keys.leftCtrl] or self.pressed[keys.rightCtrl] then
|
||||
if keyboard.state[keys.leftCtrl] or keyboard.state[keys.rightCtrl] or
|
||||
code == keys.leftCtrl or code == keys.rightCtrl then
|
||||
table.insert(result, 'control')
|
||||
end
|
||||
|
||||
if self.pressed[keys.leftAlt] or self.pressed[keys.rightAlt] then
|
||||
if keyboard.state[keys.leftAlt] or keyboard.state[keys.rightAlt] or
|
||||
code == keys.leftAlt or code == keys.rightAlt then
|
||||
table.insert(result, 'alt')
|
||||
end
|
||||
|
||||
if self.pressed[keys.leftShift] or self.pressed[keys.rightShift] then
|
||||
if keyboard.state[keys.leftShift] or keyboard.state[keys.rightShift] or
|
||||
code == keys.leftShift or code == keys.rightShift then
|
||||
if code and modifiers[code] then
|
||||
table.insert(result, 'shift')
|
||||
elseif #ch == 1 then
|
||||
@ -48,7 +52,7 @@ function input:toCode(ch, code)
|
||||
end
|
||||
|
||||
function input:reset()
|
||||
self.pressed = { }
|
||||
self.state = { }
|
||||
self.fired = nil
|
||||
|
||||
self.timer = nil
|
||||
@ -64,7 +68,7 @@ function input:translate(event, code, p1, p2)
|
||||
return input:toCode(keys.getName(code), code)
|
||||
end
|
||||
else
|
||||
self.pressed[code] = true
|
||||
self.state[code] = true
|
||||
if self:modifierPressed() and not modifiers[code] or code == 57 then
|
||||
self.fired = true
|
||||
return input:toCode(keys.getName(code), code)
|
||||
@ -81,18 +85,18 @@ function input:translate(event, code, p1, p2)
|
||||
|
||||
elseif event == 'key_up' then
|
||||
if not self.fired then
|
||||
if self.pressed[code] then
|
||||
if self.state[code] then
|
||||
self.fired = true
|
||||
local ch = input:toCode(keys.getName(code), code)
|
||||
self.pressed[code] = nil
|
||||
self.state[code] = nil
|
||||
return ch
|
||||
end
|
||||
end
|
||||
self.pressed[code] = nil
|
||||
self.state[code] = nil
|
||||
|
||||
elseif event == 'paste' then
|
||||
self.pressed[keys.leftCtrl] = nil
|
||||
self.pressed[keys.rightCtrl] = nil
|
||||
--self.state[keys.leftCtrl] = nil
|
||||
--self.state[keys.rightCtrl] = nil
|
||||
self.fired = true
|
||||
return input:toCode('paste', 255)
|
||||
|
||||
|
@ -68,6 +68,7 @@ local function encodeCommon(val, pretty, tabLevel, tTracking)
|
||||
str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
|
||||
end)
|
||||
else
|
||||
debug(val)
|
||||
arrEncoding(val, "{", "}", pairs, function(k,v)
|
||||
assert(type(k) == "string", "JSON object keys must be strings", 2)
|
||||
str = str .. encodeCommon(k, pretty, tabLevel, tTracking)
|
||||
@ -94,6 +95,13 @@ function json.encodePretty(val)
|
||||
return encodeCommon(val, true, 0, {})
|
||||
end
|
||||
|
||||
function json.encodeToFile(path, val)
|
||||
local file = io.open(path, "w")
|
||||
assert(file, "Unable to open file")
|
||||
file:write(json.encodePretty(val))
|
||||
file:close()
|
||||
end
|
||||
|
||||
------------------------------------------------------------------ decoding
|
||||
|
||||
local decodeControls = {}
|
||||
|
@ -2,6 +2,8 @@ local Event = require('event')
|
||||
local Socket = require('socket')
|
||||
local Util = require('util')
|
||||
|
||||
local os = _G.os
|
||||
|
||||
local Peripheral = Util.shallowCopy(_G.peripheral)
|
||||
|
||||
function Peripheral.getList()
|
||||
@ -176,12 +178,12 @@ local function getProxy(pi)
|
||||
if proxy.type == 'monitor' then
|
||||
Event.addRoutine(function()
|
||||
while true do
|
||||
local event = socket:read()
|
||||
if not event then
|
||||
local data = socket:read()
|
||||
if not data then
|
||||
break
|
||||
end
|
||||
if not Util.empty(event) then
|
||||
os.queueEvent(table.unpack(event))
|
||||
if data.fn and data.fn == 'event' then
|
||||
os.queueEvent(table.unpack(data.data))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
@ -43,26 +43,23 @@ end
|
||||
--[[-- Top Level Manager --]]--
|
||||
local Manager = class()
|
||||
function Manager:init()
|
||||
local running = false
|
||||
local function keyFunction(event, code, held)
|
||||
local ch = Input:translate(event, code, held)
|
||||
|
||||
-- single thread all input events
|
||||
local function singleThread(event, fn)
|
||||
Event.on(event, function(_, ...)
|
||||
if not running then
|
||||
running = true
|
||||
fn(...)
|
||||
running = false
|
||||
else
|
||||
Input:translate(event, ...)
|
||||
if ch and self.currentPage then
|
||||
local target = self.currentPage.focused or self.currentPage
|
||||
self:inputEvent(target,
|
||||
{ type = 'key', key = ch, element = target })
|
||||
self.currentPage:sync()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
Event.on('multishell_focus', function()
|
||||
Input:reset()
|
||||
end)
|
||||
local handlers = {
|
||||
char = keyFunction,
|
||||
key_up = keyFunction,
|
||||
key = keyFunction,
|
||||
|
||||
singleThread('term_resize', function(side)
|
||||
term_resize = function(_, side)
|
||||
if self.currentPage then
|
||||
-- the parent doesn't have any children set...
|
||||
-- that's why we have to resize both the parent and the current page
|
||||
@ -75,9 +72,9 @@ function Manager:init()
|
||||
self.currentPage:sync()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end,
|
||||
|
||||
singleThread('mouse_scroll', function(direction, x, y)
|
||||
mouse_scroll = function(_, direction, x, y)
|
||||
if self.currentPage then
|
||||
local event = self.currentPage:pointToChild(x, y)
|
||||
local directions = {
|
||||
@ -90,18 +87,20 @@ function Manager:init()
|
||||
{ type = 'key', key = directions[direction] })
|
||||
self.currentPage:sync()
|
||||
end
|
||||
end)
|
||||
end,
|
||||
|
||||
-- this should be moved to the device !
|
||||
singleThread('monitor_touch', function(side, x, y)
|
||||
monitor_touch = function(_, side, x, y)
|
||||
Input:translate('mouse_click', 1, x, y)
|
||||
local ch = Input:translate('mouse_up', 1, x, y)
|
||||
if self.currentPage then
|
||||
if self.currentPage.parent.device.side == side then
|
||||
self:click('mouse_click', 1, x, y)
|
||||
self:click(ch, 1, x, y)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end,
|
||||
|
||||
singleThread('mouse_click', function(button, x, y)
|
||||
mouse_click = function(_, button, x, y)
|
||||
Input:translate('mouse_click', button, x, y)
|
||||
|
||||
if self.currentPage then
|
||||
@ -113,9 +112,9 @@ function Manager:init()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end,
|
||||
|
||||
singleThread('mouse_up', function(button, x, y)
|
||||
mouse_up = function(_, button, x, y)
|
||||
local ch = Input:translate('mouse_up', button, x, y)
|
||||
|
||||
if ch == 'control-shift-mouse_click' then -- hack
|
||||
@ -130,9 +129,9 @@ function Manager:init()
|
||||
self:click(ch, button, x, y)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end,
|
||||
|
||||
singleThread('mouse_drag', function(button, x, y)
|
||||
mouse_drag = function(_, button, x, y)
|
||||
local ch = Input:translate('mouse_drag', button, x, y)
|
||||
if ch and self.currentPage then
|
||||
local event = self.currentPage:pointToChild(x, y)
|
||||
@ -140,28 +139,23 @@ function Manager:init()
|
||||
self:inputEvent(event.element, event)
|
||||
self.currentPage:sync()
|
||||
end
|
||||
end)
|
||||
end,
|
||||
|
||||
singleThread('paste', function(text)
|
||||
paste = function(_, text)
|
||||
Input:translate('paste')
|
||||
self:emitEvent({ type = 'paste', text = text })
|
||||
self.currentPage:sync()
|
||||
end,
|
||||
}
|
||||
|
||||
-- use 1 handler to single thread all events
|
||||
Event.on({
|
||||
'char', 'key_up', 'key', 'term_resize',
|
||||
'mouse_scroll', 'monitor_touch', 'mouse_click',
|
||||
'mouse_up', 'mouse_drag', 'paste' },
|
||||
function(event, ...)
|
||||
handlers[event](event, ...)
|
||||
end)
|
||||
|
||||
local function keyFunction(event, code, held)
|
||||
local ch = Input:translate(event, code, held)
|
||||
|
||||
if ch and self.currentPage then
|
||||
local target = self.currentPage.focused or self.currentPage
|
||||
self:inputEvent(target,
|
||||
{ type = 'key', key = ch, element = target })
|
||||
self.currentPage:sync()
|
||||
end
|
||||
end
|
||||
|
||||
singleThread('char', function(code, held) keyFunction('char', code, held) end)
|
||||
singleThread('key_up', function(code, held) keyFunction('key_up', code, held) end)
|
||||
singleThread('key', function(code, held) keyFunction('key', code, held) end)
|
||||
end
|
||||
|
||||
function Manager:configure(appName, ...)
|
||||
|
@ -460,6 +460,8 @@ function Util.toBytes(n)
|
||||
if not tonumber(n) then error('Util.toBytes: n must be a number', 2) end
|
||||
if n >= 1000000 or n <= -1000000 then
|
||||
return string.format('%sM', math.floor(n/1000000 * 10) / 10)
|
||||
elseif n >= 10000 or n <= -10000 then
|
||||
return string.format('%sK', math.floor(n/1000))
|
||||
elseif n >= 1000 or n <= -1000 then
|
||||
return string.format('%sK', math.floor(n/1000 * 10) / 10)
|
||||
end
|
||||
@ -549,7 +551,6 @@ end
|
||||
-- end word wrapping
|
||||
|
||||
function Util.wordWrap(str, limit)
|
||||
|
||||
local longLines = Util.split(str)
|
||||
local lines = { }
|
||||
|
||||
@ -560,6 +561,23 @@ function Util.wordWrap(str, limit)
|
||||
return lines
|
||||
end
|
||||
|
||||
function Util.args(arg)
|
||||
local tab = { remainder = { } }
|
||||
|
||||
local k = 1
|
||||
while k <= #arg do
|
||||
local v = arg[k]
|
||||
if string.sub(v, 1, 1) == '-' then
|
||||
local jopt = string.sub(v, 2)
|
||||
tab[ jopt ] = arg[ k + 1 ]
|
||||
k = k + 1
|
||||
else
|
||||
table.insert(tab.remainder, v)
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
return tab
|
||||
end
|
||||
-- http://lua-users.org/wiki/AlternativeGetOpt
|
||||
local function getopt( arg, options )
|
||||
local tab = {}
|
||||
|
@ -6,11 +6,12 @@ end
|
||||
_G.requireInjector()
|
||||
|
||||
local Config = require('config')
|
||||
local Input = require('input')
|
||||
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
|
||||
@ -28,8 +29,6 @@ local overviewId
|
||||
local runningTab
|
||||
local tabsDirty = false
|
||||
local closeInd = '*'
|
||||
local hooks = { }
|
||||
local hotkeys = { }
|
||||
local downState = { }
|
||||
|
||||
multishell.term = term.current()
|
||||
@ -124,11 +123,11 @@ local function selectTab(tab)
|
||||
|
||||
if currentTab and currentTab ~= tab then
|
||||
currentTab.window.setVisible(false)
|
||||
if coroutine.status(currentTab.co) == 'suspended' then
|
||||
--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
|
||||
--end
|
||||
if tab and not currentTab.hidden then
|
||||
tab.previousTabId = currentTab.tabId
|
||||
end
|
||||
@ -137,7 +136,9 @@ local function selectTab(tab)
|
||||
if tab ~= currentTab then
|
||||
currentTab = tab
|
||||
tab.window.setVisible(true)
|
||||
resumeTab(tab, 'multishell_focus')
|
||||
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
|
||||
end
|
||||
|
||||
@ -197,14 +198,6 @@ local function launchProcess(tab)
|
||||
return tab
|
||||
end
|
||||
|
||||
function multishell.addHotkey(code, fn)
|
||||
hotkeys[code] = fn
|
||||
end
|
||||
|
||||
function multishell.removeHotkey(code)
|
||||
hotkeys[code] = nil
|
||||
end
|
||||
|
||||
function multishell.getFocus()
|
||||
return currentTab.tabId
|
||||
end
|
||||
@ -309,31 +302,7 @@ function multishell.getCount()
|
||||
return Util.size(tabs)
|
||||
end
|
||||
|
||||
function multishell.hook(event, fn)
|
||||
if type(event) == 'table' then
|
||||
for _,v in pairs(event) do
|
||||
multishell.hook(v, fn)
|
||||
end
|
||||
else
|
||||
if not hooks[event] then
|
||||
hooks[event] = { }
|
||||
end
|
||||
table.insert(hooks[event], fn)
|
||||
end
|
||||
end
|
||||
|
||||
-- you can only unhook from within the function that hooked
|
||||
function multishell.unhook(event, fn)
|
||||
local eventHooks = hooks[event]
|
||||
if eventHooks then
|
||||
Util.removeByValue(eventHooks, fn)
|
||||
if #eventHooks == 0 then
|
||||
hooks[event] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
multishell.hook('multishell_terminate', function(_, eventData)
|
||||
kernel.hook('multishell_terminate', function(_, eventData)
|
||||
local tabId = eventData[1] or -1
|
||||
local tab = tabs[tabId]
|
||||
|
||||
@ -345,7 +314,7 @@ multishell.hook('multishell_terminate', function(_, eventData)
|
||||
return true
|
||||
end)
|
||||
|
||||
multishell.hook('multishell_redraw', function()
|
||||
kernel.hook('multishell_redraw', function()
|
||||
tabsDirty = false
|
||||
|
||||
local function write(x, text, bg, fg)
|
||||
@ -413,7 +382,7 @@ multishell.hook('multishell_redraw', function()
|
||||
return true
|
||||
end)
|
||||
|
||||
multishell.hook('term_resize', function(_, eventData)
|
||||
kernel.hook('term_resize', function(_, eventData)
|
||||
if not eventData[1] then --- TEST
|
||||
w,h = parentTerm.getSize()
|
||||
|
||||
@ -433,40 +402,16 @@ multishell.hook('term_resize', function(_, eventData)
|
||||
end
|
||||
end)
|
||||
|
||||
-- downstate should be stored in the tab (maybe)
|
||||
multishell.hook('key_up', function(_, eventData)
|
||||
--[[
|
||||
kernel.hook('key_up', function(_, eventData)
|
||||
local code = eventData[1]
|
||||
|
||||
if downState[code] ~= currentTab then
|
||||
downState[code] = nil
|
||||
if not keyboard.state[code] then
|
||||
return true
|
||||
end
|
||||
downState[code] = nil
|
||||
end)
|
||||
]]
|
||||
|
||||
multishell.hook('key', function(_, eventData)
|
||||
local code = eventData[1]
|
||||
local firstPress = not eventData[2]
|
||||
|
||||
if firstPress then
|
||||
downState[code] = currentTab
|
||||
else
|
||||
--key was pressed initially in a previous window
|
||||
if downState[code] ~= currentTab then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
multishell.hook({ 'key', 'key_up', 'char', 'paste' }, function(event, eventData)
|
||||
local code = Input:translate(event, eventData[1], eventData[2])
|
||||
|
||||
if code and hotkeys[code] then
|
||||
hotkeys[code](event, eventData)
|
||||
end
|
||||
end)
|
||||
|
||||
multishell.hook('mouse_click', function(_, eventData)
|
||||
kernel.hook('mouse_click', function(_, eventData)
|
||||
local x, y = eventData[2], eventData[3]
|
||||
if y == 1 then
|
||||
if x == 1 then
|
||||
@ -492,7 +437,7 @@ multishell.hook('mouse_click', function(_, eventData)
|
||||
eventData[3] = eventData[3] - 1
|
||||
end)
|
||||
|
||||
multishell.hook({ 'mouse_up', 'mouse_drag' }, function(event, eventData)
|
||||
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
|
||||
@ -504,7 +449,7 @@ multishell.hook({ 'mouse_up', 'mouse_drag' }, function(event, eventData)
|
||||
eventData[3] = eventData[3] - 1
|
||||
end)
|
||||
|
||||
multishell.hook('mouse_scroll', function(_, eventData)
|
||||
kernel.hook('mouse_scroll', function(_, eventData)
|
||||
local dir, y = eventData[1], eventData[3]
|
||||
|
||||
if y == 1 then
|
||||
@ -592,7 +537,7 @@ while true do
|
||||
local sEvent = table.remove(tEventData, 1)
|
||||
local stopPropagation
|
||||
|
||||
local eventHooks = hooks[sEvent]
|
||||
local eventHooks = kernel.hooks[sEvent]
|
||||
if eventHooks then
|
||||
for i = #eventHooks, 1, -1 do
|
||||
stopPropagation = eventHooks[i](sEvent, tEventData)
|
||||
|
@ -5,21 +5,25 @@ local Socket = require('socket')
|
||||
local Terminal = require('terminal')
|
||||
local Util = require('util')
|
||||
|
||||
local multishell = _ENV.multishell
|
||||
local os = _G.os
|
||||
local read = _G.read
|
||||
local term = _G.term
|
||||
|
||||
local remoteId
|
||||
local args = { ... }
|
||||
if #args == 1 then
|
||||
remoteId = tonumber(args[1])
|
||||
else
|
||||
local args = Util.args({ ... })
|
||||
|
||||
local remoteId = tonumber(table.remove(args.remainder, 1))
|
||||
if not remoteId then
|
||||
print('Enter host ID')
|
||||
remoteId = tonumber(read())
|
||||
end
|
||||
|
||||
if not remoteId then
|
||||
error('Syntax: telnet <host ID>')
|
||||
error('Syntax: telnet [-title TITLE] ID [PROGRAM]')
|
||||
end
|
||||
|
||||
if args.title then
|
||||
multishell.setTitle(multishell.getCurrent(), args.title)
|
||||
end
|
||||
|
||||
print('connecting...')
|
||||
@ -39,6 +43,7 @@ socket:write({
|
||||
width = w,
|
||||
height = h,
|
||||
isColor = ct.isColor(),
|
||||
program = args.remainder,
|
||||
})
|
||||
|
||||
Event.addRoutine(function()
|
||||
@ -48,7 +53,7 @@ Event.addRoutine(function()
|
||||
break
|
||||
end
|
||||
for _,v in ipairs(data) do
|
||||
ct[v.f](unpack(v.args))
|
||||
ct[v.f](table.unpack(v.args))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
@ -2,16 +2,17 @@ _G.requireInjector()
|
||||
|
||||
local Util = require('util')
|
||||
|
||||
local multishell = _ENV.multishell
|
||||
local kernel = _G.kernel
|
||||
local keyboard = _G.device.keyboard
|
||||
local textutils = _G.textutils
|
||||
|
||||
local data
|
||||
|
||||
multishell.hook('clipboard_copy', function(_, args)
|
||||
kernel.hook('clipboard_copy', function(_, args)
|
||||
data = args[1]
|
||||
end)
|
||||
|
||||
multishell.addHotkey('shift-paste', function(_, args)
|
||||
keyboard.addHotkey('shift-paste', function(_, args)
|
||||
if type(data) == 'table' then
|
||||
local s, m = pcall(textutils.serialize, data)
|
||||
data = (s and m) or Util.tostring(data)
|
||||
|
@ -2,10 +2,11 @@ _G.requireInjector()
|
||||
|
||||
local Util = require('util')
|
||||
|
||||
local keyboard = _G.device.keyboard
|
||||
local multishell = _ENV.multishell
|
||||
|
||||
-- overview
|
||||
multishell.addHotkey('control-o', function()
|
||||
keyboard.addHotkey('control-o', function()
|
||||
for _,tab in pairs(multishell.getTabs()) do
|
||||
if tab.isOverview then
|
||||
multishell.setFocus(tab.tabId)
|
||||
@ -14,7 +15,7 @@ multishell.addHotkey('control-o', function()
|
||||
end)
|
||||
|
||||
-- restart tab
|
||||
multishell.addHotkey('control-backspace', function()
|
||||
keyboard.addHotkey('control-backspace', function()
|
||||
local tabs = multishell.getTabs()
|
||||
local tabId = multishell.getFocus()
|
||||
local tab = tabs[tabId]
|
||||
@ -28,7 +29,7 @@ multishell.addHotkey('control-backspace', function()
|
||||
end)
|
||||
|
||||
-- next tab
|
||||
multishell.addHotkey('control-tab', function()
|
||||
keyboard.addHotkey('control-tab', function()
|
||||
local tabs = multishell.getTabs()
|
||||
local visibleTabs = { }
|
||||
local currentTabId = multishell.getFocus()
|
||||
|
@ -2,7 +2,6 @@
|
||||
local colors = _G.colors
|
||||
local fs = _G.fs
|
||||
local http = _G.http
|
||||
local shell = _ENV.shell
|
||||
local term = _G.term
|
||||
|
||||
local w, h = term.getSize()
|
||||
@ -40,6 +39,7 @@ local sandboxEnv = setmetatable({ }, { __index = _G })
|
||||
for k,v in pairs(_ENV) do
|
||||
sandboxEnv[k] = v
|
||||
end
|
||||
sandboxEnv.multishell = _ENV.multishell or { }
|
||||
|
||||
local function makeEnv()
|
||||
local env = setmetatable({ }, { __index = _G })
|
||||
@ -86,44 +86,7 @@ else
|
||||
fs.mount('', 'gitfs', GIT_REPO)
|
||||
end
|
||||
|
||||
local Util = run('sys/apis/util.lua')
|
||||
|
||||
-- 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)
|
||||
sandboxEnv.LUA_PATH = config.lua_path
|
||||
|
||||
-- extensions
|
||||
local dir = 'sys/extensions'
|
||||
for _,file in ipairs(fs.list(dir)) do
|
||||
run('sys/apps/shell', fs.combine(dir, file))
|
||||
end
|
||||
run('sys/kernel.lua')
|
||||
|
||||
-- install user file systems
|
||||
fs.loadTab('usr/etc/fstab')
|
||||
|
@ -4,8 +4,88 @@ local Peripheral = require('peripheral')
|
||||
|
||||
_G.device = Peripheral.getList()
|
||||
|
||||
-- register the main term in the devices list
|
||||
_G.device.terminal = _G.term.current()
|
||||
_G.device.terminal.side = 'terminal'
|
||||
_G.device.terminal.type = 'terminal'
|
||||
_G.device.terminal.name = 'terminal'
|
||||
|
||||
_G.device.keyboard = {
|
||||
side = 'keyboard',
|
||||
type = 'keyboard',
|
||||
name = 'keyboard',
|
||||
hotkeys = { },
|
||||
state = { },
|
||||
}
|
||||
|
||||
_G.device.mouse = {
|
||||
side = 'mouse',
|
||||
type = 'mouse',
|
||||
name = 'mouse',
|
||||
state = { },
|
||||
}
|
||||
|
||||
local Input = require('input')
|
||||
local Util = require('util')
|
||||
|
||||
local device = _G.device
|
||||
local kernel = _G.kernel
|
||||
local keyboard = _G.device.keyboard
|
||||
local os = _G.os
|
||||
|
||||
kernel.hook('peripheral', function(_, eventData)
|
||||
local side = eventData[1]
|
||||
if side then
|
||||
local dev = Peripheral.addDevice(device, side)
|
||||
if dev then
|
||||
os.queueEvent('device_attach', dev.name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
kernel.hook('peripheral_detach', function(_, eventData)
|
||||
local side = eventData[1]
|
||||
if side then
|
||||
local dev = Util.find(device, 'side', side)
|
||||
if dev then
|
||||
os.queueEvent('device_detach', dev.name)
|
||||
device[dev.name] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
kernel.hook({ 'key', 'key_up', 'char', 'paste' }, function(event, eventData)
|
||||
local code = eventData[1]
|
||||
|
||||
-- maintain global keyboard state
|
||||
if event == 'key' then
|
||||
keyboard.state[code] = true
|
||||
elseif event == 'key_up' then
|
||||
if not keyboard.state[code] then
|
||||
return true -- ensure key ups are only generated if a key down was sent
|
||||
end
|
||||
keyboard.state[code] = nil
|
||||
end
|
||||
|
||||
-- and fire hotkeys
|
||||
local hotkey = Input:translate(event, eventData[1], eventData[2])
|
||||
|
||||
if hotkey and keyboard.hotkeys[hotkey] then
|
||||
keyboard.hotkeys[hotkey](event, eventData)
|
||||
end
|
||||
end)
|
||||
|
||||
function keyboard.addHotkey(code, fn)
|
||||
keyboard.hotkeys[code] = fn
|
||||
end
|
||||
|
||||
function keyboard.removeHotkey(code)
|
||||
keyboard.hotkeys[code] = nil
|
||||
end
|
||||
|
||||
kernel.hook('monitor_touch', function(event, eventData)
|
||||
local monitor = Peripheral.getBySide(eventData[1])
|
||||
if monitor and monitor.eventChannel then
|
||||
monitor.eventChannel(event, table.unpack(eventData))
|
||||
return true -- stop propagation
|
||||
end
|
||||
end)
|
||||
|
16
sys/extensions/network.lua
Normal file
16
sys/extensions/network.lua
Normal file
@ -0,0 +1,16 @@
|
||||
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({
|
||||
path = 'sys/services/network.lua',
|
||||
hidden = true
|
||||
})
|
||||
if not s and m then
|
||||
debug(m)
|
||||
end
|
||||
end
|
||||
end)
|
83
sys/kernel.lua
Normal file
83
sys/kernel.lua
Normal file
@ -0,0 +1,83 @@
|
||||
local sandboxEnv = setmetatable({ }, { __index = _G })
|
||||
for k,v in pairs(_ENV) do
|
||||
sandboxEnv[k] = v
|
||||
end
|
||||
|
||||
_G.requireInjector()
|
||||
|
||||
local Util = require('util')
|
||||
|
||||
_G.kernel = {
|
||||
hooks = { }
|
||||
}
|
||||
|
||||
local kernel = _G.kernel
|
||||
local fs = _G.fs
|
||||
local shell = _ENV.shell
|
||||
|
||||
-- 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
|
||||
|
||||
-- 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 can only unhook from within the function that hooked
|
||||
function kernel.unhook(event, fn)
|
||||
local eventHooks = kernel.hooks[event]
|
||||
if eventHooks then
|
||||
Util.removeByValue(eventHooks, fn)
|
||||
if #eventHooks == 0 then
|
||||
kernel.hooks[event] = nil
|
||||
end
|
||||
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)
|
||||
end
|
||||
end
|
@ -5,7 +5,6 @@
|
||||
local Event = require('event')
|
||||
local Peripheral = require('peripheral')
|
||||
local Socket = require('socket')
|
||||
local Util = require('util')
|
||||
|
||||
Event.addRoutine(function()
|
||||
print('peripheral: listening on port 189')
|
||||
@ -19,6 +18,8 @@ Event.addRoutine(function()
|
||||
if uri then
|
||||
local peripheral = Peripheral.lookup(uri)
|
||||
|
||||
-- only 1 proxy of this device can happen at one time
|
||||
-- need to prevent multiple shares
|
||||
if not peripheral then
|
||||
print('peripheral: invalid peripheral ' .. uri)
|
||||
else
|
||||
@ -28,7 +29,7 @@ Event.addRoutine(function()
|
||||
}
|
||||
|
||||
if peripheral.blit then
|
||||
peripheral = Util.shallowCopy(peripheral)
|
||||
--peripheral = Util.shallowCopy(peripheral)
|
||||
peripheral.fastBlit = function(data)
|
||||
for _,v in ipairs(data) do
|
||||
peripheral[v.fn](unpack(v.args))
|
||||
@ -47,12 +48,12 @@ Event.addRoutine(function()
|
||||
socket:write(proxy)
|
||||
|
||||
if proxy.type == 'monitor' then
|
||||
local h
|
||||
h = Event.on('monitor_touch', function(...)
|
||||
if not socket:write({ ... }) then
|
||||
Event.off(h)
|
||||
peripheral.eventChannel = function(...)
|
||||
socket:write({
|
||||
fn = 'event',
|
||||
data = { ... }
|
||||
})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
while true do
|
||||
@ -61,9 +62,14 @@ Event.addRoutine(function()
|
||||
print('peripheral: lost connection from ' .. socket.dhost)
|
||||
break
|
||||
end
|
||||
if peripheral[data.fn] then
|
||||
socket:write({ peripheral[data.fn](table.unpack(data.args)) })
|
||||
end
|
||||
end
|
||||
|
||||
peripheral.eventChannel = nil
|
||||
peripheral.fastBlit = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
@ -48,7 +48,7 @@ local function telnetHost(socket)
|
||||
end
|
||||
|
||||
local shellThread = Event.addRoutine(function()
|
||||
os.run(_ENV, 'sys/apps/shell')
|
||||
os.run(_ENV, 'sys/apps/shell', table.unpack(termInfo.program))
|
||||
Event.exitPullEvents()
|
||||
end)
|
||||
|
||||
|
@ -1,47 +0,0 @@
|
||||
_G.requireInjector()
|
||||
|
||||
local Event = require('event')
|
||||
local Peripheral = require('peripheral')
|
||||
local Util = require('util')
|
||||
|
||||
local colors = _G.colors
|
||||
local device = _G.device
|
||||
local multishell = _ENV.multishell
|
||||
local os = _G.os
|
||||
local term = _G.term
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Devices')
|
||||
|
||||
local attachColor = colors.green
|
||||
local detachColor = colors.red
|
||||
|
||||
if not term.isColor() then
|
||||
attachColor = colors.white
|
||||
detachColor = colors.lightGray
|
||||
end
|
||||
|
||||
Event.on('peripheral', function(_, side)
|
||||
if side then
|
||||
local dev = Peripheral.addDevice(device, side)
|
||||
if dev then
|
||||
term.setTextColor(attachColor)
|
||||
Util.print('[%s] %s attached', dev.side, dev.name)
|
||||
os.queueEvent('device_attach', dev.name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.on('peripheral_detach', function(_, side)
|
||||
if side then
|
||||
local dev = Util.find(device, 'side', side)
|
||||
if dev then
|
||||
term.setTextColor(detachColor)
|
||||
Util.print('[%s] %s detached', dev.side, dev.name)
|
||||
os.queueEvent('device_detach', dev.name)
|
||||
device[dev.name] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
print('waiting for peripheral changes')
|
||||
Event.pullEvents()
|
@ -3,6 +3,7 @@ _G.requireInjector()
|
||||
local Terminal = require('terminal')
|
||||
local Util = require('util')
|
||||
|
||||
local keyboard = _G.device.keyboard
|
||||
local multishell = _ENV.multishell
|
||||
local os = _G.os
|
||||
local term = _G.term
|
||||
@ -25,7 +26,7 @@ end
|
||||
print('Debug started')
|
||||
print('Press ^d to activate debug window')
|
||||
|
||||
multishell.addHotkey('control-d', function()
|
||||
keyboard.addHotkey('control-d', function()
|
||||
local currentId = multishell.getFocus()
|
||||
if currentId ~= tabId then
|
||||
previousId = currentId
|
||||
@ -40,4 +41,4 @@ os.pullEventRaw('terminate')
|
||||
print('Debug stopped')
|
||||
|
||||
_G.debug = function() end
|
||||
multishell.removeHotkey('control-d')
|
||||
keyboard.removeHotkey('control-d')
|
||||
|
@ -1,73 +1,45 @@
|
||||
_G.requireInjector()
|
||||
|
||||
local Event = require('event')
|
||||
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
|
||||
|
||||
local network = { }
|
||||
_G.network = network
|
||||
if not device.wireless_modem then
|
||||
return
|
||||
end
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Net Daemon')
|
||||
|
||||
local function netUp()
|
||||
_G.requireInjector()
|
||||
print('Net daemon started')
|
||||
|
||||
local Event = require('event')
|
||||
|
||||
for _,file in pairs(fs.list('sys/network')) do
|
||||
for _,file in pairs(fs.list('sys/network')) do
|
||||
local fn, msg = Util.run(_ENV, 'sys/network/' .. file)
|
||||
if not fn then
|
||||
printError(msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.on('device_detach', function()
|
||||
Event.on('device_detach', function()
|
||||
if not device.wireless_modem then
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
Event.pullEvents()
|
||||
Event.pullEvents()
|
||||
|
||||
for _,c in pairs(network) do
|
||||
for _,c in pairs(network) do
|
||||
c.active = false
|
||||
os.queueEvent('network_detach', c)
|
||||
end
|
||||
os.queueEvent('network_down')
|
||||
Event.pullEvent('network_down')
|
||||
|
||||
Util.clear(_G.network)
|
||||
end
|
||||
os.queueEvent('network_down')
|
||||
Event.pullEvent('network_down')
|
||||
|
||||
print('Net daemon started')
|
||||
|
||||
local function startNetwork()
|
||||
print('Starting network services')
|
||||
|
||||
local success, msg = Util.runFunction(
|
||||
Util.shallowCopy(_ENV), netUp)
|
||||
|
||||
if not success and msg then
|
||||
printError(msg)
|
||||
end
|
||||
print('Network services stopped')
|
||||
end
|
||||
|
||||
if device.wireless_modem then
|
||||
startNetwork()
|
||||
else
|
||||
print('No modem detected')
|
||||
end
|
||||
|
||||
while true do
|
||||
local _, deviceName = os.pullEvent('device_attach')
|
||||
if deviceName == 'wireless_modem' then
|
||||
startNetwork()
|
||||
end
|
||||
end
|
||||
Util.clear(_G.network)
|
||||
|
||||
print('Net daemon stopped')
|
||||
|
@ -21,6 +21,7 @@ _G.transport = transport
|
||||
|
||||
function transport.open(socket)
|
||||
transport.sockets[socket.sport] = socket
|
||||
socket.activityTimer = os.clock()
|
||||
end
|
||||
|
||||
function transport.read(socket)
|
||||
@ -34,16 +35,18 @@ function transport.write(socket, data)
|
||||
--debug('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq }))
|
||||
socket.transmit(socket.dport, socket.dhost, data)
|
||||
|
||||
local timerId = os.startTimer(3)
|
||||
--local timerId = os.startTimer(3)
|
||||
|
||||
transport.timers[timerId] = socket
|
||||
socket.timers[socket.wseq] = timerId
|
||||
--transport.timers[timerId] = socket
|
||||
--socket.timers[socket.wseq] = timerId
|
||||
|
||||
socket.wseq = socket.wseq + 1
|
||||
end
|
||||
|
||||
function transport.ping(socket)
|
||||
--debug('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq }))
|
||||
if os.clock() - socket.activityTimer > 10 then
|
||||
socket.activityTimer = os.clock()
|
||||
socket.transmit(socket.dport, socket.dhost, {
|
||||
type = 'PING',
|
||||
seq = -1,
|
||||
@ -52,6 +55,7 @@ function transport.ping(socket)
|
||||
local timerId = os.startTimer(3)
|
||||
transport.timers[timerId] = socket
|
||||
socket.timers[-1] = timerId
|
||||
end
|
||||
end
|
||||
|
||||
function transport.close(socket)
|
||||
@ -67,7 +71,7 @@ while true do
|
||||
local socket = transport.timers[timerId]
|
||||
|
||||
if socket and socket.connected then
|
||||
print('transport timeout - closing socket ' .. socket.sport)
|
||||
debug('transport timeout - closing socket ' .. socket.sport)
|
||||
socket:close()
|
||||
transport.timers[timerId] = nil
|
||||
end
|
||||
@ -88,18 +92,21 @@ while true do
|
||||
if ackTimerId then
|
||||
os.cancelTimer(ackTimerId)
|
||||
socket.timers[msg.seq] = nil
|
||||
socket.activityTimer = os.clock()
|
||||
transport.timers[ackTimerId] = nil
|
||||
end
|
||||
|
||||
elseif msg.type == 'PING' then
|
||||
socket.activityTimer = os.clock()
|
||||
socket.transmit(socket.dport, socket.dhost, {
|
||||
type = 'ACK',
|
||||
seq = msg.seq,
|
||||
})
|
||||
|
||||
elseif msg.type == 'DATA' and msg.data then
|
||||
socket.activityTimer = os.clock()
|
||||
if msg.seq ~= socket.rseq then
|
||||
print('transport seq error - closing socket ' .. socket.sport)
|
||||
debug('transport seq error - closing socket ' .. socket.sport)
|
||||
socket:close()
|
||||
else
|
||||
socket.rseq = socket.rseq + 1
|
||||
@ -111,10 +118,10 @@ while true do
|
||||
end
|
||||
|
||||
--debug('>> ' .. Util.tostring({ type = 'ACK', seq = msg.seq }))
|
||||
socket.transmit(socket.dport, socket.dhost, {
|
||||
type = 'ACK',
|
||||
seq = msg.seq,
|
||||
})
|
||||
--socket.transmit(socket.dport, socket.dhost, {
|
||||
-- type = 'ACK',
|
||||
-- seq = msg.seq,
|
||||
--})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user