summaryrefslogtreecommitdiff
path: root/apps/plugins/lua/rocklib_events.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/lua/rocklib_events.c')
-rw-r--r--apps/plugins/lua/rocklib_events.c586
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)
83enum e_thread_state_flags{ 84#define DISABLE_LUA_HOOK (0)
84 THREAD_QUIT = 0x0, 85
85 THREAD_YIELD = 0x1, 86enum {
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
99enum { 109enum {
@@ -105,83 +115,79 @@ enum {
105 EVENT_CT 115 EVENT_CT
106}; 116};
107 117
108static 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
117static 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
126struct cb_data { 118struct 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
126struct thread_status {
127 uint8_t event;
128 uint8_t suspended;
129 uint8_t thread;
130 uint8_t unused;
130}; 131};
131 132
132struct event_data { 133struct 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
147static void set_event_meta(lua_State *L);
148static struct event_data ev_data; 148static struct event_data ev_data;
149static struct mutex rev_mtx SHAREDBSS_ATTR; 149static struct mutex rev_mtx SHAREDBSS_ATTR;
150 150
151#ifdef DEBUG_EV 151static inline uint8_t event_flag(unsigned int event)
152static int dbg_hook_calls = 0; 152{
153#endif 153 return (1UL << event) & 0xFF;
154}
154 155
155static inline bool has_event(int ev_flag) 156static 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
160static inline bool is_suspend(int ev_flag) 161static 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
166static void init_event_data(lua_State *L, struct event_data *ev_data) 166static 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; 174static 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 179static 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
187static inline void rev_lock_mtx(void) 193static 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
197static void lua_interrupt_callback( lua_State *L, lua_Debug *ar) 203static 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
224static void lua_interrupt_set(lua_State *L, bool is_enabled) 217static 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
234static int lua_rev_callback(lua_State *L, struct cb_data *cbd) 247static 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 */
265static 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
253static void event_thread(void) 285static 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 }
337skip_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
323event_error: 354event_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 */ 363static inline void create_event_thread_ref(struct event_data *ev_data)
334static 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
361static 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
380static void destroy_event_thread_ref(struct event_data *ev_data) 381static 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
389static void exit_event_thread(struct event_data *ev_data) 390static 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
395static void init_event_thread(bool init, struct event_data *ev_data) 402static 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
431static void playback_event_callback(unsigned short id, void *data) 428static 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
443static void register_playbk_events(int flag_events, 441static 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
468static void destroy_event_userdata(lua_State *L, int event) 465static 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
482static void create_event_userdata(lua_State *L, int event, int index) 476static 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
507static int rockev_gc(lua_State *L) { 494static 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++) 518static 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
529static 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*/
553static 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
543static int rockev_register(lua_State *L) 568static 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
579static int rockev_suspend(lua_State *L) 611static 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
602static int rockev_trigger(lua_State *L) 635static 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
624static int rockev_unregister(lua_State *L) 660static 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*/
634static 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
642static const struct luaL_reg evlib[] = { 667static 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
650int luaopen_rockevents (lua_State *L) { 675int 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}