MineOS/lib/event.lua
2015-09-05 11:34:03 +03:00

259 lines
6.3 KiB
Lua

local computer = require("computer")
local keyboard = require("keyboard")
local event, listeners, timers = {}, {}, {}
local lastInterrupt = -math.huge
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
local function createPlainFilter(name, ...)
local filter = table.pack(...)
if name == nil and filter.n == 0 then
return nil
end
return function(...)
local signal = table.pack(...)
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
end
local function createMultipleFilter(...)
local filter = table.pack(...)
if filter.n == 0 then
return nil
end
return function(...)
local signal = table.pack(...)
if type(signal[1]) ~= "string" then
return false
end
for i = 1, filter.n do
if filter[i] ~= nil and signal[1]:match(filter[i]) then
return true
end
end
return false
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(...)
if type(args[1]) == "string" then
return event.pullFiltered(createPlainFilter(...))
else
checkArg(1, args[1], "number", "nil")
checkArg(2, args[2], "string", "nil")
return event.pullFiltered(args[1], createPlainFilter(select(2, ...)))
end
end
function event.pullMultiple(...)
local seconds
local args
if type(...) == "number" then
seconds = ...
args = table.pack(select(2,...))
for i=1,args.n do
checkArg(i+1, args[i], "string", "nil")
end
else
args = table.pack(...)
for i=1,args.n do
checkArg(i, args[i], "string", "nil")
end
end
return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n)))
end
function event.pullFiltered(...)
local args = table.pack(...)
local seconds, filter
if type(args[1]) == "function" then
filter = args[1]
else
checkArg(1, args[1], "number", "nil")
checkArg(2, args[2], "function", "nil")
seconds = args[1]
filter = args[2]
end
local deadline = seconds and
(computer.uptime() + seconds) or
(filter 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()
event.takeScreenshot()
if event.shouldInterrupt() then
lastInterrupt = computer.uptime()
error("interrupted", 0)
end
if event.shouldSoftInterrupt() and (filter == nil or filter("interrupted", computer.uptime() - lastInterrupt)) then
local awaited = computer.uptime() - lastInterrupt
lastInterrupt = computer.uptime()
return "interrupted", awaited
end
if not (seconds or filter) or filter == nil or filter(table.unpack(signal, 1, signal.n)) 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.shouldSoftInterrupt()
return computer.uptime() - lastInterrupt > 1 and
keyboard.isControlDown() and
keyboard.isKeyDown(keyboard.keys.c)
end
function event.takeScreenshot()
if keyboard.isKeyDown(100) then
computer.beep(1500)
image.screenshot("screenshot.png")
computer.beep(2000)
computer.beep(2000)
end
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