From f45db552f39579b31d428350f8853d52187f5733 Mon Sep 17 00:00:00 2001 From: William Wilgus Date: Fri, 11 Oct 2019 10:09:08 -0500 Subject: 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 --- apps/plugins/lua/rocklib_events.c | 586 ++++++++++++++++++++------------------ 1 file changed, 305 insertions(+), 281 deletions(-) (limited to 'apps/plugins/lua') 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 @@ * NOTE!, CUSTOM_EVENT must be unset manually * id is only passed to callback by custom and playback events * - * rockev.suspend(["event"/nil][true/false]) passing nil suspends all events + * rockev.suspend(["event"/nil][true/false]) passing nil suspends all events. * stops event from executing, any event before re-enabling will be lost. - * Passing false will clear the suspend as will - * unregistering or re-registering an event (except suspend all) + * Passing false, unregistering or re-registering an event will clear the suspend * * rockev.unregister(evX) * Use unregister(evX) to remove an event @@ -72,28 +71,39 @@ #define EVENT_METATABLE "event metatable" #define EVENT_THREAD LUA_ROCKEVENTSNAME ".thread" -#define EV_STACKSZ DEFAULT_STACK_SIZE +#define EV_STACKSZ (DEFAULT_STACK_SIZE * 2) #define LUA_SUCCESS 0 -#define EV_TIMER_FREQ (TIMER_FREQ / HZ) + +#define EV_TIMER_TICKS 5 /* timer resolution */ +#define EV_TIMER_FREQ ((TIMER_FREQ / HZ) * EV_TIMER_TICKS) #define EV_TICKS (HZ / 5) -#define EV_INPUT (HZ / 4) -//#define DEBUG_EV - -enum e_thread_state_flags{ - THREAD_QUIT = 0x0, - THREAD_YIELD = 0x1, - THREAD_TIMEREVENT = 0x2, - THREAD_PLAYBKEVENT = 0x4, - THREAD_ACTEVENT = 0x8, - THREAD_BUTEVENT = 0x10, - THREAD_CUSTOMEVENT = 0x20, +#define EV_INPUT (HZ / 5) + +#define ENABLE_LUA_HOOK (LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT) +#define DISABLE_LUA_HOOK (0) + +enum { + THREAD_ERROR = 0x0, +/* event & suspend states */ + THREAD_ACTEVENT = 0x1, + THREAD_BUTEVENT = 0x2, + THREAD_CUSTOMEVENT = 0x4, + THREAD_PLAYBKEVENT = 0x8, + THREAD_TIMEREVENT = 0x10, + //THREAD_AVAILEVENT = 0x20, //THREAD_AVAILEVENT = 0x40, //THREAD_AVAILEVENT = 0x80, -/* thread state holds 3 status items using masks and bitshifts */ - THREAD_STATEMASK = 0x00FF, - THREAD_SUSPENDMASK = 0xFF00, - THREAD_INPUTMASK = 0xFF0000, + THREAD_EVENT_ALL = 0xFF, +/* thread states */ + //THREAD_AVAILSTATE = 0x1, + //THREAD_AVAILSTATE = 0x2, + //THREAD_AVAILSTATE = 0x4, + //THREAD_AVAILSTATE = 0x8, + //THREAD_AVAILSTATE = 0x10, + //THREAD_AVAILSTATE = 0x20, + THREAD_QUIT = 0x40, + //THREAD_AVAILSTATE = 0x80, }; enum { @@ -105,83 +115,79 @@ enum { EVENT_CT }; -static const unsigned char thread_ev_states[EVENT_CT] = -{ - [ACTEVENT] = THREAD_ACTEVENT, - [BUTEVENT] = THREAD_BUTEVENT, - [CUSTOMEVENT] = THREAD_CUSTOMEVENT, - [PLAYBKEVENT] = THREAD_PLAYBKEVENT, - [TIMEREVENT] = THREAD_TIMEREVENT, -}; - -static const char *const ev_map[EVENT_CT] = -{ - [ACTEVENT] = "action", - [BUTEVENT] = "button", - [CUSTOMEVENT] = "custom", - [PLAYBKEVENT] = "playback", - [TIMEREVENT] = "timer", -}; - struct cb_data { int cb_ref; unsigned long id; void *data; + long next_tick; + long ticks; +}; + +struct thread_status { + uint8_t event; + uint8_t suspended; + uint8_t thread; + uint8_t unused; }; struct event_data { /* lua */ - lua_State *L; - lua_State *NEWL; + lua_State *L; + lua_State *NEWL; /* rockbox */ - unsigned int thread_id; - int thread_state; - long *event_stack; - long timer_ticks; - short freq_input; - short next_input; - short next_event; + unsigned int thread_id; + long *event_stack; + volatile long *get_tick; + struct thread_status status; + char next_event; /* callbacks */ - struct cb_data *cb[EVENT_CT]; + struct cb_data *cb[EVENT_CT]; }; +static void set_event_meta(lua_State *L); static struct event_data ev_data; static struct mutex rev_mtx SHAREDBSS_ATTR; -#ifdef DEBUG_EV -static int dbg_hook_calls = 0; -#endif +static inline uint8_t event_flag(unsigned int event) +{ + return (1UL << event) & 0xFF; +} -static inline bool has_event(int ev_flag) +static inline bool has_thread_status(uint8_t ev_flag) { - return ((THREAD_STATEMASK & (ev_data.thread_state & ev_flag)) == ev_flag); + return (ev_data.status.thread & ev_flag) == ev_flag; } -static inline bool is_suspend(int ev_flag) +static inline void set_evt(uint8_t ev_flag) { - ev_flag <<= 8; - return ((THREAD_SUSPENDMASK & (ev_data.thread_state & ev_flag)) == ev_flag); + ev_data.status.event |= ev_flag; } -static void init_event_data(lua_State *L, struct event_data *ev_data) +static inline bool remove_evt(uint8_t ev_flag) { - /* lua */ - ev_data->L = L; - //ev_data->NEWL = NULL; - /* rockbox */ - ev_data->thread_id = UINT_MAX; - ev_data->thread_state = THREAD_YIELD | THREAD_SUSPENDMASK; - //ev_data->event_stack = NULL; - //ev_data->timer_ticks = 0; - ev_data->freq_input = EV_INPUT; - ev_data->next_input = EV_INPUT; - ev_data->next_event = EV_TICKS; - /* callbacks */ - for (int i= 0; i < EVENT_CT; i++) - ev_data->cb[i] = NULL; + /* returns previous flag status and clears it */ + bool has_flag = (ev_data.status.event & ev_flag) == ev_flag; + ev_data.status.event &= ~(ev_flag); + return has_flag; +} + +static inline bool is_suspend(uint8_t ev_flag) +{ + return (ev_data.status.suspended & ev_flag) == ev_flag; } -/* lock and unlock routines allow us to execute the event thread without +static void suspend_evt(uint8_t ev_flag, bool suspend) +{ + if(!suspend && !has_thread_status(THREAD_QUIT)) + { + ev_data.status.suspended &= ~(ev_flag); + ev_flag = 0; + } + remove_evt(ev_flag); + ev_data.status.suspended |= ev_flag; +} + +/* mutex lock and unlock routines allow us to execute the event thread without * trashing the lua state on error, yield, or sleep in the callback functions */ static inline void rev_lock_mtx(void) @@ -194,80 +200,108 @@ static inline void rev_unlock_mtx(void) rb->mutex_unlock(&rev_mtx); } -static void lua_interrupt_callback( lua_State *L, lua_Debug *ar) +static void lua_interrupt_callback(lua_State *L, lua_Debug *ar) { - (void) L; (void) ar; -#ifdef DEBUG_EV - dbg_hook_calls++; -#endif rb->yield(); rev_lock_mtx(); rev_unlock_mtx(); /* must wait till event thread is done to continue */ -#ifdef DEBUG_EV - rb->splashf(0, "spin %d, hooked %d", dbg_hook_calls, (lua_gethookmask(L) != 0)); - unsigned char delay = -1; - /* we can't sleep or yield without affecting count so lets spin in a loop */ - while(delay > 0) {delay--;} - if (lua_gethookmask(L) == 0) - dbg_hook_calls = 0; -#endif - /* if callback error, pass error to the main lua state */ if (lua_status(ev_data.NEWL) != LUA_SUCCESS) - luaL_error (L, lua_tostring (ev_data.NEWL, -1)); + luaL_error(L, lua_tostring(ev_data.NEWL, -1)); } -static void lua_interrupt_set(lua_State *L, bool is_enabled) +static void lua_interrupt_set(lua_State *L, int hookmask) { - const int hookmask = LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT; + lua_Hook hook; + int count; + static lua_Hook oldhook = NULL; + static int oldmask = 0; + static int oldcount = 0; - if (is_enabled) - lua_sethook(L, lua_interrupt_callback, hookmask, 1 ); + if (hookmask == ENABLE_LUA_HOOK) + { + hook = lua_gethook(L); + if (hook == lua_interrupt_callback) + return; /* our hook is already active */ + /* preserve prior hook */ + oldhook = hook; + oldmask = lua_gethookmask(L); + oldcount = lua_gethookcount(L); + hook = lua_interrupt_callback; + count = 1; + } else - lua_sethook(L, NULL, 0, 0 ); + { + hook = oldhook; + hookmask = oldmask; + count = oldcount; + } + + lua_sethook(L, hook, hookmask, count); } -static int lua_rev_callback(lua_State *L, struct cb_data *cbd) +static int lua_rev_callback(lua_State *L, struct cb_data *evt) { int lua_status = LUA_ERRRUN; - if (L != NULL) - { - /* load cb function from lua registry */ - lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->cb_ref); + /* load cb function from lua registry */ + lua_rawgeti(L, LUA_REGISTRYINDEX, evt->cb_ref); - lua_pushinteger(L, cbd->id); - lua_pushlightuserdata (L, cbd->data); + lua_pushinteger(L, evt->id); + lua_pushlightuserdata(L, evt->data); + + lua_status = lua_resume(L, 2); /* call the saved function */ + if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */ + luaL_where(L, 1); /* push error string on stack */ - lua_status = lua_resume(L, 2); - if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */ - luaL_where(L, 0); /* push error string on stack */ - } return lua_status; } +/* timer interrupt callback */ +static void rev_timer_isr(void) +{ + uint8_t ev_flag = 0; + long curr_tick = *(ev_data.get_tick); + struct cb_data *evt; + + for (unsigned int i= 0; i < EVENT_CT; i++) + { + if (!is_suspend(event_flag(i))) + { + evt = ev_data.cb[i]; + if(evt->ticks > 0 && TIME_AFTER(curr_tick, evt->next_tick)) + ev_flag |= event_flag(i); + } + } + set_evt(ev_flag); + if (--ev_data.next_event <= 0 && ev_data.status.event) + lua_interrupt_set(ev_data.L, ENABLE_LUA_HOOK); +} + static void event_thread(void) { unsigned long action; - int event; - int ev_flag; + uint8_t ev_flag; + unsigned int event; + struct cb_data *evt; - while(ev_data.thread_state != THREAD_QUIT && lua_status(ev_data.L) == LUA_SUCCESS) + while(!has_thread_status(THREAD_QUIT) && lua_status(ev_data.L) == LUA_SUCCESS) { rev_lock_mtx(); - lua_interrupt_set(ev_data.L, true); + + lua_interrupt_set(ev_data.L, ENABLE_LUA_HOOK); for (event = 0; event < EVENT_CT; event++) { - ev_flag = thread_ev_states[event]; - if (!has_event(ev_flag) || is_suspend(ev_flag)) + ev_flag = event_flag(event); + if (!remove_evt(ev_flag) || is_suspend(ev_flag)) continue; /* check next event */ - ev_data.thread_state &= ~(ev_flag); /* event handled */ + evt = ev_data.cb[event]; switch (event) { @@ -278,118 +312,91 @@ static void event_thread(void) else if (action == ACTION_NONE) { /* only send ACTION_NONE once */ - if (ev_data.cb[ACTEVENT]->id == ACTION_NONE || - rb->button_status() != 0) - continue; /* check next event */ + if (evt->id == ACTION_NONE || rb->button_status() != 0) + goto skip_callback; /* check next event */ } - ev_data.cb[ACTEVENT]->id = action; + evt->id = action; break; case BUTEVENT: - ev_data.cb[BUTEVENT]->id = rb->button_get(false); - if (ev_data.cb[BUTEVENT]->id == BUTTON_NONE) - continue; /* check next event */ + evt->id = rb->button_get(false); + if (evt->id == BUTTON_NONE) + goto skip_callback; /* check next event */ break; case CUSTOMEVENT: - ev_data.thread_state |= thread_ev_states[CUSTOMEVENT]; // don't reset */ - break; case PLAYBKEVENT: - break; case TIMEREVENT: - ev_data.cb[TIMEREVENT]->id = *rb->current_tick + ev_data.timer_ticks; break; } - if (lua_rev_callback(ev_data.NEWL, ev_data.cb[event]) != LUA_SUCCESS) + if (lua_rev_callback(ev_data.NEWL, evt) != LUA_SUCCESS) { rev_unlock_mtx(); goto event_error; } +skip_callback: + evt->next_tick = *(ev_data.get_tick) + evt->ticks; } rev_unlock_mtx(); /* we are safe to release back to main lua state */ do { -#ifdef DEBUG_EV - dbg_hook_calls--; -#endif - lua_interrupt_set(ev_data.L, false); + lua_interrupt_set(ev_data.L, DISABLE_LUA_HOOK); ev_data.next_event = EV_TICKS; rb->yield(); - } while(ev_data.thread_state == THREAD_YIELD || is_suspend(THREAD_SUSPENDMASK >> 8)); + } while (!has_thread_status(THREAD_QUIT) && (is_suspend(THREAD_EVENT_ALL) + || !ev_data.status.event)); } + rb->yield(); + lua_interrupt_set(ev_data.L, DISABLE_LUA_HOOK); event_error: /* thread is exiting -- clean up */ rb->timer_unregister(); - //rb->yield(); rb->thread_exit(); return; } -/* timer interrupt callback */ -static void rev_timer_isr(void) -{ - if (!is_suspend(THREAD_SUSPENDMASK >> 8)) /* all events suspended? */ - { - ev_data.next_event--; - ev_data.next_input--; - - if (ev_data.next_input <=0) - { - ev_data.thread_state |= ((ev_data.thread_state & THREAD_INPUTMASK) >> 16); - ev_data.next_input = ev_data.freq_input; - } - - if (ev_data.cb[TIMEREVENT] != NULL && !is_suspend(TIMEREVENT)) - { - if (TIME_AFTER(*rb->current_tick, ev_data.cb[TIMEREVENT]->id)) - { - ev_data.thread_state |= thread_ev_states[TIMEREVENT]; - ev_data.next_event = 0; - } - } - - if (ev_data.next_event <= 0) - lua_interrupt_set(ev_data.L, true); - } -} - -static void create_event_thread_ref(struct event_data *ev_data) +static inline void create_event_thread_ref(struct event_data *ev_data) { lua_State *L = ev_data->L; lua_createtable(L, 2, 0); - ev_data->event_stack = (long *) lua_newuserdata (L, EV_STACKSZ); + ev_data->event_stack = (long *) lua_newuserdata(L, EV_STACKSZ); /* attach EVENT_METATABLE to ud so we get notified on garbage collection */ - luaL_getmetatable (L, EVENT_METATABLE); - lua_setmetatable (L, -2); + set_event_meta(L); lua_rawseti(L, -2, 1); ev_data->NEWL = lua_newthread(L); lua_rawseti(L, -2, 2); - lua_setfield (L, LUA_REGISTRYINDEX, EVENT_THREAD); /* store references */ + lua_setfield(L, LUA_REGISTRYINDEX, EVENT_THREAD); /* store references */ } -static void destroy_event_thread_ref(struct event_data *ev_data) +static inline void destroy_event_thread_ref(struct event_data *ev_data) { lua_State *L = ev_data->L; ev_data->event_stack = NULL; ev_data->NEWL = NULL; lua_pushnil(L); - lua_setfield (L, LUA_REGISTRYINDEX, EVENT_THREAD); /* free references */ + lua_setfield(L, LUA_REGISTRYINDEX, EVENT_THREAD); /* free references */ } -static void exit_event_thread(struct event_data *ev_data) +static inline void exit_event_thread(struct event_data *ev_data) { - ev_data->thread_state = THREAD_QUIT; + suspend_evt(THREAD_EVENT_ALL, true); + ev_data->status.thread = THREAD_QUIT; + rb->thread_wait(ev_data->thread_id); /* wait for thread to exit */ + destroy_event_thread_ref(ev_data); + + ev_data->status.thread = 0; + ev_data->thread_id = UINT_MAX; } 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) if (ev_data->event_stack != NULL) /* make sure we don't double free */ { if (!init && ev_data->thread_id != UINT_MAX) - { - ev_data->thread_state |= THREAD_SUSPENDMASK; /* suspend all events */ - rb->yield(); exit_event_thread(ev_data); - destroy_event_thread_ref(ev_data); - lua_interrupt_set(ev_data->L, false); - ev_data->thread_state = THREAD_YIELD | THREAD_SUSPENDMASK; - ev_data->thread_id = UINT_MAX; - } + return; } - else if (!init || ev_data->thread_state == THREAD_QUIT) + else if (!init) return; create_event_thread_ref(ev_data); - if (ev_data->NEWL == NULL || ev_data->event_stack == NULL) - return; ev_data->thread_id = rb->create_thread(&event_thread, ev_data->event_stack, @@ -424,27 +422,26 @@ static void init_event_thread(bool init, struct event_data *ev_data) IF_COP(, COP)); /* Timer is used to poll waiting events */ - rb->timer_register(0, NULL, EV_TIMER_FREQ, rev_timer_isr IF_COP(, CPU)); - ev_data->thread_state &= ~THREAD_SUSPENDMASK; + rb->timer_register(1, NULL, EV_TIMER_FREQ, rev_timer_isr IF_COP(, CPU)); } static void playback_event_callback(unsigned short id, void *data) { /* playback events are synchronous we need to return ASAP so set a flag */ - if (!is_suspend(THREAD_PLAYBKEVENT)) /* playback events suspended? */ + struct cb_data *evt = ev_data.cb[PLAYBKEVENT]; + evt->id = id; + evt->data = data; + if (!is_suspend(THREAD_PLAYBKEVENT)) { - ev_data.thread_state |= thread_ev_states[PLAYBKEVENT]; - ev_data.cb[PLAYBKEVENT]->id = id; - ev_data.cb[PLAYBKEVENT]->data = data; - lua_interrupt_set(ev_data.L, true); + set_evt(THREAD_PLAYBKEVENT); + lua_interrupt_set(ev_data.L, ENABLE_LUA_HOOK); } } -static void register_playbk_events(int flag_events, - void (*handler)(unsigned short id, void *data)) +static void register_playbk_events(int flag_events) { - long unsigned int i = 0; - const unsigned short playback_events[7] = + unsigned short i, pb_evt; + static const unsigned short playback_events[7] = { /*flags*/ PLAYBACK_EVENT_START_PLAYBACK, /* 0x1 */ PLAYBACK_EVENT_TRACK_BUFFER, /* 0x2 */ @@ -455,43 +452,34 @@ static void register_playbk_events(int flag_events, PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE /* 0x40*/ }; - for(; i < ARRAYLEN(playback_events); i++, flag_events >>= 1) + for(i = 0; i < ARRAYLEN(playback_events); i++) { - if (flag_events == 0) /* remove events */ - rb->remove_event(playback_events[i], handler); - else /* add events */ - if ((flag_events & 0x1) == 0x1) - rb->add_event(playback_events[i], handler); + pb_evt = playback_events[i]; + if (!(flag_events & (1 << i))) + rb->remove_event(pb_evt, playback_event_callback); + else + rb->add_event(pb_evt, playback_event_callback); } } -static void destroy_event_userdata(lua_State *L, int event) +static void destroy_event_userdata(lua_State *L, unsigned int event) { - if (ev_data.cb[event] != NULL) - { - int ev_flag = thread_ev_states[event]; - int ev_clear = (ev_flag | (ev_flag << 16)); - if (!is_suspend(THREAD_SUSPENDMASK >> 8)) /* all events suspended? */ - ev_clear |= (ev_flag << 8); - ev_data.thread_state &= ~(ev_clear); - luaL_unref (L, LUA_REGISTRYINDEX, ev_data.cb[event]->cb_ref); - ev_data.cb[event] = NULL; - } + uint8_t ev_flag = event_flag(event); + struct cb_data *evt = ev_data.cb[event]; + suspend_evt(ev_flag, true); + if (evt != NULL) + luaL_unref(L, LUA_REGISTRYINDEX, evt->cb_ref); + + ev_data.cb[event] = NULL; } -static void create_event_userdata(lua_State *L, int event, int index) +static void create_event_userdata(lua_State *L, unsigned int event, int index) { /* if function is already registered , unregister it */ destroy_event_userdata(L, event); - if (!lua_isfunction (L, index)) - { - init_event_thread(false, &ev_data); - luaL_typerror (L, index, "function"); - return; - } - - lua_pushvalue (L, index); /* copy passed lua function on top of stack */ + luaL_checktype(L, index, LUA_TFUNCTION); + lua_pushvalue(L, index); /* copy passed lua function on top of stack */ int ref_lua = luaL_ref(L, LUA_REGISTRYINDEX); 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) ev_data.cb[event]->cb_ref = ref_lua; /* store ref for later call/release */ /* attach EVENT_METATABLE to ud so we get notified on garbage collection */ - luaL_getmetatable (L, EVENT_METATABLE); - lua_setmetatable (L, -2); + set_event_meta(L); /* cb_data is on top of stack */ } static int rockev_gc(lua_State *L) { bool has_events = false; - void *d = (void *) lua_touserdata (L, 1); + void *d = (void *) lua_touserdata(L, 1); - if (d == NULL) - { - return 0; - } - else if (d == ev_data.event_stack) /* thread stack is gc'd kill thread */ - { - init_event_thread(false, &ev_data); - } - else if (d == ev_data.cb[PLAYBKEVENT]) + if (d != NULL) { - register_playbk_events(0, &playback_event_callback); + for (unsigned int i= 0; i < EVENT_CT; i++) + { + if (d == ev_data.cb[i] || d == ev_data.event_stack) + { + if (i == PLAYBKEVENT) + register_playbk_events(0); + destroy_event_userdata(L, i); + } + else if (ev_data.cb[i] != NULL) + has_events = true; + } + + if (!has_events) /* nothing to wait for kill thread */ + init_event_thread(false, &ev_data); } + return 0; +} - for( int i= 0; i < EVENT_CT; i++) +static void set_event_meta(lua_State *L) +{ + if (luaL_newmetatable(L, EVENT_METATABLE)) { - if (d == ev_data.cb[i]) - destroy_event_userdata(L, i); - else if (ev_data.cb[i] != NULL) - has_events = true; + /* set __gc field so we can clean-up our objects */ + lua_pushcfunction(L, rockev_gc); + lua_setfield(L, -2, "__gc"); } + lua_setmetatable(L, -2); +} + +static void init_event_data(lua_State *L, struct event_data *ev_data) +{ + /* lua */ + ev_data->L = L; + /*ev_data->NEWL = NULL;*/ + /* rockbox */ + ev_data->thread_id = UINT_MAX; + ev_data->get_tick = rb->current_tick; - if (!has_events) /* nothing to wait for kill thread */ - init_event_thread(false, &ev_data); + ev_data->status.event = 0; + ev_data->status.suspended = THREAD_EVENT_ALL; + ev_data->status.thread = 0; - return 0; + /*ev_data->event_stack = NULL;*/ + ev_data->next_event = EV_TICKS; + /* callbacks */ + for (unsigned int i= 0; i < EVENT_CT; i++) + ev_data->cb[i] = NULL; } /****************************************************************************** * LUA INTERFACE ************************************************************** ******************************************************************************* */ +static unsigned int get_event_by_name(lua_State *L) +{ + static const char *const ev_map[EVENT_CT] = + { + [ACTEVENT] = "action", + [BUTEVENT] = "button", + [CUSTOMEVENT] = "custom", + [PLAYBKEVENT] = "playback", + [TIMEREVENT] = "timer", + }; + + return luaL_checkoption(L, 1, NULL, ev_map); +} + static int rockev_register(lua_State *L) { - int event = luaL_checkoption(L, 1, NULL, ev_map); - int ev_flag = thread_ev_states[event]; + /* register (event, cb [, args] */ + unsigned int event = get_event_by_name(L); + uint8_t ev_flag = event_flag(event); int playbk_events; - lua_settop (L, 3); /* we need to lock our optional args before...*/ + lua_settop(L, 3); /* we need to lock our optional args before...*/ create_event_userdata(L, event, 2);/* cb_data is on top of stack */ + init_event_thread(!has_thread_status(THREAD_QUIT), &ev_data); + + long event_ticks = 0; + struct cb_data *evt; switch (event) { case ACTEVENT: /* fall through */ case BUTEVENT: - ev_data.freq_input = luaL_optinteger(L, 3, EV_INPUT); - if (ev_data.freq_input < HZ / 20) ev_data.freq_input = HZ / 20; - ev_data.thread_state |= (ev_flag | (ev_flag << 16)); + event_ticks = luaL_optinteger(L, 3, EV_INPUT); break; case CUSTOMEVENT: + event_ticks = luaL_optinteger(L, 3, EV_TIMER_TICKS); + ev_flag = 0; /* don't remove suspend */ break; - case PLAYBKEVENT: - /* see register_playbk_events() for flags */ + case PLAYBKEVENT: /* see register_playbk_events() for flags */ + event_ticks = 0; /* playback events are not triggered by timeout */ playbk_events = luaL_optinteger(L, 3, 0x3F); - register_playbk_events(playbk_events, &playback_event_callback); + register_playbk_events(playbk_events); break; case TIMEREVENT: - ev_data.timer_ticks = luaL_checkinteger(L, 3); - ev_data.cb[TIMEREVENT]->id = *rb->current_tick + ev_data.timer_ticks; + event_ticks = luaL_checkinteger(L, 3); break; } - init_event_thread(true, &ev_data); + evt = ev_data.cb[event]; + evt->ticks = event_ticks; + evt->next_tick = *(ev_data.get_tick) + event_ticks; + suspend_evt(ev_flag, false); return 1; /* returns cb_data */ } static int rockev_suspend(lua_State *L) { - if (ev_data.thread_state == THREAD_QUIT) - return 0; - - int event; /*Arg 1 is event pass nil to suspend all */ + unsigned int event; /*Arg 1 is event pass nil to suspend all */ bool suspend = luaL_optboolean(L, 2, true); - int ev_flag = THREAD_SUSPENDMASK; + uint8_t ev_flag = THREAD_EVENT_ALL; if (!lua_isnoneornil(L, 1)) { - event = luaL_checkoption(L, 1, NULL, ev_map); - ev_flag = thread_ev_states[event] << 8; + event = get_event_by_name(L); + ev_flag = event_flag(event); } - if (suspend) - ev_data.thread_state |= ev_flag; - else - ev_data.thread_state &= ~(ev_flag); + suspend_evt(ev_flag, suspend); + + /* don't resume invalid events */ + for (unsigned int i = 0; i < EVENT_CT; i++) + { + if (ev_data.cb[i] == NULL) + suspend_evt(event_flag(i), true); + } return 0; } static int rockev_trigger(lua_State *L) { - int event = luaL_checkoption(L, 1, NULL, ev_map); + unsigned int event = get_event_by_name(L); bool enable = luaL_optboolean(L, 2, true); - int ev_flag; + uint8_t ev_flag = event_flag(event); + struct cb_data *evt = ev_data.cb[event]; - /* protect from invalid events */ - if (ev_data.cb[event] != NULL) + /* don't trigger invalid events */ + if (evt != NULL) { - ev_flag = thread_ev_states[event]; /* allow user to pass an id to some of the callback functions */ - ev_data.cb[event]->id = luaL_optinteger(L, 3, ev_data.cb[event]->id); + evt->id = luaL_optinteger(L, 3, evt->id); + + if (event == CUSTOMEVENT) + suspend_evt(ev_flag, !enable); if (enable) - ev_data.thread_state |= ev_flag; + set_evt(ev_flag); else - ev_data.thread_state &= ~(ev_flag); + remove_evt(ev_flag); } return 0; } static int rockev_unregister(lua_State *L) { - luaL_checkudata (L, 1, EVENT_METATABLE); + luaL_checkudata(L, 1, EVENT_METATABLE); rockev_gc(L); - lua_pushnil(L); - return 1; -} -/* -** Creates events metatable. -*/ -static int event_create_meta (lua_State *L) { - luaL_newmetatable (L, EVENT_METATABLE); - /* set __gc field so we can clean-up our objects */ - lua_pushcfunction (L, rockev_gc); - lua_setfield (L, -2, "__gc"); - return 1; + return 0; } static const struct luaL_reg evlib[] = { @@ -647,10 +672,9 @@ static const struct luaL_reg evlib[] = { {NULL, NULL} }; -int luaopen_rockevents (lua_State *L) { +int luaopen_rockevents(lua_State *L) { rb->mutex_init(&rev_mtx); init_event_data(L, &ev_data); - event_create_meta (L); - luaL_register (L, LUA_ROCKEVENTSNAME, evlib); + luaL_register(L, LUA_ROCKEVENTSNAME, evlib); return 1; } -- cgit