summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/plugins/lua/SOURCES1
-rwxr-xr-xapps/plugins/lua/rbdefines_helper.pl4
-rw-r--r--apps/plugins/lua/rockaux.c19
-rw-r--r--apps/plugins/lua/rocklib.c21
-rw-r--r--apps/plugins/lua/rocklib.h2
-rw-r--r--apps/plugins/lua/rocklib_events.c632
-rw-r--r--apps/plugins/lua/rocklib_events.h5
-rw-r--r--apps/plugins/lua/rocklua.c3
8 files changed, 674 insertions, 13 deletions
diff --git a/apps/plugins/lua/SOURCES b/apps/plugins/lua/SOURCES
index 8877a4164d..93fa5e9c83 100644
--- a/apps/plugins/lua/SOURCES
+++ b/apps/plugins/lua/SOURCES
@@ -37,3 +37,4 @@ strtol.c
strstr.c
rocklua.c
luadir.c
+rocklib_events.c
diff --git a/apps/plugins/lua/rbdefines_helper.pl b/apps/plugins/lua/rbdefines_helper.pl
index f15cc71d36..ba15346a8a 100755
--- a/apps/plugins/lua/rbdefines_helper.pl
+++ b/apps/plugins/lua/rbdefines_helper.pl
@@ -31,8 +31,12 @@ my @rockbox_defines = (
'^SCREEN_REMOTE$',
'^FONT_SYSFIXED$',
'^FONT_UI$',
+ '^PLAYBACK_EVENT_.*',
'^PLAYLIST_(INSERT|PREPEND|REPLACE)',
'^TOUCHSCREEN_(POINT|BUTTON)$',
+ '^SYS_CHARGER_(DIS|)CONNECTED$',
+ '^SYS_(TIMEOUT|POWEROFF)$',
+ '^SYS_USB_(DIS|)CONNECTED$',
'^HOME_DIR$',
'^PLUGIN_DIR$',
'^PLUGIN(_APPS_|_GAMES_|_)DATA_DIR$',
diff --git a/apps/plugins/lua/rockaux.c b/apps/plugins/lua/rockaux.c
index 562d1654a7..734b6a8324 100644
--- a/apps/plugins/lua/rockaux.c
+++ b/apps/plugins/lua/rockaux.c
@@ -24,6 +24,7 @@
#include "plugin.h"
#define _ROCKCONF_H_ /* Protect against unwanted include */
#include "lua.h"
+#include "lib/pluginlib_actions.h"
extern long strtol(const char *nptr, char **endptr, int base);
@@ -164,3 +165,21 @@ int filetol(int fd, long *num)
return retn;
}
+int get_plugin_action(int timeout, bool with_remote)
+{
+ static const struct button_mapping *m1[] = { pla_main_ctx };
+ int btn;
+
+#ifndef HAVE_REMOTE_LCD
+ (void) with_remote;
+#else
+ static const struct button_mapping *m2[] = { pla_main_ctx, pla_remote_ctx };
+
+ if (with_remote)
+ btn = pluginlib_getaction(timeout, m2, 2);
+ else
+#endif
+ btn = pluginlib_getaction(timeout, m1, 1);
+
+ return btn;
+}
diff --git a/apps/plugins/lua/rocklib.c b/apps/plugins/lua/rocklib.c
index 1d20989009..9518fe955b 100644
--- a/apps/plugins/lua/rocklib.c
+++ b/apps/plugins/lua/rocklib.c
@@ -30,7 +30,6 @@
#include "lauxlib.h"
#include "rocklib.h"
#include "lib/helper.h"
-#include "lib/pluginlib_actions.h"
/*
* http://www.lua.org/manual/5.1/manual.html#lua_CFunction
@@ -88,19 +87,9 @@ RB_WRAP(current_path)
RB_WRAP(get_plugin_action)
{
- static const struct button_mapping *m1[] = { pla_main_ctx };
int timeout = luaL_checkint(L, 1);
- int btn;
-
-#ifdef HAVE_REMOTE_LCD
- static const struct button_mapping *m2[] = { pla_main_ctx, pla_remote_ctx };
bool with_remote = luaL_optint(L, 2, 0);
- if (with_remote)
- btn = pluginlib_getaction(timeout, m2, 2);
- else
-#endif
- btn = pluginlib_getaction(timeout, m1, 1);
-
+ int btn = get_plugin_action(timeout, with_remote);
lua_pushinteger(L, btn);
return 1;
}
@@ -829,6 +818,14 @@ LUALIB_API int luaopen_rock(lua_State *L)
RB_CONSTANT(PLAYLIST_PREPEND),
RB_CONSTANT(PLAYLIST_REPLACE),
+/* queue sys events */
+ RB_CONSTANT(SYS_USB_CONNECTED),
+ RB_CONSTANT(SYS_USB_DISCONNECTED),
+ RB_CONSTANT(SYS_TIMEOUT),
+ RB_CONSTANT(SYS_POWEROFF),
+ RB_CONSTANT(SYS_CHARGER_CONNECTED),
+ RB_CONSTANT(SYS_CHARGER_DISCONNECTED),
+
#ifdef HAVE_TOUCHSCREEN
RB_CONSTANT(TOUCHSCREEN_POINT),
RB_CONSTANT(TOUCHSCREEN_BUTTON),
diff --git a/apps/plugins/lua/rocklib.h b/apps/plugins/lua/rocklib.h
index 5d3abe30ec..02b5ff6c88 100644
--- a/apps/plugins/lua/rocklib.h
+++ b/apps/plugins/lua/rocklib.h
@@ -46,8 +46,10 @@ struct lua_str_reg {
};
LUALIB_API int (luaopen_rock) (lua_State *L) __attribute__((aligned(0x8)));
+/* in rockaux.c */
int get_current_path(lua_State *L, int level);
int filetol(int fd, long *num);
+int get_plugin_action(int timeout, bool with_remote);
#endif /* _ROCKLIB_H_ */
diff --git a/apps/plugins/lua/rocklib_events.c b/apps/plugins/lua/rocklib_events.c
new file mode 100644
index 0000000000..9e363edbdd
--- /dev/null
+++ b/apps/plugins/lua/rocklib_events.c
@@ -0,0 +1,632 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 affects all events
+ * stops event from executing, any but the last 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 LUA_SUCCESS 0
+#define EV_TIMER_FREQ (TIMER_FREQ / HZ)
+#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,
+ //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,
+};
+
+enum {
+ ACTEVENT = 0,
+ BUTEVENT,
+ CUSTOMEVENT,
+ PLAYBKEVENT,
+ TIMEREVENT,
+ 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;
+};
+
+struct event_data {
+ /* lua */
+ lua_State *L;
+ lua_State *NEWL;
+ /* rockbox */
+ unsigned int thread_id;
+ int thread_state;
+ long *event_stack;
+ long timer_ticks;
+ short next_input;
+ short next_event;
+ /* callbacks */
+ struct cb_data *cb[EVENT_CT];
+};
+
+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 bool has_event(int ev_flag)
+{
+ return ((THREAD_STATEMASK & (ev_data.thread_state & ev_flag)) == ev_flag);
+}
+
+static inline bool is_suspend(int ev_flag)
+{
+ ev_flag <<= 8;
+ return ((THREAD_SUSPENDMASK & (ev_data.thread_state & ev_flag)) == ev_flag);
+}
+
+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->thread_state = THREAD_YIELD;
+ //ev_data->event_stack = NULL;
+ //ev_data->timer_ticks = 0;
+ ev_data->next_input = EV_TICKS;
+ ev_data->next_event = EV_INPUT;
+ /* callbacks */
+ for (int i= 0; i < EVENT_CT; i++)
+ ev_data->cb[i] = NULL;
+}
+
+/* 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) 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));
+}
+
+static void lua_interrupt_set(lua_State *L, bool is_enabled)
+{
+ const int hookmask = LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT;
+
+ if (is_enabled)
+ lua_sethook(L, lua_interrupt_callback, hookmask, 1 );
+ else
+ lua_sethook(L, NULL, 0, 0 );
+}
+
+static int lua_rev_callback(lua_State *L, struct cb_data *cbd)
+{
+ int lua_status = LUA_ERRRUN;
+
+ if (L != NULL)
+ {
+ /* load cb function from lua registry */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->cb_ref);
+
+ lua_pushinteger(L, cbd->id);
+ lua_pushlightuserdata (L, cbd->data);
+
+ 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;
+}
+
+static void event_thread(void)
+{
+ unsigned long action;
+ int event;
+ int ev_flag;
+
+ while(ev_data.thread_state != THREAD_QUIT && lua_status(ev_data.L) == LUA_SUCCESS)
+ {
+ rev_lock_mtx();
+ lua_interrupt_set(ev_data.L, true);
+
+ for (event = 0; event < EVENT_CT; event++)
+ {
+ ev_flag = thread_ev_states[event];
+ if (!has_event(ev_flag) || is_suspend(ev_flag))
+ continue; /* check next event */
+
+ ev_data.thread_state &= ~(ev_flag); /* event handled */
+
+ 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 (ev_data.cb[ACTEVENT]->id == ACTION_NONE ||
+ rb->button_status() != 0)
+ continue; /* check next event */
+ }
+ ev_data.cb[ACTEVENT]->id = action;
+ break;
+ case BUTEVENT:
+ ev_data.cb[BUTEVENT]->id = rb->button_get(false);
+ if (ev_data.cb[BUTEVENT]->id == 0)
+ continue; /* 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)
+ {
+ rev_unlock_mtx();
+ goto event_error;
+ }
+ }
+ 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);
+ ev_data.next_event = EV_TICKS;
+ rb->yield();
+ } while(ev_data.thread_state == THREAD_YIELD || is_suspend(THREAD_SUSPENDMASK));
+
+ }
+
+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)
+{
+ 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_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)
+{
+ lua_State *L = ev_data->L;
+
+ lua_createtable(L, 2, 0);
+
+ ev_data->event_stack = (long *) lua_newuserdata (L, DEFAULT_STACK_SIZE);
+
+ /* attach EVENT_METATABLE to ud so we get notified on garbage collection */
+ luaL_getmetatable (L, EVENT_METATABLE);
+ lua_setmetatable (L, -2);
+ 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 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 void exit_event_thread(struct event_data *ev_data)
+{
+ ev_data->thread_state = THREAD_QUIT;
+ rb->thread_wait(ev_data->thread_id); /* wait for thread to exit */
+
+ ev_data->thread_state = THREAD_YIELD;
+ 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);
+ destroy_event_thread_ref(ev_data);
+ lua_interrupt_set(ev_data->L, false);
+ }
+ return;
+ }
+ 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,
+ DEFAULT_STACK_SIZE,
+ 0,
+ EVENT_THREAD
+ IF_PRIO(, PRIORITY_SYSTEM)
+ IF_COP(, COP));
+
+ /* Timer is used to poll waiting events */
+ rb->timer_register(0, 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 */
+ 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);
+}
+
+static void register_playbk_events(int flag_events,
+ void (*handler)(unsigned short id, void *data))
+{
+ long unsigned int i = 0;
+ 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 < ARRAYLEN(playback_events); i++, flag_events >>= 1)
+ {
+ 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);
+ }
+}
+
+static void destroy_event_userdata(lua_State *L, int event)
+{
+ if (ev_data.cb[event] != NULL)
+ {
+ int ev_flag = thread_ev_states[event];
+ ev_data.thread_state &= ~(ev_flag | (ev_flag << 8) | (ev_flag << 16));
+
+ luaL_unref (L, LUA_REGISTRYINDEX, ev_data.cb[event]->cb_ref);
+ ev_data.cb[event] = NULL;
+ }
+}
+
+static void create_event_userdata(lua_State *L, 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 */
+ 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 */
+ luaL_getmetatable (L, EVENT_METATABLE);
+ lua_setmetatable (L, -2);
+ /* 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)
+ 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])
+ register_playbk_events(0, &playback_event_callback);
+
+ for( int i= 0; i < EVENT_CT; i++)
+ {
+ if (d == ev_data.cb[i])
+ 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;
+}
+
+/******************************************************************************
+ * LUA INTERFACE **************************************************************
+*******************************************************************************
+*/
+
+static int rockev_register(lua_State *L)
+{
+ int event = luaL_checkoption(L, 1, NULL, ev_map);
+ int ev_flag = thread_ev_states[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 */
+
+ switch (event)
+ {
+ case ACTEVENT:
+ /* fall through */
+ case BUTEVENT:
+ ev_data.thread_state |= (ev_flag | (ev_flag << 16));
+ break;
+ case CUSTOMEVENT:
+ break;
+ case PLAYBKEVENT:
+ /* see register_playbk_events() for flags */
+ playbk_events = luaL_optinteger(L, 3, 0x3F);
+ register_playbk_events(playbk_events, &playback_event_callback);
+ break;
+ case TIMEREVENT:
+ ev_data.timer_ticks = luaL_checkinteger(L, 3);
+ ev_data.cb[TIMEREVENT]->id = *rb->current_tick + ev_data.timer_ticks;
+ break;
+ }
+
+ init_event_thread(true, &ev_data);
+
+ return 1; /* returns cb_data */
+}
+
+static int rockev_suspend(lua_State *L)
+{
+ int event; /*Arg 1 is event pass nil to suspend all */
+ bool suspend = luaL_optboolean(L, 2, true);
+ int ev_flag = THREAD_SUSPENDMASK;
+
+ if (!lua_isnoneornil(L, 1))
+ {
+ event = luaL_checkoption(L, 1, NULL, ev_map);
+ ev_flag = thread_ev_states[event] << 8;
+ }
+
+ if (suspend)
+ ev_data.thread_state |= ev_flag;
+ else
+ ev_data.thread_state &= ~(ev_flag);
+
+ return 0;
+}
+
+static int rockev_trigger(lua_State *L)
+{
+ int event = luaL_checkoption(L, 1, NULL, ev_map);
+ bool enable = luaL_optboolean(L, 2, true);
+
+ int ev_flag;
+
+ /* protect from invalid events */
+ if (ev_data.cb[event] != 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);
+
+ if (enable)
+ ev_data.thread_state |= ev_flag;
+ else
+ ev_data.thread_state &= ~(ev_flag);
+ }
+ return 0;
+}
+
+static int rockev_unregister(lua_State *L)
+{
+ 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;
+}
+
+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);
+ event_create_meta (L);
+ luaL_register (L, LUA_ROCKEVENTSNAME, evlib);
+ return 1;
+}
diff --git a/apps/plugins/lua/rocklib_events.h b/apps/plugins/lua/rocklib_events.h
new file mode 100644
index 0000000000..c96a25e577
--- /dev/null
+++ b/apps/plugins/lua/rocklib_events.h
@@ -0,0 +1,5 @@
+#define LUA_ROCKEVENTSNAME "rockev"
+int luaopen_rockevents (lua_State *L);
+
+/* in rockaux.c */
+int get_plugin_action(int timeout, bool with_remote);
diff --git a/apps/plugins/lua/rocklua.c b/apps/plugins/lua/rocklua.c
index eec6ee54f9..0d0b1f63f7 100644
--- a/apps/plugins/lua/rocklua.c
+++ b/apps/plugins/lua/rocklua.c
@@ -26,7 +26,7 @@
#include "rocklib.h"
#include "rocklib_img.h"
#include "luadir.h"
-
+#include "rocklib_events.h"
static const luaL_Reg lualibs[] = {
@@ -40,6 +40,7 @@ static const luaL_Reg lualibs[] = {
{LUA_OSLIBNAME, luaopen_os},
{LUA_ROCKLIBNAME, luaopen_rock},
{LUA_ROCKLIBNAME, luaopen_rock_img},
+ {LUA_ROCKEVENTSNAME, luaopen_rockevents},
{LUA_DIRLIBNAME, luaopen_luadir},
{NULL, NULL}
};