4c3937591c
you could return values in the event callbacks that would never be processed this would eventually cause a lua stack overflow settop(0) eats all return values (if any) Change-Id: Icac6b27e592b385421275d4bd899ed3fe1065669
686 lines
20 KiB
C
686 lines
20 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2019 William Wilgus
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* lua events from rockbox *****************************************************
|
|
* This library allows events to be subscribed / recieved within a lua script
|
|
* most events in rb are synchronous so flags are set and later checked by a
|
|
* secondary thread to make them (semi?) asynchronous.
|
|
*
|
|
* There are a few caveats to be aware of:
|
|
* FIRST, The main lua state is halted till the lua callback(s) are finished
|
|
* Yielding will not return control to your script from within a callback
|
|
* Also, subsequent callbacks may be delayed by the code in your lua callback
|
|
* SECOND, You must store the value returned from the event_register function
|
|
* you might get away with it for a bit but gc will destroy your callback
|
|
* eventually if you do not store the event
|
|
* THIRD, You only get one cb per event type
|
|
* ["action", "button", "custom", "playback", "timer"]
|
|
* (Re-registration of an event overwrites the previous one)
|
|
*
|
|
* Usage:
|
|
* possible events =["action", "button", "custom", "playback", "timer"]
|
|
*
|
|
* local ev = rockev.register("event", cb_function, [timeout / flags])
|
|
* cb_function([id] [, data]) ... end
|
|
*
|
|
*
|
|
* rockev.trigger("event", [true/false], [id])
|
|
* sets an event to triggered,
|
|
* 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.
|
|
* stops event from executing, any event before re-enabling will be lost.
|
|
* Passing false, unregistering or re-registering an event will clear the suspend
|
|
*
|
|
* rockev.unregister(evX)
|
|
* Use unregister(evX) to remove an event
|
|
* Unregistering is not necessary before script end, it will be
|
|
* cleaned up on script exit
|
|
*
|
|
*******************************************************************************
|
|
* *
|
|
*/
|
|
|
|
#define LUA_LIB
|
|
|
|
#define _ROCKCONF_H_ /* We don't need strcmp() etc. wrappers */
|
|
#include "lua.h"
|
|
#include "lauxlib.h"
|
|
#include "plugin.h"
|
|
#include "rocklib_events.h"
|
|
|
|
#define EVENT_METATABLE "event metatable"
|
|
|
|
#define EVENT_THREAD LUA_ROCKEVENTSNAME ".thread"
|
|
#define EV_STACKSZ (DEFAULT_STACK_SIZE * 2)
|
|
|
|
#define LUA_SUCCESS 0
|
|
|
|
#define EV_TIMER_TICKS 5 /* timer resolution */
|
|
#define EV_TIMER_FREQ ((TIMER_FREQ / HZ) * EV_TIMER_TICKS)
|
|
#define EV_TICKS (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_BTNEVENT = 0x2,
|
|
THREAD_CUSTOMEVENT = 0x4,
|
|
THREAD_PLAYBKEVENT = 0x8,
|
|
THREAD_TIMEREVENT = 0x10,
|
|
//THREAD_AVAILEVENT = 0x20,
|
|
//THREAD_AVAILEVENT = 0x40,
|
|
//THREAD_AVAILEVENT = 0x80,
|
|
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 {
|
|
ACTEVENT = 0,
|
|
BTNEVENT,
|
|
CUSTOMEVENT,
|
|
PLAYBKEVENT,
|
|
TIMEREVENT,
|
|
EVENT_CT
|
|
};
|
|
|
|
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;
|
|
/* rockbox */
|
|
unsigned int thread_id;
|
|
long *event_stack;
|
|
volatile long *get_tick;
|
|
struct thread_status status;
|
|
/* callbacks */
|
|
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;
|
|
|
|
static inline uint8_t event_flag(unsigned int event)
|
|
{
|
|
return (1UL << event) & 0xFF;
|
|
}
|
|
|
|
static inline bool has_thread_status(uint8_t ev_flag)
|
|
{
|
|
return (ev_data.status.thread & ev_flag) == ev_flag;
|
|
}
|
|
|
|
static inline void set_evt(uint8_t ev_flag)
|
|
{
|
|
ev_data.status.event |= ev_flag;
|
|
}
|
|
|
|
static inline bool remove_evt(uint8_t ev_flag)
|
|
{
|
|
/* 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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
rb->mutex_lock(&rev_mtx);
|
|
}
|
|
|
|
static inline void rev_unlock_mtx(void)
|
|
{
|
|
rb->mutex_unlock(&rev_mtx);
|
|
}
|
|
|
|
static void lua_interrupt_callback(lua_State *L, lua_Debug *ar)
|
|
{
|
|
(void) ar;
|
|
|
|
rb->yield();
|
|
|
|
rev_lock_mtx();
|
|
rev_unlock_mtx(); /* must wait till event thread is done to continue */
|
|
|
|
/* 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));
|
|
}
|
|
|
|
static void lua_interrupt_set(lua_State *L, int hookmask)
|
|
{
|
|
lua_Hook hook;
|
|
int count;
|
|
static lua_Hook oldhook = NULL;
|
|
static int oldmask = 0;
|
|
static int oldcount = 0;
|
|
|
|
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 = 10;
|
|
}
|
|
else
|
|
{
|
|
hook = oldhook;
|
|
hookmask = oldmask;
|
|
count = oldcount;
|
|
}
|
|
|
|
lua_sethook(L, hook, hookmask, count);
|
|
}
|
|
|
|
static int lua_rev_callback(lua_State *L, struct cb_data *evt)
|
|
{
|
|
int lua_status = LUA_ERRRUN;
|
|
|
|
/* load cb function from lua registry */
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, evt->cb_ref);
|
|
|
|
lua_pushinteger(L, evt->id);
|
|
lua_pushlightuserdata(L, evt->data);
|
|
|
|
lua_status = lua_resume(L, 2); /* call the saved function */
|
|
if (lua_status == LUA_SUCCESS)
|
|
lua_settop(L, 0); /* eat any value(s) returned */
|
|
else if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */
|
|
luaL_where(L, 1); /* 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 ((i == ACTEVENT || i == BTNEVENT) &&
|
|
(rb->button_queue_count() || rb->button_status() || evt->id))
|
|
ev_flag |= event_flag(i); /* any buttons ready? */
|
|
else if(evt->ticks > 0 && TIME_AFTER(curr_tick, evt->next_tick))
|
|
ev_flag |= event_flag(i);
|
|
|
|
}
|
|
}
|
|
set_evt(ev_flag);
|
|
if (ev_data.status.event) /* any events ready? */
|
|
lua_interrupt_set(ev_data.L, ENABLE_LUA_HOOK);
|
|
}
|
|
|
|
static void event_thread(void)
|
|
{
|
|
unsigned long action;
|
|
uint8_t ev_flag;
|
|
unsigned int event;
|
|
struct cb_data *evt;
|
|
|
|
while(!has_thread_status(THREAD_QUIT) && lua_status(ev_data.L) == LUA_SUCCESS)
|
|
{
|
|
rev_lock_mtx();
|
|
|
|
lua_interrupt_set(ev_data.L, ENABLE_LUA_HOOK);
|
|
|
|
for (event = 0; event < EVENT_CT; event++)
|
|
{
|
|
ev_flag = event_flag(event);
|
|
if (!remove_evt(ev_flag) || is_suspend(ev_flag))
|
|
continue; /* check next event */
|
|
|
|
evt = ev_data.cb[event];
|
|
|
|
switch (event)
|
|
{
|
|
case ACTEVENT:
|
|
action = get_plugin_action(TIMEOUT_NOBLOCK, true);
|
|
if (action == ACTION_UNKNOWN)
|
|
continue; /* check next event */
|
|
else if (action == ACTION_NONE)
|
|
{
|
|
/* only send ACTION_NONE once */
|
|
if (evt->id == ACTION_NONE || rb->button_status() != 0)
|
|
continue; /* check next event */
|
|
}
|
|
evt->id = action;
|
|
break;
|
|
case BTNEVENT:
|
|
evt->id = rb->button_get(false);
|
|
/* only send BUTTON_NONE once */
|
|
if (evt->id == BUTTON_NONE)
|
|
continue; /* check next event */
|
|
break;
|
|
case CUSTOMEVENT:
|
|
case PLAYBKEVENT:
|
|
case TIMEREVENT:
|
|
break;
|
|
|
|
}
|
|
|
|
if (lua_rev_callback(ev_data.NEWL, evt) != LUA_SUCCESS)
|
|
{
|
|
rev_unlock_mtx();
|
|
goto event_error;
|
|
}
|
|
evt->next_tick = *(ev_data.get_tick) + evt->ticks;
|
|
}
|
|
rev_unlock_mtx(); /* we are safe to release back to main lua state */
|
|
|
|
do
|
|
{
|
|
lua_interrupt_set(ev_data.L, DISABLE_LUA_HOOK);
|
|
rb->yield();
|
|
} 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->thread_exit();
|
|
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
/* attach EVENT_METATABLE to ud so we get notified on garbage collection */
|
|
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 */
|
|
}
|
|
|
|
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 */
|
|
}
|
|
|
|
static inline void exit_event_thread(struct event_data *ev_data)
|
|
{
|
|
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)
|
|
{
|
|
if (ev_data->event_stack != NULL) /* make sure we don't double free */
|
|
{
|
|
if (!init && ev_data->thread_id != UINT_MAX)
|
|
exit_event_thread(ev_data);
|
|
|
|
return;
|
|
}
|
|
else if (!init)
|
|
return;
|
|
|
|
create_event_thread_ref(ev_data);
|
|
|
|
ev_data->thread_id = rb->create_thread(&event_thread,
|
|
ev_data->event_stack,
|
|
EV_STACKSZ,
|
|
0,
|
|
EVENT_THREAD
|
|
IF_PRIO(, PRIORITY_SYSTEM)
|
|
IF_COP(, COP));
|
|
|
|
/* Timer is used to poll waiting events */
|
|
if (!rb->timer_register(1, NULL, EV_TIMER_FREQ, rev_timer_isr IF_COP(, CPU)))
|
|
{
|
|
rb->splash(100, "No timer available!");
|
|
}
|
|
}
|
|
|
|
static void playback_event_callback(unsigned short id, void *data)
|
|
{
|
|
/* playback events are synchronous we need to return ASAP so set a flag */
|
|
struct cb_data *evt = ev_data.cb[PLAYBKEVENT];
|
|
evt->id = id;
|
|
evt->data = data;
|
|
if (!is_suspend(THREAD_PLAYBKEVENT))
|
|
{
|
|
set_evt(THREAD_PLAYBKEVENT);
|
|
lua_interrupt_set(ev_data.L, ENABLE_LUA_HOOK);
|
|
}
|
|
}
|
|
|
|
static void register_playbk_events(int flag_events)
|
|
{
|
|
unsigned short i, pb_evt;
|
|
static const unsigned short playback_events[7] =
|
|
{ /*flags*/
|
|
PLAYBACK_EVENT_START_PLAYBACK, /* 0x1 */
|
|
PLAYBACK_EVENT_TRACK_BUFFER, /* 0x2 */
|
|
PLAYBACK_EVENT_CUR_TRACK_READY, /* 0x4 */
|
|
PLAYBACK_EVENT_TRACK_FINISH, /* 0x8 */
|
|
PLAYBACK_EVENT_TRACK_CHANGE, /* 0x10*/
|
|
PLAYBACK_EVENT_TRACK_SKIP, /* 0x20*/
|
|
PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE /* 0x40*/
|
|
};
|
|
|
|
for(i = 0; i < ARRAYLEN(playback_events); i++)
|
|
{
|
|
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, unsigned int event)
|
|
{
|
|
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, unsigned int event, int index)
|
|
{
|
|
/* if function is already registered , unregister it */
|
|
destroy_event_userdata(L, event);
|
|
|
|
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));
|
|
|
|
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 */
|
|
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);
|
|
|
|
if (d != NULL)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static void set_event_meta(lua_State *L)
|
|
{
|
|
if (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");
|
|
}
|
|
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;
|
|
|
|
ev_data->status.event = 0;
|
|
ev_data->status.suspended = THREAD_EVENT_ALL;
|
|
ev_data->status.thread = 0;
|
|
|
|
/*ev_data->event_stack = NULL;*/
|
|
/* 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",
|
|
[BTNEVENT] = "button",
|
|
[CUSTOMEVENT] = "custom",
|
|
[PLAYBKEVENT] = "playback",
|
|
[TIMEREVENT] = "timer",
|
|
};
|
|
|
|
return luaL_checkoption(L, 1, NULL, ev_map);
|
|
}
|
|
|
|
|
|
static int rockev_register(lua_State *L)
|
|
{
|
|
/* 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...*/
|
|
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 BTNEVENT:
|
|
event_ticks = 0; /* button events not triggered by timeout */
|
|
break;
|
|
case CUSTOMEVENT:
|
|
event_ticks = luaL_optinteger(L, 3, EV_TICKS);
|
|
ev_flag = 0; /* don't remove suspend */
|
|
break;
|
|
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);
|
|
break;
|
|
case TIMEREVENT:
|
|
event_ticks = luaL_checkinteger(L, 3);
|
|
break;
|
|
}
|
|
|
|
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)
|
|
{
|
|
unsigned int event; /*Arg 1 is event, pass nil to suspend all */
|
|
bool suspend = luaL_optboolean(L, 2, true);
|
|
uint8_t ev_flag = THREAD_EVENT_ALL;
|
|
|
|
if (!lua_isnoneornil(L, 1))
|
|
{
|
|
event = get_event_by_name(L);
|
|
ev_flag = event_flag(event);
|
|
}
|
|
|
|
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)
|
|
{
|
|
unsigned int event = get_event_by_name(L);
|
|
bool enable = luaL_optboolean(L, 2, true);
|
|
|
|
uint8_t ev_flag = event_flag(event);
|
|
struct cb_data *evt = ev_data.cb[event];
|
|
|
|
/* don't trigger invalid events */
|
|
if (evt != NULL)
|
|
{
|
|
/* allow user to pass an id to some of the callback functions */
|
|
evt->id = luaL_optinteger(L, 3, evt->id);
|
|
|
|
if (event == CUSTOMEVENT)
|
|
suspend_evt(ev_flag, !enable);
|
|
|
|
if (enable)
|
|
set_evt(ev_flag);
|
|
else
|
|
remove_evt(ev_flag);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rockev_unregister(lua_State *L)
|
|
{
|
|
luaL_checkudata(L, 1, EVENT_METATABLE);
|
|
rockev_gc(L);
|
|
return 0;
|
|
}
|
|
|
|
static const struct luaL_reg evlib[] = {
|
|
{"register", rockev_register},
|
|
{"suspend", rockev_suspend},
|
|
{"trigger", rockev_trigger},
|
|
{"unregister", rockev_unregister},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
int luaopen_rockevents(lua_State *L) {
|
|
rb->mutex_init(&rev_mtx);
|
|
init_event_data(L, &ev_data);
|
|
luaL_register(L, LUA_ROCKEVENTSNAME, evlib);
|
|
return 1;
|
|
}
|