Event(事件) API为用户提供了一套基本的事件处理系统,可利用其编写响应操作系统或其他程序/运行库传递的signals代码。
例如,你可以利用此API捕获按下的按键、在外接显示器连接到电脑或断开连接时进行响应,或是处理传入的网络信息。
event API主要有两种用法:
在驱动模式下,你需要在程序中为事件注册回调(callback)函数(用 event.listen()
函数),然后退出程序,以继续执行原先的程序(通常是shell)。
而在优先模式下,你无需在程序中注册事件,可以直接使用events.pull()
函数进行处理。
注意:虽然从技术层面上讲可以同时使用两种工作模式,但不推荐这样做。为了保证所有进行了注册的函数都能接收到事件,事件的一次触发只有在所有函数都被调用后才算结束。因此如果你将处理函数(handler)进行了注册,又执行了拉取(pull)操作,那么同一个事件会被响应两次。
event.listen(event: string, callback: function): boolean
number
,代表了事件id,可传递给event.cancel
函数取消事件。如果函数已经注册过此类事件,将会返回false
。event.ignore(event: string, callback: function): boolean
true
。如果指定的函数并未注册到此类事件则返回false
。false
,此时相当于调用 event.ignore
,并向其传参侦听器和侦听器所注册的函数。 event.timer(interval: number, callback: function[, times: number]): number
interval
指定的时长后将会被触发。
interval(触发间隔) - 调用回调函数的间隔时长,单位为秒。可以为像0.05这样的小数。math.huge
可以无限次重复调用。event.cancel(timerId: number): boolean
event.timer
函数创建的定时器。event.timer
函数返回的定时器id。true
。如果不存在指定id对应的定时器则返回false
。event.pull([timeout: number], [name: string], ...): string, ...
nil
。nil
,此时事件名称不会被过滤。模式字符串的使用可参考string.match
函数。nil
,这样将不会过滤此参数。touch
信号(在玩家点击二级或三级显示器时触发)带有如下参数: screenX: number
(点击处的X坐标)、screenY: number
(点击处的Y坐标)、playerName: string
(进行点击的玩家姓名)。local _, x, y = event.pull("touch", nil, nil, "Steve")
event.pullFiltered([timeout: number], [filter: function]): string, ...
(自1.5.9起)
从队列中拉取并返回下一个可用事件,或者等待可用事件出现。支持以函数作为过滤器。timeout(超时时间) - 如果指定了值,则函数会至多等待此秒数。超时后仍未出现可用事件,则函数会返回nil
。样例:
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)} --程序将会不限时等待拉取key_up(松开按键)、key_down(按下按键)和click(点击屏幕)事件。过滤器会确保只拉取allowedPlayers表中的玩家触发的事件。
event.pullMultiple(...): ...
(自1.5.9起)
pullMultiple
函数可接收多个要拉取的事件名称作为参数,同时支持基本的过滤功能。event.onError(message: any)
events.pull
函数触发函数执行的代码)。默认情况下,此函数会将报错信息记录到临时文件中。
如果你想以其他方式处理事件报错,可以将此函数替换为自定义函数。event.push(name: string[, ...])
event.pull
,添加此别名会更符合逻辑。在OpenOS 1.6.4及更高版本,中断功能已经被删除。下列两个函数现在已经过时。
event.shouldSoftInterrupt(): boolean
(自1.5.9起添加,在1.6.4被移除)event.shouldInterrupt(): boolean
(自1.5.9起添加,在1.6.4被移除)
中断是一类用于关闭或停止进程的消息。在OpenOS中computer.pullSignal()
函数和修饰过的此函数会产生两种类型的事件。
若event.pull*()
函数在指定了过滤器但不指定超时时间的情况下执行,一定情况下意味着无限期执行。这两种事件非常有用。
Ctrl+C
时产生的事件信号。信号包含两个参数,事件名称"interrupted"
(中断)和电脑运行时间。
Ctrl-Alt-C
时产生。它会通过抛出"interrupted"
(中断)异常以强制停止event.pull*()
函数。通常用户编写的脚本只会涉及一两个事件,不涉及其他事件的处理。建议将“interrupted”事件作为软件中断处理。
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.