blob: 76020d1e39dce06109b558cdec1ccb4e07bacfd7 [file] [log] [blame] [raw]
local computer = require("computer")
local keyboard = require("keyboard")
local event, listeners, timers = {}, {}, {}
local lastInterrupt = -math.huge
local function matches(signal, name, filter)
if name and not (type(signal[1]) == "string" and signal[1]:match(name))
then
return false
end
for i = 1, filter.n do
if filter[i] ~= nil and filter[i] ~= signal[i + 1] then
return false
end
end
return true
end
local function call(callback, ...)
local result, message = pcall(callback, ...)
if not result and type(event.onError) == "function" then
pcall(event.onError, message)
return
end
return message
end
local function dispatch(signal, ...)
if listeners[signal] then
local function callbacks()
local list = {}
for index, listener in ipairs(listeners[signal]) do
list[index] = listener
end
return list
end
for _, callback in ipairs(callbacks()) do
if call(callback, signal, ...) == false then
event.ignore(signal, callback) -- alternative method of removing a listener
end
end
end
end
local function tick()
local function elapsed()
local list = {}
for id, timer in pairs(timers) do
if timer.after <= computer.uptime() then
table.insert(list, timer.callback)
timer.times = timer.times - 1
if timer.times <= 0 then
timers[id] = nil
else
timer.after = computer.uptime() + timer.interval
end
end
end
return list
end
for _, callback in ipairs(elapsed()) do
call(callback)
end
end
-------------------------------------------------------------------------------
function event.cancel(timerId)
checkArg(1, timerId, "number")
if timers[timerId] then
timers[timerId] = nil
return true
end
return false
end
function event.ignore(name, callback)
checkArg(1, name, "string")
checkArg(2, callback, "function")
if listeners[name] then
for i = 1, #listeners[name] do
if listeners[name][i] == callback then
table.remove(listeners[name], i)
if #listeners[name] == 0 then
listeners[name] = nil
end
return true
end
end
end
return false
end
function event.listen(name, callback)
checkArg(1, name, "string")
checkArg(2, callback, "function")
if listeners[name] then
for i = 1, #listeners[name] do
if listeners[name][i] == callback then
return false
end
end
else
listeners[name] = {}
end
table.insert(listeners[name], callback)
return true
end
function event.onError(message)
local log = io.open("/tmp/event.log", "a")
if log then
log:write(message .. "\n")
log:close()
end
end
function event.pull(...)
local args = table.pack(...)
local seconds, name, filter
if type(args[1]) == "string" then
name = args[1]
filter = table.pack(table.unpack(args, 2, args.n))
else
checkArg(1, args[1], "number", "nil")
checkArg(2, args[2], "string", "nil")
seconds = args[1]
name = args[2]
filter = table.pack(table.unpack(args, 3, args.n))
end
local hasFilter = name ~= nil
if not hasFilter then
for i = 1, filter.n do
hasFilter = hasFilter or filter[i] ~= nil
end
end
local deadline = seconds and
(computer.uptime() + seconds) or
(hasFilter and math.huge or 0)
repeat
local closest = seconds and deadline or math.huge
for _, timer in pairs(timers) do
closest = math.min(closest, timer.after)
end
local signal = table.pack(computer.pullSignal(closest - computer.uptime()))
if signal.n > 0 then
dispatch(table.unpack(signal, 1, signal.n))
end
tick()
if event.shouldInterrupt() then
lastInterrupt = computer.uptime()
error("interrupted", 0)
end
if not (seconds or hasFilter) or matches(signal, name, filter) then
return table.unpack(signal, 1, signal.n)
end
until computer.uptime() >= deadline
end
function event.shouldInterrupt()
return computer.uptime() - lastInterrupt > 1 and
keyboard.isControlDown() and
keyboard.isAltDown() and
keyboard.isKeyDown(keyboard.keys.c)
end
function event.timer(interval, callback, times)
checkArg(1, interval, "number")
checkArg(2, callback, "function")
checkArg(3, times, "number", "nil")
local id
repeat
id = math.floor(math.random(1, 0x7FFFFFFF))
until not timers[id]
timers[id] = {
interval = interval,
after = computer.uptime() + interval,
callback = callback,
times = times or 1
}
return id
end
-------------------------------------------------------------------------------
return event