Event(事件) API为用户提供了一套基本的事件处理系统,可利用其编写响应操作系统或其他程序/运行库传递的signals代码。
例如,你可以利用此API捕获按下的按键、在外接显示器连接到电脑或断开连接时进行响应,或是处理传入的网络信息。
event API主要有两种用法:
在驱动模式下,你需要在程序中为事件注册回调(callback)函数(用 event.listen()
函数),然后退出程序,以继续执行原先的程序(通常是shell)。
而在优先模式下,你无需在程序中注册事件,可以直接使用events.pull()
函数进行处理。
注意:虽然从技术层面上讲可以同时使用两种工作模式,但不推荐这样做。为了保证所有进行了注册的函数都能接收到事件,事件的一次触发只有在所有函数都被调用后才算结束。因此如果你将处理函数(handler)进行了注册,又执行了拉取(pull)操作,那么同一个事件会被响应两次。
event.listen(event: string, callback: function): boolean
number
, the event id which can be canceled via event.cancel
, if the event was successfully registered, false
if this function was already registered for this event type.event.ignore(event: string, callback: function): boolean
true
if the event was successfully unregistered, false
if this function was not registered for this event type.false
to unregister themselves, which is equivalent to calling event.ignore
and passing the listener with the event name it was registered for.event.timer(interval: number, callback: function[, times: number]): number
interval
.math.huge
for infinite repeat.event.cancel(timerId: number): boolean
event.timer
.event.timer
.true
if the timer was stopped, false
if there was no timer with the specified ID.event.pull([timeout: number], [name: string], ...): string, ...
nil
if no event was queued during that time.nil
in which case the event names will not be filtered. See string.match
on how to use patterns.nil
in which case this particular argument will not be filtered.touch
signal (when a player clicks on a tier two or three screen) has the signature screenX: number, screenY: number, playerName: string
local _, x, y = event.pull("touch", nil, nil, "Steve")
event.pullFiltered([timeout: number], [filter: function]): string, ...
(Since 1.5.9)
Pulls and returns the next available event from the queue, or waits until one becomes available but allows filtering by specifying filter function.
timeout - if passed the function will wait for a new event for this many seconds at maximum then returns nil
if no event was queued during that time.Example:
local allowedPlayers = {"Kubuxu", "Sangar", "Magik6k", "Vexatos"} local function filter(name, ...) if name ~= "key_up" and name ~= "key_down" and name ~= "touch" then return false end local nick if name == "touch" then nick = select(3, ...) else nick = select(4, ...) end for _, allowed in ipairs(allowedPlayers) do if nick == allowed then return true end end return false end local e = {event.pullFiltered(filter)} -- We are pulling key_up, key_down and click events for unlimited amount of time. The filter will ensure that only events caused by players in allowedPlayers are pulled.
event.pullMultiple(...): ...
(Since 1.5.9)
As its arguments pullMultiple
accepts multiple event names to be pulled, allowing basic filtering of multiple events at once.event.onError(message: any)
event.pull
). Per default, this logs errors into a file on the temporary file system.event.push(name: string[, ...])
event.pull
for signals.Starting In OpenOS 1.6.4 and later, interrupts have been cleaned up. The following two methods are now obsolete
event.shouldSoftInterrupt(): boolean
(Since 1.5.9 and removed in 1.6.4)event.shouldInterrupt(): boolean
(Since 1.5.9 and removed in 1.6.4)
Interrupts are a type of messaging intended to close or stop a process. In OpenOS the computer.pullSignal()
, and thus any wrapper, generates 2 types of events.
They are especially useful when event.pull*()
is called without time limit and with a filter. In some cases this means that event.pull*()
could be waiting indefinitely.
Ctrl+C
. The signal returns two fields, the event name "interrupted"
and the computer uptime
Ctrl-Alt-C
. It forcibly exits the event.pull*()
method by throwing a "interrupted"
error.Typically user scripts care about one or two events, and don't care to handle the rest. It is good to handle “interrupted” for soft interrupts.
while true do local id, _, x, y = event.pullMultiple("touch", "interrupted") if id == "interrupted" then print("soft interrupt, closing") break elseif id == "touch" then print("user clicked", x, y) end end
Here is a clever solution for providing a general purpose event handler. In this example the primary functionality uses the event id returned by event.pull()
as a key for a table of callbacks, using metamethods to handle undefined events. Note that event.pull
puts the program on hold until there is an event available.
local event = require "event" -- load event table and store the pointer to it in event local char_space = string.byte(" ") -- numerical representation of the space char local running = true -- state variable so the loop can terminate function unknownEvent() -- do nothing if the event wasn't relevant end -- table that holds all event handlers -- in case no match can be found returns the dummy function unknownEvent local myEventHandlers = setmetatable({}, { __index = function() return unknownEvent end }) -- Example key-handler that simply sets running to false if the user hits space function myEventHandlers.key_up(adress, char, code, playerName) if (char == char_space) then running = false end end -- The main event handler as function to separate eventID from the remaining arguments function handleEvent(eventID, ...) if (eventID) then -- can be nil if no event was pulled for some time myEventHandlers[eventID](...) -- call the appropriate event handler with all remaining arguments end end -- main event loop which processes all events, or sleeps if there is nothing to do while running do handleEvent(event.pull()) -- sleeps until an event is available, then process it end
If you work in driver mode, you need to register the events instead, by either registering a global event handler like the one in the example above, or by registering each individual handler on its own. If you would write the example above to work in the background, the while running do
loop would be replaced like this:
event.listen("key_up", handleEvent) -- register handleEvent to be called on key_up then end the program
It would also be possible to register myEventHandlers.key_up
directly, in which case it would receive an additional parameter (the event name) as the first parameter.