diff options
author | William Wilgus <me.theuser@yahoo.com> | 2019-10-11 10:09:08 -0500 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2019-11-15 14:28:03 +0100 |
commit | f45db552f39579b31d428350f8853d52187f5733 (patch) | |
tree | 82f98e89fd36d20b6f992db8481884145ae8aeb0 /apps | |
parent | 9cfdbdb355e3251dc524630ccba5f8eb895458bd (diff) | |
download | rockbox-f45db552f39579b31d428350f8853d52187f5733.tar.gz rockbox-f45db552f39579b31d428350f8853d52187f5733.zip |
lua rockev rewrite
After a few issues with the timer code in the hosted players
I realized a few shortcomings in the original rockev code...
This rewrite gives thread_exit an explicit flag instead of 0
custom events now have a set-able timeout
previous lua hooks are restored
flags are stored in uint8_t
Change-Id: I9ca45246ac9233a2faa581e26a8cc5e5fb512e88
Diffstat (limited to 'apps')
-rw-r--r-- | apps/plugins/lua/rocklib_events.c | 586 |
1 files changed, 305 insertions, 281 deletions
diff --git a/apps/plugins/lua/rocklib_events.c b/apps/plugins/lua/rocklib_events.c index 3d893bb7f9..961fea030c 100644 --- a/apps/plugins/lua/rocklib_events.c +++ b/apps/plugins/lua/rocklib_events.c | |||
@@ -47,10 +47,9 @@ | |||
47 | * NOTE!, CUSTOM_EVENT must be unset manually | 47 | * NOTE!, CUSTOM_EVENT must be unset manually |
48 | * id is only passed to callback by custom and playback events | 48 | * id is only passed to callback by custom and playback events |
49 | * | 49 | * |
50 | * rockev.suspend(["event"/nil][true/false]) passing nil suspends all events | 50 | * rockev.suspend(["event"/nil][true/false]) passing nil suspends all events. |
51 | * stops event from executing, any event before re-enabling will be lost. | 51 | * stops event from executing, any event before re-enabling will be lost. |
52 | * Passing false will clear the suspend as will | 52 | * Passing false, unregistering or re-registering an event will clear the suspend |
53 | * unregistering or re-registering an event (except suspend all) | ||
54 | * | 53 | * |
55 | * rockev.unregister(evX) | 54 | * rockev.unregister(evX) |
56 | * Use unregister(evX) to remove an event | 55 | * Use unregister(evX) to remove an event |
@@ -72,28 +71,39 @@ | |||
72 | #define EVENT_METATABLE "event metatable" | 71 | #define EVENT_METATABLE "event metatable" |
73 | 72 | ||
74 | #define EVENT_THREAD LUA_ROCKEVENTSNAME ".thread" | 73 | #define EVENT_THREAD LUA_ROCKEVENTSNAME ".thread" |
75 | #define EV_STACKSZ DEFAULT_STACK_SIZE | 74 | #define EV_STACKSZ (DEFAULT_STACK_SIZE * 2) |
76 | 75 | ||
77 | #define LUA_SUCCESS 0 | 76 | #define LUA_SUCCESS 0 |
78 | #define EV_TIMER_FREQ (TIMER_FREQ / HZ) | 77 | |
78 | #define EV_TIMER_TICKS 5 /* timer resolution */ | ||
79 | #define EV_TIMER_FREQ ((TIMER_FREQ / HZ) * EV_TIMER_TICKS) | ||
79 | #define EV_TICKS (HZ / 5) | 80 | #define EV_TICKS (HZ / 5) |
80 | #define EV_INPUT (HZ / 4) | 81 | #define EV_INPUT (HZ / 5) |
81 | //#define DEBUG_EV | 82 | |
82 | 83 | #define ENABLE_LUA_HOOK (LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT) | |
83 | enum e_thread_state_flags{ | 84 | #define DISABLE_LUA_HOOK (0) |
84 | THREAD_QUIT = 0x0, | 85 | |
85 | THREAD_YIELD = 0x1, | 86 | enum { |
86 | THREAD_TIMEREVENT = 0x2, | 87 | THREAD_ERROR = 0x0, |
87 | THREAD_PLAYBKEVENT = 0x4, | 88 | /* event & suspend states */ |
88 | THREAD_ACTEVENT = 0x8, | 89 | THREAD_ACTEVENT = 0x1, |
89 | THREAD_BUTEVENT = 0x10, | 90 | THREAD_BUTEVENT = 0x2, |
90 | THREAD_CUSTOMEVENT = 0x20, | 91 | THREAD_CUSTOMEVENT = 0x4, |
92 | THREAD_PLAYBKEVENT = 0x8, | ||
93 | THREAD_TIMEREVENT = 0x10, | ||
94 | //THREAD_AVAILEVENT = 0x20, | ||
91 | //THREAD_AVAILEVENT = 0x40, | 95 | //THREAD_AVAILEVENT = 0x40, |
92 | //THREAD_AVAILEVENT = 0x80, | 96 | //THREAD_AVAILEVENT = 0x80, |
93 | /* thread state holds 3 status items using masks and bitshifts */ | 97 | THREAD_EVENT_ALL = 0xFF, |
94 | THREAD_STATEMASK = 0x00FF, | 98 | /* thread states */ |
95 | THREAD_SUSPENDMASK = 0xFF00, | 99 | //THREAD_AVAILSTATE = 0x1, |
96 | THREAD_INPUTMASK = 0xFF0000, | 100 | //THREAD_AVAILSTATE = 0x2, |
101 | //THREAD_AVAILSTATE = 0x4, | ||
102 | //THREAD_AVAILSTATE = 0x8, | ||
103 | //THREAD_AVAILSTATE = 0x10, | ||
104 | //THREAD_AVAILSTATE = 0x20, | ||
105 | THREAD_QUIT = 0x40, | ||
106 | //THREAD_AVAILSTATE = 0x80, | ||
97 | }; | 107 | }; |
98 | 108 | ||
99 | enum { | 109 | enum { |
@@ -105,83 +115,79 @@ enum { | |||
105 | EVENT_CT | 115 | EVENT_CT |
106 | }; | 116 | }; |
107 | 117 | ||
108 | static const unsigned char thread_ev_states[EVENT_CT] = | ||
109 | { | ||
110 | [ACTEVENT] = THREAD_ACTEVENT, | ||
111 | [BUTEVENT] = THREAD_BUTEVENT, | ||
112 | [CUSTOMEVENT] = THREAD_CUSTOMEVENT, | ||
113 | [PLAYBKEVENT] = THREAD_PLAYBKEVENT, | ||
114 | [TIMEREVENT] = THREAD_TIMEREVENT, | ||
115 | }; | ||
116 | |||
117 | static const char *const ev_map[EVENT_CT] = | ||
118 | { | ||
119 | [ACTEVENT] = "action", | ||
120 | [BUTEVENT] = "button", | ||
121 | [CUSTOMEVENT] = "custom", | ||
122 | [PLAYBKEVENT] = "playback", | ||
123 | [TIMEREVENT] = "timer", | ||
124 | }; | ||
125 | |||
126 | struct cb_data { | 118 | struct cb_data { |
127 | int cb_ref; | 119 | int cb_ref; |
128 | unsigned long id; | 120 | unsigned long id; |
129 | void *data; | 121 | void *data; |
122 | long next_tick; | ||
123 | long ticks; | ||
124 | }; | ||
125 | |||
126 | struct thread_status { | ||
127 | uint8_t event; | ||
128 | uint8_t suspended; | ||
129 | uint8_t thread; | ||
130 | uint8_t unused; | ||
130 | }; | 131 | }; |
131 | 132 | ||
132 | struct event_data { | 133 | struct event_data { |
133 | /* lua */ | 134 | /* lua */ |
134 | lua_State *L; | 135 | lua_State *L; |
135 | lua_State *NEWL; | 136 | lua_State *NEWL; |
136 | /* rockbox */ | 137 | /* rockbox */ |
137 | unsigned int thread_id; | 138 | unsigned int thread_id; |
138 | int thread_state; | 139 | long *event_stack; |
139 | long *event_stack; | 140 | volatile long *get_tick; |
140 | long timer_ticks; | 141 | struct thread_status status; |
141 | short freq_input; | 142 | char next_event; |
142 | short next_input; | ||
143 | short next_event; | ||
144 | /* callbacks */ | 143 | /* callbacks */ |
145 | struct cb_data *cb[EVENT_CT]; | 144 | struct cb_data *cb[EVENT_CT]; |
146 | }; | 145 | }; |
147 | 146 | ||
147 | static void set_event_meta(lua_State *L); | ||
148 | static struct event_data ev_data; | 148 | static struct event_data ev_data; |
149 | static struct mutex rev_mtx SHAREDBSS_ATTR; | 149 | static struct mutex rev_mtx SHAREDBSS_ATTR; |
150 | 150 | ||
151 | #ifdef DEBUG_EV | 151 | static inline uint8_t event_flag(unsigned int event) |
152 | static int dbg_hook_calls = 0; | 152 | { |
153 | #endif | 153 | return (1UL << event) & 0xFF; |
154 | } | ||
154 | 155 | ||
155 | static inline bool has_event(int ev_flag) | 156 | static inline bool has_thread_status(uint8_t ev_flag) |
156 | { | 157 | { |
157 | return ((THREAD_STATEMASK & (ev_data.thread_state & ev_flag)) == ev_flag); | 158 | return (ev_data.status.thread & ev_flag) == ev_flag; |
158 | } | 159 | } |
159 | 160 | ||
160 | static inline bool is_suspend(int ev_flag) | 161 | static inline void set_evt(uint8_t ev_flag) |
161 | { | 162 | { |
162 | ev_flag <<= 8; | 163 | ev_data.status.event |= ev_flag; |
163 | return ((THREAD_SUSPENDMASK & (ev_data.thread_state & ev_flag)) == ev_flag); | ||
164 | } | 164 | } |
165 | 165 | ||
166 | static void init_event_data(lua_State *L, struct event_data *ev_data) | 166 | static inline bool remove_evt(uint8_t ev_flag) |
167 | { | 167 | { |
168 | /* lua */ | 168 | /* returns previous flag status and clears it */ |
169 | ev_data->L = L; | 169 | bool has_flag = (ev_data.status.event & ev_flag) == ev_flag; |
170 | //ev_data->NEWL = NULL; | 170 | ev_data.status.event &= ~(ev_flag); |
171 | /* rockbox */ | 171 | return has_flag; |
172 | ev_data->thread_id = UINT_MAX; | 172 | } |
173 | ev_data->thread_state = THREAD_YIELD | THREAD_SUSPENDMASK; | 173 | |
174 | //ev_data->event_stack = NULL; | 174 | static inline bool is_suspend(uint8_t ev_flag) |
175 | //ev_data->timer_ticks = 0; | 175 | { |
176 | ev_data->freq_input = EV_INPUT; | 176 | return (ev_data.status.suspended & ev_flag) == ev_flag; |
177 | ev_data->next_input = EV_INPUT; | ||
178 | ev_data->next_event = EV_TICKS; | ||
179 | /* callbacks */ | ||
180 | for (int i= 0; i < EVENT_CT; i++) | ||
181 | ev_data->cb[i] = NULL; | ||
182 | } | 177 | } |
183 | 178 | ||
184 | /* lock and unlock routines allow us to execute the event thread without | 179 | static void suspend_evt(uint8_t ev_flag, bool suspend) |
180 | { | ||
181 | if(!suspend && !has_thread_status(THREAD_QUIT)) | ||
182 | { | ||
183 | ev_data.status.suspended &= ~(ev_flag); | ||
184 | ev_flag = 0; | ||
185 | } | ||
186 | remove_evt(ev_flag); | ||
187 | ev_data.status.suspended |= ev_flag; | ||
188 | } | ||
189 | |||
190 | /* mutex lock and unlock routines allow us to execute the event thread without | ||
185 | * trashing the lua state on error, yield, or sleep in the callback functions */ | 191 | * trashing the lua state on error, yield, or sleep in the callback functions */ |
186 | 192 | ||
187 | static inline void rev_lock_mtx(void) | 193 | static inline void rev_lock_mtx(void) |
@@ -194,80 +200,108 @@ static inline void rev_unlock_mtx(void) | |||
194 | rb->mutex_unlock(&rev_mtx); | 200 | rb->mutex_unlock(&rev_mtx); |
195 | } | 201 | } |
196 | 202 | ||
197 | static void lua_interrupt_callback( lua_State *L, lua_Debug *ar) | 203 | static void lua_interrupt_callback(lua_State *L, lua_Debug *ar) |
198 | { | 204 | { |
199 | (void) L; | ||
200 | (void) ar; | 205 | (void) ar; |
201 | #ifdef DEBUG_EV | ||
202 | dbg_hook_calls++; | ||
203 | #endif | ||
204 | 206 | ||
205 | rb->yield(); | 207 | rb->yield(); |
206 | 208 | ||
207 | rev_lock_mtx(); | 209 | rev_lock_mtx(); |
208 | rev_unlock_mtx(); /* must wait till event thread is done to continue */ | 210 | rev_unlock_mtx(); /* must wait till event thread is done to continue */ |
209 | 211 | ||
210 | #ifdef DEBUG_EV | ||
211 | rb->splashf(0, "spin %d, hooked %d", dbg_hook_calls, (lua_gethookmask(L) != 0)); | ||
212 | unsigned char delay = -1; | ||
213 | /* we can't sleep or yield without affecting count so lets spin in a loop */ | ||
214 | while(delay > 0) {delay--;} | ||
215 | if (lua_gethookmask(L) == 0) | ||
216 | dbg_hook_calls = 0; | ||
217 | #endif | ||
218 | |||
219 | /* if callback error, pass error to the main lua state */ | 212 | /* if callback error, pass error to the main lua state */ |
220 | if (lua_status(ev_data.NEWL) != LUA_SUCCESS) | 213 | if (lua_status(ev_data.NEWL) != LUA_SUCCESS) |
221 | luaL_error (L, lua_tostring (ev_data.NEWL, -1)); | 214 | luaL_error(L, lua_tostring(ev_data.NEWL, -1)); |
222 | } | 215 | } |
223 | 216 | ||
224 | static void lua_interrupt_set(lua_State *L, bool is_enabled) | 217 | static void lua_interrupt_set(lua_State *L, int hookmask) |
225 | { | 218 | { |
226 | const int hookmask = LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT; | 219 | lua_Hook hook; |
220 | int count; | ||
221 | static lua_Hook oldhook = NULL; | ||
222 | static int oldmask = 0; | ||
223 | static int oldcount = 0; | ||
227 | 224 | ||
228 | if (is_enabled) | 225 | if (hookmask == ENABLE_LUA_HOOK) |
229 | lua_sethook(L, lua_interrupt_callback, hookmask, 1 ); | 226 | { |
227 | hook = lua_gethook(L); | ||
228 | if (hook == lua_interrupt_callback) | ||
229 | return; /* our hook is already active */ | ||
230 | /* preserve prior hook */ | ||
231 | oldhook = hook; | ||
232 | oldmask = lua_gethookmask(L); | ||
233 | oldcount = lua_gethookcount(L); | ||
234 | hook = lua_interrupt_callback; | ||
235 | count = 1; | ||
236 | } | ||
230 | else | 237 | else |
231 | lua_sethook(L, NULL, 0, 0 ); | 238 | { |
239 | hook = oldhook; | ||
240 | hookmask = oldmask; | ||
241 | count = oldcount; | ||
242 | } | ||
243 | |||
244 | lua_sethook(L, hook, hookmask, count); | ||
232 | } | 245 | } |
233 | 246 | ||
234 | static int lua_rev_callback(lua_State *L, struct cb_data *cbd) | 247 | static int lua_rev_callback(lua_State *L, struct cb_data *evt) |
235 | { | 248 | { |
236 | int lua_status = LUA_ERRRUN; | 249 | int lua_status = LUA_ERRRUN; |
237 | 250 | ||
238 | if (L != NULL) | 251 | /* load cb function from lua registry */ |
239 | { | 252 | lua_rawgeti(L, LUA_REGISTRYINDEX, evt->cb_ref); |
240 | /* load cb function from lua registry */ | ||
241 | lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->cb_ref); | ||
242 | 253 | ||
243 | lua_pushinteger(L, cbd->id); | 254 | lua_pushinteger(L, evt->id); |
244 | lua_pushlightuserdata (L, cbd->data); | 255 | lua_pushlightuserdata(L, evt->data); |
256 | |||
257 | lua_status = lua_resume(L, 2); /* call the saved function */ | ||
258 | if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */ | ||
259 | luaL_where(L, 1); /* push error string on stack */ | ||
245 | 260 | ||
246 | lua_status = lua_resume(L, 2); | ||
247 | if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */ | ||
248 | luaL_where(L, 0); /* push error string on stack */ | ||
249 | } | ||
250 | return lua_status; | 261 | return lua_status; |
251 | } | 262 | } |
252 | 263 | ||
264 | /* timer interrupt callback */ | ||
265 | static void rev_timer_isr(void) | ||
266 | { | ||
267 | uint8_t ev_flag = 0; | ||
268 | long curr_tick = *(ev_data.get_tick); | ||
269 | struct cb_data *evt; | ||
270 | |||
271 | for (unsigned int i= 0; i < EVENT_CT; i++) | ||
272 | { | ||
273 | if (!is_suspend(event_flag(i))) | ||
274 | { | ||
275 | evt = ev_data.cb[i]; | ||
276 | if(evt->ticks > 0 && TIME_AFTER(curr_tick, evt->next_tick)) | ||
277 | ev_flag |= event_flag(i); | ||
278 | } | ||
279 | } | ||
280 | set_evt(ev_flag); | ||
281 | if (--ev_data.next_event <= 0 && ev_data.status.event) | ||
282 | lua_interrupt_set(ev_data.L, ENABLE_LUA_HOOK); | ||
283 | } | ||
284 | |||
253 | static void event_thread(void) | 285 | static void event_thread(void) |
254 | { | 286 | { |
255 | unsigned long action; | 287 | unsigned long action; |
256 | int event; | 288 | uint8_t ev_flag; |
257 | int ev_flag; | 289 | unsigned int event; |
290 | struct cb_data *evt; | ||
258 | 291 | ||
259 | while(ev_data.thread_state != THREAD_QUIT && lua_status(ev_data.L) == LUA_SUCCESS) | 292 | while(!has_thread_status(THREAD_QUIT) && lua_status(ev_data.L) == LUA_SUCCESS) |
260 | { | 293 | { |
261 | rev_lock_mtx(); | 294 | rev_lock_mtx(); |
262 | lua_interrupt_set(ev_data.L, true); | 295 | |
296 | lua_interrupt_set(ev_data.L, ENABLE_LUA_HOOK); | ||
263 | 297 | ||
264 | for (event = 0; event < EVENT_CT; event++) | 298 | for (event = 0; event < EVENT_CT; event++) |
265 | { | 299 | { |
266 | ev_flag = thread_ev_states[event]; | 300 | ev_flag = event_flag(event); |
267 | if (!has_event(ev_flag) || is_suspend(ev_flag)) | 301 | if (!remove_evt(ev_flag) || is_suspend(ev_flag)) |
268 | continue; /* check next event */ | 302 | continue; /* check next event */ |
269 | 303 | ||
270 | ev_data.thread_state &= ~(ev_flag); /* event handled */ | 304 | evt = ev_data.cb[event]; |
271 | 305 | ||
272 | switch (event) | 306 | switch (event) |
273 | { | 307 | { |
@@ -278,118 +312,91 @@ static void event_thread(void) | |||
278 | else if (action == ACTION_NONE) | 312 | else if (action == ACTION_NONE) |
279 | { | 313 | { |
280 | /* only send ACTION_NONE once */ | 314 | /* only send ACTION_NONE once */ |
281 | if (ev_data.cb[ACTEVENT]->id == ACTION_NONE || | 315 | if (evt->id == ACTION_NONE || rb->button_status() != 0) |
282 | rb->button_status() != 0) | 316 | goto skip_callback; /* check next event */ |
283 | continue; /* check next event */ | ||
284 | } | 317 | } |
285 | ev_data.cb[ACTEVENT]->id = action; | 318 | evt->id = action; |
286 | break; | 319 | break; |
287 | case BUTEVENT: | 320 | case BUTEVENT: |
288 | ev_data.cb[BUTEVENT]->id = rb->button_get(false); | 321 | evt->id = rb->button_get(false); |
289 | if (ev_data.cb[BUTEVENT]->id == BUTTON_NONE) | 322 | if (evt->id == BUTTON_NONE) |
290 | continue; /* check next event */ | 323 | goto skip_callback; /* check next event */ |
291 | break; | 324 | break; |
292 | case CUSTOMEVENT: | 325 | case CUSTOMEVENT: |
293 | ev_data.thread_state |= thread_ev_states[CUSTOMEVENT]; // don't reset */ | ||
294 | break; | ||
295 | case PLAYBKEVENT: | 326 | case PLAYBKEVENT: |
296 | break; | ||
297 | case TIMEREVENT: | 327 | case TIMEREVENT: |
298 | ev_data.cb[TIMEREVENT]->id = *rb->current_tick + ev_data.timer_ticks; | ||
299 | break; | 328 | break; |
300 | 329 | ||
301 | } | 330 | } |
302 | 331 | ||
303 | if (lua_rev_callback(ev_data.NEWL, ev_data.cb[event]) != LUA_SUCCESS) | 332 | if (lua_rev_callback(ev_data.NEWL, evt) != LUA_SUCCESS) |
304 | { | 333 | { |
305 | rev_unlock_mtx(); | 334 | rev_unlock_mtx(); |
306 | goto event_error; | 335 | goto event_error; |
307 | } | 336 | } |
337 | skip_callback: | ||
338 | evt->next_tick = *(ev_data.get_tick) + evt->ticks; | ||
308 | } | 339 | } |
309 | rev_unlock_mtx(); /* we are safe to release back to main lua state */ | 340 | rev_unlock_mtx(); /* we are safe to release back to main lua state */ |
310 | 341 | ||
311 | do | 342 | do |
312 | { | 343 | { |
313 | #ifdef DEBUG_EV | 344 | lua_interrupt_set(ev_data.L, DISABLE_LUA_HOOK); |
314 | dbg_hook_calls--; | ||
315 | #endif | ||
316 | lua_interrupt_set(ev_data.L, false); | ||
317 | ev_data.next_event = EV_TICKS; | 345 | ev_data.next_event = EV_TICKS; |
318 | rb->yield(); | 346 | rb->yield(); |
319 | } while(ev_data.thread_state == THREAD_YIELD || is_suspend(THREAD_SUSPENDMASK >> 8)); | 347 | } while (!has_thread_status(THREAD_QUIT) && (is_suspend(THREAD_EVENT_ALL) |
348 | || !ev_data.status.event)); | ||
320 | 349 | ||
321 | } | 350 | } |
351 | rb->yield(); | ||
352 | lua_interrupt_set(ev_data.L, DISABLE_LUA_HOOK); | ||
322 | 353 | ||
323 | event_error: | 354 | event_error: |
324 | 355 | ||
325 | /* thread is exiting -- clean up */ | 356 | /* thread is exiting -- clean up */ |
326 | rb->timer_unregister(); | 357 | rb->timer_unregister(); |
327 | //rb->yield(); | ||
328 | rb->thread_exit(); | 358 | rb->thread_exit(); |
329 | 359 | ||
330 | return; | 360 | return; |
331 | } | 361 | } |
332 | 362 | ||
333 | /* timer interrupt callback */ | 363 | static inline void create_event_thread_ref(struct event_data *ev_data) |
334 | static void rev_timer_isr(void) | ||
335 | { | ||
336 | if (!is_suspend(THREAD_SUSPENDMASK >> 8)) /* all events suspended? */ | ||
337 | { | ||
338 | ev_data.next_event--; | ||
339 | ev_data.next_input--; | ||
340 | |||
341 | if (ev_data.next_input <=0) | ||
342 | { | ||
343 | ev_data.thread_state |= ((ev_data.thread_state & THREAD_INPUTMASK) >> 16); | ||
344 | ev_data.next_input = ev_data.freq_input; | ||
345 | } | ||
346 | |||
347 | if (ev_data.cb[TIMEREVENT] != NULL && !is_suspend(TIMEREVENT)) | ||
348 | { | ||
349 | if (TIME_AFTER(*rb->current_tick, ev_data.cb[TIMEREVENT]->id)) | ||
350 | { | ||
351 | ev_data.thread_state |= thread_ev_states[TIMEREVENT]; | ||
352 | ev_data.next_event = 0; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | if (ev_data.next_event <= 0) | ||
357 | lua_interrupt_set(ev_data.L, true); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | static void create_event_thread_ref(struct event_data *ev_data) | ||
362 | { | 364 | { |
363 | lua_State *L = ev_data->L; | 365 | lua_State *L = ev_data->L; |
364 | 366 | ||
365 | lua_createtable(L, 2, 0); | 367 | lua_createtable(L, 2, 0); |
366 | 368 | ||
367 | ev_data->event_stack = (long *) lua_newuserdata (L, EV_STACKSZ); | 369 | ev_data->event_stack = (long *) lua_newuserdata(L, EV_STACKSZ); |
368 | 370 | ||
369 | /* attach EVENT_METATABLE to ud so we get notified on garbage collection */ | 371 | /* attach EVENT_METATABLE to ud so we get notified on garbage collection */ |
370 | luaL_getmetatable (L, EVENT_METATABLE); | 372 | set_event_meta(L); |
371 | lua_setmetatable (L, -2); | ||
372 | lua_rawseti(L, -2, 1); | 373 | lua_rawseti(L, -2, 1); |
373 | 374 | ||
374 | ev_data->NEWL = lua_newthread(L); | 375 | ev_data->NEWL = lua_newthread(L); |
375 | lua_rawseti(L, -2, 2); | 376 | lua_rawseti(L, -2, 2); |
376 | 377 | ||
377 | lua_setfield (L, LUA_REGISTRYINDEX, EVENT_THREAD); /* store references */ | 378 | lua_setfield(L, LUA_REGISTRYINDEX, EVENT_THREAD); /* store references */ |
378 | } | 379 | } |
379 | 380 | ||
380 | static void destroy_event_thread_ref(struct event_data *ev_data) | 381 | static inline void destroy_event_thread_ref(struct event_data *ev_data) |
381 | { | 382 | { |
382 | lua_State *L = ev_data->L; | 383 | lua_State *L = ev_data->L; |
383 | ev_data->event_stack = NULL; | 384 | ev_data->event_stack = NULL; |
384 | ev_data->NEWL = NULL; | 385 | ev_data->NEWL = NULL; |
385 | lua_pushnil(L); | 386 | lua_pushnil(L); |
386 | lua_setfield (L, LUA_REGISTRYINDEX, EVENT_THREAD); /* free references */ | 387 | lua_setfield(L, LUA_REGISTRYINDEX, EVENT_THREAD); /* free references */ |
387 | } | 388 | } |
388 | 389 | ||
389 | static void exit_event_thread(struct event_data *ev_data) | 390 | static inline void exit_event_thread(struct event_data *ev_data) |
390 | { | 391 | { |
391 | ev_data->thread_state = THREAD_QUIT; | 392 | suspend_evt(THREAD_EVENT_ALL, true); |
393 | ev_data->status.thread = THREAD_QUIT; | ||
394 | |||
392 | rb->thread_wait(ev_data->thread_id); /* wait for thread to exit */ | 395 | rb->thread_wait(ev_data->thread_id); /* wait for thread to exit */ |
396 | destroy_event_thread_ref(ev_data); | ||
397 | |||
398 | ev_data->status.thread = 0; | ||
399 | ev_data->thread_id = UINT_MAX; | ||
393 | } | 400 | } |
394 | 401 | ||
395 | static void init_event_thread(bool init, struct event_data *ev_data) | 402 | static void init_event_thread(bool init, struct event_data *ev_data) |
@@ -397,23 +404,14 @@ static void init_event_thread(bool init, struct event_data *ev_data) | |||
397 | if (ev_data->event_stack != NULL) /* make sure we don't double free */ | 404 | if (ev_data->event_stack != NULL) /* make sure we don't double free */ |
398 | { | 405 | { |
399 | if (!init && ev_data->thread_id != UINT_MAX) | 406 | if (!init && ev_data->thread_id != UINT_MAX) |
400 | { | ||
401 | ev_data->thread_state |= THREAD_SUSPENDMASK; /* suspend all events */ | ||
402 | rb->yield(); | ||
403 | exit_event_thread(ev_data); | 407 | exit_event_thread(ev_data); |
404 | destroy_event_thread_ref(ev_data); | 408 | |
405 | lua_interrupt_set(ev_data->L, false); | ||
406 | ev_data->thread_state = THREAD_YIELD | THREAD_SUSPENDMASK; | ||
407 | ev_data->thread_id = UINT_MAX; | ||
408 | } | ||
409 | return; | 409 | return; |
410 | } | 410 | } |
411 | else if (!init || ev_data->thread_state == THREAD_QUIT) | 411 | else if (!init) |
412 | return; | 412 | return; |
413 | 413 | ||
414 | create_event_thread_ref(ev_data); | 414 | create_event_thread_ref(ev_data); |
415 | if (ev_data->NEWL == NULL || ev_data->event_stack == NULL) | ||
416 | return; | ||
417 | 415 | ||
418 | ev_data->thread_id = rb->create_thread(&event_thread, | 416 | ev_data->thread_id = rb->create_thread(&event_thread, |
419 | ev_data->event_stack, | 417 | ev_data->event_stack, |
@@ -424,27 +422,26 @@ static void init_event_thread(bool init, struct event_data *ev_data) | |||
424 | IF_COP(, COP)); | 422 | IF_COP(, COP)); |
425 | 423 | ||
426 | /* Timer is used to poll waiting events */ | 424 | /* Timer is used to poll waiting events */ |
427 | rb->timer_register(0, NULL, EV_TIMER_FREQ, rev_timer_isr IF_COP(, CPU)); | 425 | rb->timer_register(1, NULL, EV_TIMER_FREQ, rev_timer_isr IF_COP(, CPU)); |
428 | ev_data->thread_state &= ~THREAD_SUSPENDMASK; | ||
429 | } | 426 | } |
430 | 427 | ||
431 | static void playback_event_callback(unsigned short id, void *data) | 428 | static void playback_event_callback(unsigned short id, void *data) |
432 | { | 429 | { |
433 | /* playback events are synchronous we need to return ASAP so set a flag */ | 430 | /* playback events are synchronous we need to return ASAP so set a flag */ |
434 | if (!is_suspend(THREAD_PLAYBKEVENT)) /* playback events suspended? */ | 431 | struct cb_data *evt = ev_data.cb[PLAYBKEVENT]; |
432 | evt->id = id; | ||
433 | evt->data = data; | ||
434 | if (!is_suspend(THREAD_PLAYBKEVENT)) | ||
435 | { | 435 | { |
436 | ev_data.thread_state |= thread_ev_states[PLAYBKEVENT]; | 436 | set_evt(THREAD_PLAYBKEVENT); |
437 | ev_data.cb[PLAYBKEVENT]->id = id; | 437 | lua_interrupt_set(ev_data.L, ENABLE_LUA_HOOK); |
438 | ev_data.cb[PLAYBKEVENT]->data = data; | ||
439 | lua_interrupt_set(ev_data.L, true); | ||
440 | } | 438 | } |
441 | } | 439 | } |
442 | 440 | ||
443 | static void register_playbk_events(int flag_events, | 441 | static void register_playbk_events(int flag_events) |
444 | void (*handler)(unsigned short id, void *data)) | ||
445 | { | 442 | { |
446 | long unsigned int i = 0; | 443 | unsigned short i, pb_evt; |
447 | const unsigned short playback_events[7] = | 444 | static const unsigned short playback_events[7] = |
448 | { /*flags*/ | 445 | { /*flags*/ |
449 | PLAYBACK_EVENT_START_PLAYBACK, /* 0x1 */ | 446 | PLAYBACK_EVENT_START_PLAYBACK, /* 0x1 */ |
450 | PLAYBACK_EVENT_TRACK_BUFFER, /* 0x2 */ | 447 | PLAYBACK_EVENT_TRACK_BUFFER, /* 0x2 */ |
@@ -455,43 +452,34 @@ static void register_playbk_events(int flag_events, | |||
455 | PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE /* 0x40*/ | 452 | PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE /* 0x40*/ |
456 | }; | 453 | }; |
457 | 454 | ||
458 | for(; i < ARRAYLEN(playback_events); i++, flag_events >>= 1) | 455 | for(i = 0; i < ARRAYLEN(playback_events); i++) |
459 | { | 456 | { |
460 | if (flag_events == 0) /* remove events */ | 457 | pb_evt = playback_events[i]; |
461 | rb->remove_event(playback_events[i], handler); | 458 | if (!(flag_events & (1 << i))) |
462 | else /* add events */ | 459 | rb->remove_event(pb_evt, playback_event_callback); |
463 | if ((flag_events & 0x1) == 0x1) | 460 | else |
464 | rb->add_event(playback_events[i], handler); | 461 | rb->add_event(pb_evt, playback_event_callback); |
465 | } | 462 | } |
466 | } | 463 | } |
467 | 464 | ||
468 | static void destroy_event_userdata(lua_State *L, int event) | 465 | static void destroy_event_userdata(lua_State *L, unsigned int event) |
469 | { | 466 | { |
470 | if (ev_data.cb[event] != NULL) | 467 | uint8_t ev_flag = event_flag(event); |
471 | { | 468 | struct cb_data *evt = ev_data.cb[event]; |
472 | int ev_flag = thread_ev_states[event]; | 469 | suspend_evt(ev_flag, true); |
473 | int ev_clear = (ev_flag | (ev_flag << 16)); | 470 | if (evt != NULL) |
474 | if (!is_suspend(THREAD_SUSPENDMASK >> 8)) /* all events suspended? */ | 471 | luaL_unref(L, LUA_REGISTRYINDEX, evt->cb_ref); |
475 | ev_clear |= (ev_flag << 8); | 472 | |
476 | ev_data.thread_state &= ~(ev_clear); | 473 | ev_data.cb[event] = NULL; |
477 | luaL_unref (L, LUA_REGISTRYINDEX, ev_data.cb[event]->cb_ref); | ||
478 | ev_data.cb[event] = NULL; | ||
479 | } | ||
480 | } | 474 | } |
481 | 475 | ||
482 | static void create_event_userdata(lua_State *L, int event, int index) | 476 | static void create_event_userdata(lua_State *L, unsigned int event, int index) |
483 | { | 477 | { |
484 | /* if function is already registered , unregister it */ | 478 | /* if function is already registered , unregister it */ |
485 | destroy_event_userdata(L, event); | 479 | destroy_event_userdata(L, event); |
486 | 480 | ||
487 | if (!lua_isfunction (L, index)) | 481 | luaL_checktype(L, index, LUA_TFUNCTION); |
488 | { | 482 | lua_pushvalue(L, index); /* copy passed lua function on top of stack */ |
489 | init_event_thread(false, &ev_data); | ||
490 | luaL_typerror (L, index, "function"); | ||
491 | return; | ||
492 | } | ||
493 | |||
494 | lua_pushvalue (L, index); /* copy passed lua function on top of stack */ | ||
495 | int ref_lua = luaL_ref(L, LUA_REGISTRYINDEX); | 483 | int ref_lua = luaL_ref(L, LUA_REGISTRYINDEX); |
496 | 484 | ||
497 | ev_data.cb[event] = (struct cb_data *)lua_newuserdata(L, sizeof(struct cb_data)); | 485 | ev_data.cb[event] = (struct cb_data *)lua_newuserdata(L, sizeof(struct cb_data)); |
@@ -499,144 +487,181 @@ static void create_event_userdata(lua_State *L, int event, int index) | |||
499 | ev_data.cb[event]->cb_ref = ref_lua; /* store ref for later call/release */ | 487 | ev_data.cb[event]->cb_ref = ref_lua; /* store ref for later call/release */ |
500 | 488 | ||
501 | /* attach EVENT_METATABLE to ud so we get notified on garbage collection */ | 489 | /* attach EVENT_METATABLE to ud so we get notified on garbage collection */ |
502 | luaL_getmetatable (L, EVENT_METATABLE); | 490 | set_event_meta(L); |
503 | lua_setmetatable (L, -2); | ||
504 | /* cb_data is on top of stack */ | 491 | /* cb_data is on top of stack */ |
505 | } | 492 | } |
506 | 493 | ||
507 | static int rockev_gc(lua_State *L) { | 494 | static int rockev_gc(lua_State *L) { |
508 | bool has_events = false; | 495 | bool has_events = false; |
509 | void *d = (void *) lua_touserdata (L, 1); | 496 | void *d = (void *) lua_touserdata(L, 1); |
510 | 497 | ||
511 | if (d == NULL) | 498 | if (d != NULL) |
512 | { | ||
513 | return 0; | ||
514 | } | ||
515 | else if (d == ev_data.event_stack) /* thread stack is gc'd kill thread */ | ||
516 | { | ||
517 | init_event_thread(false, &ev_data); | ||
518 | } | ||
519 | else if (d == ev_data.cb[PLAYBKEVENT]) | ||
520 | { | 499 | { |
521 | register_playbk_events(0, &playback_event_callback); | 500 | for (unsigned int i= 0; i < EVENT_CT; i++) |
501 | { | ||
502 | if (d == ev_data.cb[i] || d == ev_data.event_stack) | ||
503 | { | ||
504 | if (i == PLAYBKEVENT) | ||
505 | register_playbk_events(0); | ||
506 | destroy_event_userdata(L, i); | ||
507 | } | ||
508 | else if (ev_data.cb[i] != NULL) | ||
509 | has_events = true; | ||
510 | } | ||
511 | |||
512 | if (!has_events) /* nothing to wait for kill thread */ | ||
513 | init_event_thread(false, &ev_data); | ||
522 | } | 514 | } |
515 | return 0; | ||
516 | } | ||
523 | 517 | ||
524 | for( int i= 0; i < EVENT_CT; i++) | 518 | static void set_event_meta(lua_State *L) |
519 | { | ||
520 | if (luaL_newmetatable(L, EVENT_METATABLE)) | ||
525 | { | 521 | { |
526 | if (d == ev_data.cb[i]) | 522 | /* set __gc field so we can clean-up our objects */ |
527 | destroy_event_userdata(L, i); | 523 | lua_pushcfunction(L, rockev_gc); |
528 | else if (ev_data.cb[i] != NULL) | 524 | lua_setfield(L, -2, "__gc"); |
529 | has_events = true; | ||
530 | } | 525 | } |
526 | lua_setmetatable(L, -2); | ||
527 | } | ||
528 | |||
529 | static void init_event_data(lua_State *L, struct event_data *ev_data) | ||
530 | { | ||
531 | /* lua */ | ||
532 | ev_data->L = L; | ||
533 | /*ev_data->NEWL = NULL;*/ | ||
534 | /* rockbox */ | ||
535 | ev_data->thread_id = UINT_MAX; | ||
536 | ev_data->get_tick = rb->current_tick; | ||
531 | 537 | ||
532 | if (!has_events) /* nothing to wait for kill thread */ | 538 | ev_data->status.event = 0; |
533 | init_event_thread(false, &ev_data); | 539 | ev_data->status.suspended = THREAD_EVENT_ALL; |
540 | ev_data->status.thread = 0; | ||
534 | 541 | ||
535 | return 0; | 542 | /*ev_data->event_stack = NULL;*/ |
543 | ev_data->next_event = EV_TICKS; | ||
544 | /* callbacks */ | ||
545 | for (unsigned int i= 0; i < EVENT_CT; i++) | ||
546 | ev_data->cb[i] = NULL; | ||
536 | } | 547 | } |
537 | 548 | ||
538 | /****************************************************************************** | 549 | /****************************************************************************** |
539 | * LUA INTERFACE ************************************************************** | 550 | * LUA INTERFACE ************************************************************** |
540 | ******************************************************************************* | 551 | ******************************************************************************* |
541 | */ | 552 | */ |
553 | static unsigned int get_event_by_name(lua_State *L) | ||
554 | { | ||
555 | static const char *const ev_map[EVENT_CT] = | ||
556 | { | ||
557 | [ACTEVENT] = "action", | ||
558 | [BUTEVENT] = "button", | ||
559 | [CUSTOMEVENT] = "custom", | ||
560 | [PLAYBKEVENT] = "playback", | ||
561 | [TIMEREVENT] = "timer", | ||
562 | }; | ||
563 | |||
564 | return luaL_checkoption(L, 1, NULL, ev_map); | ||
565 | } | ||
566 | |||
542 | 567 | ||
543 | static int rockev_register(lua_State *L) | 568 | static int rockev_register(lua_State *L) |
544 | { | 569 | { |
545 | int event = luaL_checkoption(L, 1, NULL, ev_map); | 570 | /* register (event, cb [, args] */ |
546 | int ev_flag = thread_ev_states[event]; | 571 | unsigned int event = get_event_by_name(L); |
572 | uint8_t ev_flag = event_flag(event); | ||
547 | int playbk_events; | 573 | int playbk_events; |
548 | 574 | ||
549 | lua_settop (L, 3); /* we need to lock our optional args before...*/ | 575 | lua_settop(L, 3); /* we need to lock our optional args before...*/ |
550 | create_event_userdata(L, event, 2);/* cb_data is on top of stack */ | 576 | create_event_userdata(L, event, 2);/* cb_data is on top of stack */ |
577 | init_event_thread(!has_thread_status(THREAD_QUIT), &ev_data); | ||
578 | |||
579 | long event_ticks = 0; | ||
580 | struct cb_data *evt; | ||
551 | 581 | ||
552 | switch (event) | 582 | switch (event) |
553 | { | 583 | { |
554 | case ACTEVENT: | 584 | case ACTEVENT: |
555 | /* fall through */ | 585 | /* fall through */ |
556 | case BUTEVENT: | 586 | case BUTEVENT: |
557 | ev_data.freq_input = luaL_optinteger(L, 3, EV_INPUT); | 587 | event_ticks = luaL_optinteger(L, 3, EV_INPUT); |
558 | if (ev_data.freq_input < HZ / 20) ev_data.freq_input = HZ / 20; | ||
559 | ev_data.thread_state |= (ev_flag | (ev_flag << 16)); | ||
560 | break; | 588 | break; |
561 | case CUSTOMEVENT: | 589 | case CUSTOMEVENT: |
590 | event_ticks = luaL_optinteger(L, 3, EV_TIMER_TICKS); | ||
591 | ev_flag = 0; /* don't remove suspend */ | ||
562 | break; | 592 | break; |
563 | case PLAYBKEVENT: | 593 | case PLAYBKEVENT: /* see register_playbk_events() for flags */ |
564 | /* see register_playbk_events() for flags */ | 594 | event_ticks = 0; /* playback events are not triggered by timeout */ |
565 | playbk_events = luaL_optinteger(L, 3, 0x3F); | 595 | playbk_events = luaL_optinteger(L, 3, 0x3F); |
566 | register_playbk_events(playbk_events, &playback_event_callback); | 596 | register_playbk_events(playbk_events); |
567 | break; | 597 | break; |
568 | case TIMEREVENT: | 598 | case TIMEREVENT: |
569 | ev_data.timer_ticks = luaL_checkinteger(L, 3); | 599 | event_ticks = luaL_checkinteger(L, 3); |
570 | ev_data.cb[TIMEREVENT]->id = *rb->current_tick + ev_data.timer_ticks; | ||
571 | break; | 600 | break; |
572 | } | 601 | } |
573 | 602 | ||
574 | init_event_thread(true, &ev_data); | 603 | evt = ev_data.cb[event]; |
604 | evt->ticks = event_ticks; | ||
605 | evt->next_tick = *(ev_data.get_tick) + event_ticks; | ||
606 | suspend_evt(ev_flag, false); | ||
575 | 607 | ||
576 | return 1; /* returns cb_data */ | 608 | return 1; /* returns cb_data */ |
577 | } | 609 | } |
578 | 610 | ||
579 | static int rockev_suspend(lua_State *L) | 611 | static int rockev_suspend(lua_State *L) |
580 | { | 612 | { |
581 | if (ev_data.thread_state == THREAD_QUIT) | 613 | unsigned int event; /*Arg 1 is event pass nil to suspend all */ |
582 | return 0; | ||
583 | |||
584 | int event; /*Arg 1 is event pass nil to suspend all */ | ||
585 | bool suspend = luaL_optboolean(L, 2, true); | 614 | bool suspend = luaL_optboolean(L, 2, true); |
586 | int ev_flag = THREAD_SUSPENDMASK; | 615 | uint8_t ev_flag = THREAD_EVENT_ALL; |
587 | 616 | ||
588 | if (!lua_isnoneornil(L, 1)) | 617 | if (!lua_isnoneornil(L, 1)) |
589 | { | 618 | { |
590 | event = luaL_checkoption(L, 1, NULL, ev_map); | 619 | event = get_event_by_name(L); |
591 | ev_flag = thread_ev_states[event] << 8; | 620 | ev_flag = event_flag(event); |
592 | } | 621 | } |
593 | 622 | ||
594 | if (suspend) | 623 | suspend_evt(ev_flag, suspend); |
595 | ev_data.thread_state |= ev_flag; | 624 | |
596 | else | 625 | /* don't resume invalid events */ |
597 | ev_data.thread_state &= ~(ev_flag); | 626 | for (unsigned int i = 0; i < EVENT_CT; i++) |
627 | { | ||
628 | if (ev_data.cb[i] == NULL) | ||
629 | suspend_evt(event_flag(i), true); | ||
630 | } | ||
598 | 631 | ||
599 | return 0; | 632 | return 0; |
600 | } | 633 | } |
601 | 634 | ||
602 | static int rockev_trigger(lua_State *L) | 635 | static int rockev_trigger(lua_State *L) |
603 | { | 636 | { |
604 | int event = luaL_checkoption(L, 1, NULL, ev_map); | 637 | unsigned int event = get_event_by_name(L); |
605 | bool enable = luaL_optboolean(L, 2, true); | 638 | bool enable = luaL_optboolean(L, 2, true); |
606 | 639 | ||
607 | int ev_flag; | 640 | uint8_t ev_flag = event_flag(event); |
641 | struct cb_data *evt = ev_data.cb[event]; | ||
608 | 642 | ||
609 | /* protect from invalid events */ | 643 | /* don't trigger invalid events */ |
610 | if (ev_data.cb[event] != NULL) | 644 | if (evt != NULL) |
611 | { | 645 | { |
612 | ev_flag = thread_ev_states[event]; | ||
613 | /* allow user to pass an id to some of the callback functions */ | 646 | /* allow user to pass an id to some of the callback functions */ |
614 | ev_data.cb[event]->id = luaL_optinteger(L, 3, ev_data.cb[event]->id); | 647 | evt->id = luaL_optinteger(L, 3, evt->id); |
648 | |||
649 | if (event == CUSTOMEVENT) | ||
650 | suspend_evt(ev_flag, !enable); | ||
615 | 651 | ||
616 | if (enable) | 652 | if (enable) |
617 | ev_data.thread_state |= ev_flag; | 653 | set_evt(ev_flag); |
618 | else | 654 | else |
619 | ev_data.thread_state &= ~(ev_flag); | 655 | remove_evt(ev_flag); |
620 | } | 656 | } |
621 | return 0; | 657 | return 0; |
622 | } | 658 | } |
623 | 659 | ||
624 | static int rockev_unregister(lua_State *L) | 660 | static int rockev_unregister(lua_State *L) |
625 | { | 661 | { |
626 | luaL_checkudata (L, 1, EVENT_METATABLE); | 662 | luaL_checkudata(L, 1, EVENT_METATABLE); |
627 | rockev_gc(L); | 663 | rockev_gc(L); |
628 | lua_pushnil(L); | 664 | return 0; |
629 | return 1; | ||
630 | } | ||
631 | /* | ||
632 | ** Creates events metatable. | ||
633 | */ | ||
634 | static int event_create_meta (lua_State *L) { | ||
635 | luaL_newmetatable (L, EVENT_METATABLE); | ||
636 | /* set __gc field so we can clean-up our objects */ | ||
637 | lua_pushcfunction (L, rockev_gc); | ||
638 | lua_setfield (L, -2, "__gc"); | ||
639 | return 1; | ||
640 | } | 665 | } |
641 | 666 | ||
642 | static const struct luaL_reg evlib[] = { | 667 | static const struct luaL_reg evlib[] = { |
@@ -647,10 +672,9 @@ static const struct luaL_reg evlib[] = { | |||
647 | {NULL, NULL} | 672 | {NULL, NULL} |
648 | }; | 673 | }; |
649 | 674 | ||
650 | int luaopen_rockevents (lua_State *L) { | 675 | int luaopen_rockevents(lua_State *L) { |
651 | rb->mutex_init(&rev_mtx); | 676 | rb->mutex_init(&rev_mtx); |
652 | init_event_data(L, &ev_data); | 677 | init_event_data(L, &ev_data); |
653 | event_create_meta (L); | 678 | luaL_register(L, LUA_ROCKEVENTSNAME, evlib); |
654 | luaL_register (L, LUA_ROCKEVENTSNAME, evlib); | ||
655 | return 1; | 679 | return 1; |
656 | } | 680 | } |