testing version of LuaBIOS and OpenOS
people were having issues getting them to work so now we promote consistency
This commit is contained in:
204
data/OpenOS/boot/04_component.lua
Normal file
204
data/OpenOS/boot/04_component.lua
Normal file
@@ -0,0 +1,204 @@
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local event = require("event")
|
||||
|
||||
local adding = {}
|
||||
local primaries = {}
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- This allows writing component.modem.open(123) instead of writing
|
||||
-- component.getPrimary("modem").open(123), which may be nicer to read.
|
||||
setmetatable(component, {
|
||||
__index = function(_, key)
|
||||
return component.getPrimary(key)
|
||||
end,
|
||||
__pairs = function(self)
|
||||
local parent = false
|
||||
return function(_, key)
|
||||
if parent then
|
||||
return next(primaries, key)
|
||||
else
|
||||
local k, v = next(self, key)
|
||||
if not k then
|
||||
parent = true
|
||||
return next(primaries)
|
||||
else
|
||||
return k, v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
function component.get(address, componentType)
|
||||
checkArg(1, address, "string")
|
||||
checkArg(2, componentType, "string", "nil")
|
||||
for c in component.list(componentType, true) do
|
||||
if c:sub(1, address:len()) == address then
|
||||
return c
|
||||
end
|
||||
end
|
||||
return nil, "no such component"
|
||||
end
|
||||
|
||||
function component.isAvailable(componentType)
|
||||
checkArg(1, componentType, "string")
|
||||
if not primaries[componentType] and not adding[componentType] then
|
||||
-- This is mostly to avoid out of memory errors preventing proxy
|
||||
-- creation cause confusion by trying to create the proxy again,
|
||||
-- causing the oom error to be thrown again.
|
||||
component.setPrimary(componentType, component.list(componentType, true)())
|
||||
end
|
||||
return primaries[componentType] ~= nil
|
||||
end
|
||||
|
||||
function component.isPrimary(address)
|
||||
local componentType = component.type(address)
|
||||
if componentType then
|
||||
if component.isAvailable(componentType) then
|
||||
return primaries[componentType].address == address
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function component.getPrimary(componentType)
|
||||
checkArg(1, componentType, "string")
|
||||
assert(component.isAvailable(componentType),
|
||||
"no primary '" .. componentType .. "' available")
|
||||
return primaries[componentType]
|
||||
end
|
||||
|
||||
function component.setPrimary(componentType, address)
|
||||
checkArg(1, componentType, "string")
|
||||
checkArg(2, address, "string", "nil")
|
||||
debugprint("setPrimary", componentType, address)
|
||||
if address ~= nil then
|
||||
address = component.get(address, componentType)
|
||||
assert(address, "no such component")
|
||||
end
|
||||
|
||||
local wasAvailable = primaries[componentType]
|
||||
if wasAvailable and address == wasAvailable.address then
|
||||
return
|
||||
end
|
||||
local wasAdding = adding[componentType]
|
||||
if wasAdding and address == wasAdding.address then
|
||||
return
|
||||
end
|
||||
if wasAdding then
|
||||
event.cancel(wasAdding.timer)
|
||||
end
|
||||
primaries[componentType] = nil
|
||||
adding[componentType] = nil
|
||||
|
||||
local primary = address and component.proxy(address) or nil
|
||||
if wasAvailable then
|
||||
computer.pushSignal("component_unavailable", componentType)
|
||||
end
|
||||
if primary then
|
||||
if wasAvailable or wasAdding then
|
||||
adding[componentType] = {
|
||||
address=address,
|
||||
proxy = primary,
|
||||
timer=event.timer(0.1, function()
|
||||
adding[componentType] = nil
|
||||
primaries[componentType] = primary
|
||||
computer.pushSignal("component_available", componentType)
|
||||
end)
|
||||
}
|
||||
else
|
||||
primaries[componentType] = primary
|
||||
computer.pushSignal("component_available", componentType)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function onComponentAdded(_, address, componentType)
|
||||
local prev = primaries[componentType] or (adding[componentType] and adding[componentType].proxy)
|
||||
|
||||
if prev then
|
||||
-- special handlers -- some components are just better at being primary
|
||||
if componentType == "screen" then
|
||||
--the primary has no keyboards but we do
|
||||
if #prev.getKeyboards() == 0 then
|
||||
local first_kb = component.invoke(address, 'getKeyboards')[1]
|
||||
if first_kb then
|
||||
-- just in case our kb failed to achieve primary
|
||||
-- possible if existing primary keyboard became primary first without a screen
|
||||
-- then prev (a screen) was added without a keyboard
|
||||
-- and then we attached this screen+kb pair, and our kb fired first - failing to achieve primary
|
||||
-- also, our kb may fire right after this, which is fine
|
||||
component.setPrimary("keyboard", first_kb)
|
||||
prev = nil -- nil meaning we should take this new one over the previous
|
||||
end
|
||||
end
|
||||
elseif componentType == "keyboard" then
|
||||
-- to reduce signal noise, if this kb is also the prev, we do not need to reset primary
|
||||
if address ~= prev.address then
|
||||
--keyboards never replace primary keyboards unless the are the only keyboard on the primary screen
|
||||
local current_screen = primaries.screen or (adding.screen and adding.screen.proxy)
|
||||
--if there is not yet a screen, do not use this keyboard, it's not any better
|
||||
if current_screen then
|
||||
-- the next phase is complicated
|
||||
-- there is already a screen and there is already a keyboard
|
||||
-- this keyboard is only better if this is a keyboard of the primary screen AND the current keyboard is not
|
||||
-- i don't think we can trust kb order (1st vs 2nd), 2nd could fire first
|
||||
-- but if there are two kbs on a screen, we can give preferred treatment to the first
|
||||
-- thus, assume 2nd is not attached for the purposes of primary kb
|
||||
-- and THUS, whichever (if either) is the 1st kb of the current screen
|
||||
-- this is only possible if
|
||||
-- 1. the only kb on the system (current) has no screen
|
||||
-- 2. a screen is added without a kb
|
||||
-- 3. this kb is added later manually
|
||||
|
||||
-- prev is true when addr is not equal to the primary keyboard of the current screen -- meaning
|
||||
-- when addr is different, and thus it is not the primary keyboard, then we ignore this
|
||||
-- keyboard, and keep the previous
|
||||
-- prev is false means we should take this new keyboard
|
||||
prev = address ~= current_screen.getKeyboards()[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not prev then
|
||||
component.setPrimary(componentType, address)
|
||||
end
|
||||
end
|
||||
|
||||
local function onComponentRemoved(_, address, componentType)
|
||||
if primaries[componentType] and primaries[componentType].address == address or
|
||||
adding[componentType] and adding[componentType].address == address
|
||||
then
|
||||
local next = component.list(componentType, true)()
|
||||
component.setPrimary(componentType, next)
|
||||
|
||||
if componentType == "screen" and next then
|
||||
-- setPrimary already set the proxy (if successful)
|
||||
local proxy = (primaries.screen or (adding.screen and adding.screen.proxy))
|
||||
if proxy then
|
||||
-- if a screen is removed, and the primary keyboard is actually attached to another, non-primary, screen
|
||||
-- then the `next` screen, if it has a keyboard, should TAKE priority
|
||||
local next_kb = proxy.getKeyboards()[1] -- costly, don't call this method often
|
||||
local old_kb = primaries.keyboard or adding.keyboard
|
||||
-- if the next screen doesn't have a kb, this operation is without purpose, leave things as they are
|
||||
-- if there was no previous kb, use the new one
|
||||
if next_kb and (not old_kb or old_kb.address ~= next_kb) then
|
||||
component.setPrimary("keyboard", next_kb)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
event.listen("component_added", onComponentAdded)
|
||||
event.listen("component_removed", onComponentRemoved)
|
||||
|
||||
if _G.boot_screen then
|
||||
component.setPrimary("screen", _G.boot_screen)
|
||||
end
|
||||
_G.boot_screen = nil
|
||||
Reference in New Issue
Block a user