opus/sys/apis/event.lua

217 lines
4.0 KiB
Lua
Raw Normal View History

2016-12-11 19:24:52 +00:00
local Event = {
2017-07-28 23:01:59 +00:00
uid = 1, -- unique id for handlers
routines = { }, -- coroutines
types = { }, -- event handlers
timers = { }, -- named timers
2017-07-24 02:37:07 +00:00
terminate = false,
2016-12-11 19:24:52 +00:00
}
2017-07-28 23:01:59 +00:00
local Routine = { }
2016-12-11 19:24:52 +00:00
2017-07-28 23:01:59 +00:00
function Routine:isDead()
if not self.co then
return true
end
return coroutine.status(self.co) == 'dead'
2016-12-11 19:24:52 +00:00
end
2017-07-28 23:01:59 +00:00
function Routine:terminate()
if self.co then
self:resume('terminate')
2016-12-11 19:24:52 +00:00
end
end
2017-07-28 23:01:59 +00:00
function Routine:resume(event, ...)
if not self.co then
error('Cannot resume a dead routine')
2017-07-28 23:01:59 +00:00
end
if not self.filter or self.filter == event or event == "terminate" then
local s, m = coroutine.resume(self.co, event, ...)
if coroutine.status(self.co) == 'dead' then
self.co = nil
self.filter = nil
Event.routines[self.uid] = nil
else
self.filter = m
end
if not s and event ~= 'terminate' then
error('\n' .. (m or 'Error processing event'))
2016-12-11 19:24:52 +00:00
end
2017-07-28 23:01:59 +00:00
return s, m
end
return true, self.filter
2016-12-11 19:24:52 +00:00
end
2017-07-28 23:01:59 +00:00
local function nextUID()
Event.uid = Event.uid + 1
return Event.uid - 1
2016-12-11 19:24:52 +00:00
end
2017-07-30 23:53:43 +00:00
function Event.on(event, fn)
local handlers = Event.types[event]
if not handlers then
handlers = { }
Event.types[event] = handlers
2017-07-28 23:01:59 +00:00
end
local handler = {
uid = nextUID(),
2017-07-30 23:53:43 +00:00
event = event,
2017-07-28 23:01:59 +00:00
fn = fn,
}
2017-07-30 23:53:43 +00:00
handlers[handler.uid] = handler
2017-07-28 23:01:59 +00:00
setmetatable(handler, { __index = Routine })
return handler
2016-12-11 19:24:52 +00:00
end
2017-07-28 23:01:59 +00:00
function Event.off(h)
if h and h.event then
Event.types[h.event][h.uid] = nil
2016-12-11 19:24:52 +00:00
end
end
2017-07-28 23:01:59 +00:00
local function addTimer(interval, recurring, fn)
local timerId = os.startTimer(interval)
2017-07-30 23:53:43 +00:00
local handler
2017-07-28 23:01:59 +00:00
2017-07-30 23:53:43 +00:00
handler = Event.on('timer', function(t, id)
2017-07-28 23:01:59 +00:00
if timerId == id then
fn(t, id)
if recurring then
timerId = os.startTimer(interval)
2016-12-11 19:24:52 +00:00
else
2017-07-30 23:53:43 +00:00
Event.off(handler)
2016-12-11 19:24:52 +00:00
end
end
2017-07-28 23:01:59 +00:00
end)
2017-08-02 13:52:41 +00:00
return handler
2017-07-28 23:01:59 +00:00
end
function Event.onInterval(interval, fn)
return addTimer(interval, true, fn)
end
2016-12-11 19:24:52 +00:00
2017-07-28 23:01:59 +00:00
function Event.onTimeout(timeout, fn)
return addTimer(timeout, false, fn)
2016-12-11 19:24:52 +00:00
end
2017-07-28 23:01:59 +00:00
function Event.addNamedTimer(name, interval, recurring, fn)
Event.cancelNamedTimer(name)
Event.timers[name] = addTimer(interval, recurring, fn)
2016-12-11 19:24:52 +00:00
end
2017-07-28 23:01:59 +00:00
function Event.cancelNamedTimer(name)
local timer = Event.timers[name]
if timer then
Event.off(timer)
end
2016-12-11 19:24:52 +00:00
end
2017-07-24 02:37:07 +00:00
function Event.waitForEvent(event, timeout)
2016-12-11 19:24:52 +00:00
local timerId = os.startTimer(timeout)
repeat
2017-07-28 23:01:59 +00:00
local e = { os.pullEvent() }
if e[1] == event then
return table.unpack(e)
2016-12-11 19:24:52 +00:00
end
2017-07-28 23:01:59 +00:00
until e[1] == 'timer' and e[2] == timerId
2016-12-11 19:24:52 +00:00
end
2017-07-28 23:01:59 +00:00
function Event.addRoutine(fn)
local r = {
co = coroutine.create(fn),
uid = nextUID()
}
setmetatable(r, { __index = Routine })
Event.routines[r.uid] = r
r:resume()
2017-07-24 02:37:07 +00:00
return r
2016-12-11 19:24:52 +00:00
end
2017-07-24 02:37:07 +00:00
function Event.pullEvents(...)
2017-07-28 23:01:59 +00:00
for _, fn in ipairs({ ... }) do
Event.addRoutine(fn)
2017-07-24 02:37:07 +00:00
end
2016-12-11 19:24:52 +00:00
repeat
2017-07-24 02:37:07 +00:00
local e = Event.pullEvent()
until e[1] == 'terminate'
2016-12-11 19:24:52 +00:00
end
2017-07-24 02:37:07 +00:00
function Event.exitPullEvents()
Event.terminate = true
os.sleep(0)
2016-12-27 03:26:43 +00:00
end
2017-07-30 23:53:43 +00:00
local function processHandlers(event)
2017-07-28 23:01:59 +00:00
2017-07-30 23:53:43 +00:00
local handlers = Event.types[event]
if handlers then
for _,h in pairs(handlers) do
if not h.co then
2017-07-28 23:01:59 +00:00
-- callbacks are single threaded (only 1 co per handler)
h.co = coroutine.create(h.fn)
Event.routines[h.uid] = h
end
end
end
end
2017-08-02 13:52:41 +00:00
local function tokeys(t)
local keys = { }
for k in pairs(t) do
keys[#keys+1] = k
end
return keys
end
2017-07-28 23:01:59 +00:00
local function processRoutines(...)
2017-07-30 23:53:43 +00:00
2017-08-02 13:52:41 +00:00
local keys = tokeys(Event.routines)
2017-07-28 23:01:59 +00:00
for _,key in ipairs(keys) do
local r = Event.routines[key]
if r then
r:resume(...)
end
end
end
2017-08-09 14:19:00 +00:00
function Event.processEvent(e)
processHandlers(e[1])
processRoutines(table.unpack(e))
end
2017-07-24 02:37:07 +00:00
function Event.pullEvent(eventType)
2016-12-27 21:01:06 +00:00
while true do
local e = { os.pullEventRaw() }
2017-07-28 23:01:59 +00:00
2017-07-30 23:53:43 +00:00
processHandlers(e[1])
2017-07-28 23:01:59 +00:00
processRoutines(table.unpack(e))
2017-07-24 02:37:07 +00:00
if Event.terminate or e[1] == 'terminate' then
Event.terminate = false
return { 'terminate' }
end
2016-12-11 19:24:52 +00:00
2017-07-24 02:37:07 +00:00
if not eventType or e[1] == eventType then
return e
end
end
2016-12-11 19:24:52 +00:00
end
return Event