MineOS/lib/event.lua

236 lines
6.9 KiB
Lua
Executable File

-- This is a fast OpenComputers event processing library written as an alternative
-- for its' OpenOS analogue which has become too slow and inefficient in the latest updates
--------------------------------------------------------------------------------------------------------
local computer = require("computer")
local event = {
push = computer.pushSignal,
handlers = {},
interruptingEnabled = true,
interruptingDelay = 1,
interruptingKeyCodes = {
[29] = true,
[46] = true,
[56] = true
},
onError = function(errorMessage)
-- require("ECSAPI").error("CYKA: " .. tostring(errorMessage))
end
}
local lastInterrupt, interruptingKeysDown = 0, {}
--------------------------------------------------------------------------------------------------------
function event.register(callback, signalType, times, interval)
checkArg(1, callback, "function")
checkArg(2, signalType, "string", "nil")
checkArg(3, times, "number", "nil")
checkArg(4, nextTriggerTime, "number", "nil")
local ID
while not ID do
ID = math.random(1, 0x7FFFFFFF)
for handlerIndex = 1, #event.handlers do
if event.handlers[handlerIndex].ID == ID then
ID = nil
break
end
end
end
local handler = {
ID = ID,
signalType = signalType,
callback = callback,
times = times or math.huge,
interval = interval,
nextTriggerTime = interval and (computer.uptime() + interval) or nil
}
table.insert(event.handlers, handler)
return handler
end
function event.cancel(ID)
checkArg(1, ID, "number")
for handlerIndex = 1, #event.handlers do
if event.handlers[handlerIndex].ID == ID then
table.remove(event.handlers, handlerIndex)
return true
end
end
return false, "No registered handlers found for ID \"" .. ID .. "\""
end
--------------------------------------------------------------------------------------------------------
function event.listen(signalType, callback)
checkArg(1, signalType, "string")
checkArg(2, callback, "function")
for handlerIndex = 1, #event.handlers do
if event.handlers[handlerIndex].callback == callback then
return false, "Callback method " .. tostring(callback) .. " is already registered"
end
end
event.register(callback, signalType)
end
function event.ignore(signalType, callback)
checkArg(1, signalType, "string")
checkArg(2, callback, "function")
for handlerIndex = 1, #event.handlers do
if event.handlers[handlerIndex].signalType == signalType and event.handlers[handlerIndex].callback == callback then
table.remove(event.handlers, handlerIndex)
return true
end
end
return false, "No registered listeners found for signal \"" .. signalType .. "\" and callback method \"" .. tostring(callback)
end
--------------------------------------------------------------------------------------------------------
function event.timer(interval, callback, times)
checkArg(1, interval, "number")
checkArg(2, callback, "function")
checkArg(3, times, "number", "nil")
event.register(callback, nil, times, interval)
return event.handlers[#event.handlers].ID
end
--------------------------------------------------------------------------------------------------------
local function executeHandlerCallback(callback, ...)
local success, result = pcall(callback, ...)
if success then
return result
else
if type(event.onError) == "function" then
pcall(event.onError, result)
end
end
end
local function eventTick(timeout)
local eventData, handlerIndex, uptime = {computer.pullSignal(timeout)}, 1, computer.uptime()
while handlerIndex <= #event.handlers do
if event.handlers[handlerIndex].times > 0 then
if
(not event.handlers[handlerIndex].signalType or event.handlers[handlerIndex].signalType == eventData[1]) and
(not event.handlers[handlerIndex].nextTriggerTime or event.handlers[handlerIndex].nextTriggerTime <= uptime)
then
executeHandlerCallback(event.handlers[handlerIndex].callback, table.unpack(eventData))
uptime = computer.uptime()
event.handlers[handlerIndex].times = event.handlers[handlerIndex].times - 1
if event.handlers[handlerIndex].nextTriggerTime then
event.handlers[handlerIndex].nextTriggerTime = uptime + event.handlers[handlerIndex].interval
end
end
handlerIndex = handlerIndex + 1
else
table.remove(event.handlers, handlerIndex)
end
end
-- Interruption support
if event.interruptingEnabled then
-- Analysing for which interrupting key is pressed - we don't need keyboard API for this
if eventData[1] == "key_down" then
if event.interruptingKeyCodes[eventData[4]] then
interruptingKeysDown[eventData[4]] = true
end
elseif eventData[1] == "key_up" then
if event.interruptingKeyCodes[eventData[4]] then
interruptingKeysDown[eventData[4]] = nil
end
end
local shouldInterrupt = true
for keyCode in pairs(event.interruptingKeyCodes) do
if not interruptingKeysDown[keyCode] then
shouldInterrupt = false
end
end
if shouldInterrupt and uptime - lastInterrupt > event.interruptingDelay then
lastInterrupt = uptime
error("interrupted", 0)
end
end
return eventData
end
local function getNearestHandlerTriggerTime()
local nearestTriggerTime
for handlerIndex = 1, #event.handlers do
if event.handlers[handlerIndex].nextTriggerTime then
nearestTriggerTime = math.min(nearestTriggerTime or math.huge, event.handlers[handlerIndex].nextTriggerTime)
end
end
return nearestTriggerTime
end
function event.skip(signalType)
event.skipSignalType = signalType
end
function event.pull(...)
local args = {...}
local args1Type, timeout, signalType = type(args[1])
if args1Type == "string" then
timeout, signalType = math.huge, args[1]
elseif args1Type == "number" then
timeout, signalType = args[1], type(args[2]) == "string" and args[2] or nil
end
local deadline = computer.uptime() + (timeout or math.huge)
while computer.uptime() <= deadline do
local eventData = eventTick((getNearestHandlerTriggerTime() or deadline) - computer.uptime())
if eventData[1] and (not signalType or signalType == eventData[1]) then
if eventData[1] == event.skipSignalType then
event.skipSignalType = nil
else
return table.unpack(eventData)
end
end
end
end
--------------------------------------------------------------------------------------------------------
local doubleTouchInterval, lastTouchX, lastTouchY, lastTouchButton, lastTouchUptime, lastTouchScreenAddress = 0.3, 0, 0, 0, 0
event.listen("touch", function(signalType, screenAddress, x, y, button, user)
local uptime = computer.uptime()
if lastTouchX == x and lastTouchY == y and lastTouchButton == button and lastTouchScreenAddress == screenAddress and uptime - lastTouchUptime <= doubleTouchInterval then
event.skip("touch")
computer.pushSignal("double_touch", screenAddress, x, y, button, user)
end
lastTouchX, lastTouchY, lastTouchButton, lastTouchUptime, lastTouchScreenAddress = x, y, button, uptime, screenAddress
end)
--------------------------------------------------------------------------------------------------------
return event