· 6 years ago · Jan 09, 2020, 02:44 PM
1local event, handlers, interruptingKeysDown, lastInterrupt = {
2 interruptingEnabled = true,
3 interruptingDelay = 1,
4 interruptingKeyCodes = {
5 [29] = true,
6 [46] = true,
7 [56] = true
8 },
9 push = computer.pushSignal
10}, {}, {}, 0
11
12local computerPullSignal, computerUptime, mathHuge, mathMin, skipSignalType = computer.pullSignal, computer.uptime, math.huge, math.min
13
14--------------------------------------------------------------------------------------------------------
15
16function event.addHandler(callback, interval, times)
17 checkArg(1, callback, "function")
18 checkArg(2, interval, "number", "nil")
19 checkArg(3, times, "number", "nil")
20
21 local handler = {
22 callback = callback,
23 times = times or mathHuge,
24 interval = interval,
25 nextTriggerTime = interval and computerUptime() + interval or 0
26 }
27
28 handlers[handler] = true
29
30 return handler
31end
32
33function event.removeHandler(handler)
34 checkArg(1, handler, "table")
35
36 if handlers[handler] then
37 handlers[handler] = nil
38
39 return true
40 else
41 return false, "Handler with given table is not registered"
42 end
43end
44
45function event.getHandlers()
46 return handlers
47end
48
49function event.skip(signalType)
50 skipSignalType = signalType
51end
52
53function event.pull(preferredTimeout)
54 local uptime, signalData = computerUptime()
55 local deadline = uptime + (preferredTimeout or mathHuge)
56
57 repeat
58 -- Determining pullSignal timeout
59 timeout = deadline
60 for handler in pairs(handlers) do
61 if handler.nextTriggerTime > 0 then
62 timeout = mathMin(timeout, handler.nextTriggerTime)
63 end
64 end
65
66 -- Pulling signal data
67 signalData = { computerPullSignal(timeout - computerUptime()) }
68
69 -- Handlers processing
70 for handler in pairs(handlers) do
71 if handler.times > 0 then
72 uptime = computerUptime()
73
74 if
75 handler.nextTriggerTime <= uptime
76 then
77 handler.times = handler.times - 1
78 if handler.nextTriggerTime > 0 then
79 handler.nextTriggerTime = uptime + handler.interval
80 end
81
82 -- Callback running
83 handler.callback(table.unpack(signalData))
84 end
85 else
86 handlers[handler] = nil
87 end
88 end
89
90 -- Program interruption support. It's faster to do it here instead of registering handlers
91 if signalData[1] == "key_down" or signalData[1] == "key_up" and event.interruptingEnabled then
92 -- Analysing for which interrupting key is pressed - we don't need keyboard API for this
93 if event.interruptingKeyCodes[signalData[4]] then
94 interruptingKeysDown[signalData[4]] = signalData[1] == "key_down" and true or nil
95 end
96
97 local shouldInterrupt = true
98 for keyCode in pairs(event.interruptingKeyCodes) do
99 if not interruptingKeysDown[keyCode] then
100 shouldInterrupt = false
101 end
102 end
103
104 if shouldInterrupt and uptime - lastInterrupt > event.interruptingDelay then
105 lastInterrupt = uptime
106 error("interrupted", 0)
107 end
108 end
109
110 -- Loop-breaking condition
111 if signalData[1] then
112 if signalData[1] == skipSignalType then
113 skipSignalType = nil
114 else
115 return table.unpack(signalData)
116 end
117 end
118 until uptime >= deadline
119end
120
121-- Sleeps "time" of seconds via "busy-wait" concept
122function event.sleep(time)
123 checkArg(1, time, "number", "nil")
124
125 local deadline = computerUptime() + (time or 0)
126 repeat
127 event.pull(deadline - computerUptime())
128 until computerUptime() >= deadline
129end
130
131--------------------------------------------------------------------------------------------------------
132
133return event