# HG changeset patch # User Myhailo Danylenko # Date 1237738558 -7200 # Node ID 50d4e9bc622d055e9039b45c75332d2a30ccd71b # Parent a95a3a73482cc0b4f02522d92c9cedb0e73c4121 Events diff -r a95a3a73482c -r 50d4e9bc622d TODO --- a/TODO Sun Mar 22 05:49:14 2009 +0200 +++ b/TODO Sun Mar 22 18:15:58 2009 +0200 @@ -18,4 +18,5 @@ eliminate main.parse_args? common disco routines should get connection object from outer space - no mcabber dependency, the same for forms parsing, pep sending (though it anyway needs to be converted to pubsub)... tune should set self-song only on reply from server +test node config diff -r a95a3a73482c -r 50d4e9bc622d main.c --- a/main.c Sun Mar 22 05:49:14 2009 +0200 +++ b/main.c Sun Mar 22 18:15:58 2009 +0200 @@ -21,6 +21,7 @@ #include "hooks.h" // hk_add_handler, hk_del_handler #include "settings.h" // settings_set, settings_del, settings_get #include "compl.h" // compl_new_category, compl_add_category_word, compl_del_category_word +#include "events.h" // evs_* // global lua state object, necessary for uninitialization function @@ -525,6 +526,105 @@ return 0; } +// MCABBER EVENTS + +/// event context +/// Enum, indicating what exactly caused event function firing. +/// XXX Well, I am not really understand that EVS_* constants mapping semantics, +/// so, I just provide data, obtained by experiment. +/// G: +static const string2enum_t lua_event_context[] = { + { "timeout", EVS_CONTEXT_TIMEOUT }, + { "cancel", EVS_CONTEXT_CANCEL }, + { "reject", EVS_CONTEXT_USER }, + { "accept", EVS_CONTEXT_USER + 1 }, + { NULL, 0 }, +}; + +typedef struct { + lua_State *L; + int reference; +} lua_event_callback_t; + +static GSList *lua_events = NULL; + +/// event function +/// Function to be called, when some event state change occurs +/// A: event context +static int lua_event_callback (eviqs *event, int context) +{ + lua_event_callback_t *cb = event->data; + + lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); + luaL_pushenum (cb->L, context, lua_event_context); + if (lua_pcall (cb->L, 1, 0, 0)) { + scr_LogPrint (LPRINT_LOGNORM, "lua: Event callback execution error: %s", lua_tostring (cb->L, -1)); + lua_pop (cb->L, 1); + } + + luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference); + lua_events = g_slist_remove (lua_events, event); + evs_del (event->id); // XXX + + return 0; // XXX +} + +/// main.event +/// Creates new event. If called without arguments, returns event id list. +/// A: event function (optional), integer (expiration timeout, optional), string (description, optional) +/// R: string (event id) or nothing (creation error) or table (list of event names) +static int lua_main_event (lua_State *L) +{ + int top = lua_gettop (L); + if (top > 0) { // Create + eviqs *event; + lua_event_callback_t *cb; + int timeout = 0; + char *desc = NULL; + luaL_argcheck (L, lua_type (L, 1) == LUA_TFUNCTION, 1, "event function expected"); + + if (top > 1) { + timeout = luaL_checkinteger (L, 2); + if (top > 2) { + desc = g_strdup (luaL_checkstring (L, 3)); // g_freed by mcabber + lua_pop (L, 2); + } else + lua_pop (L, 1); + } + + event = evs_new (EVS_TYPE_USER, timeout); + if (!event) + return 0; + + lua_pushvalue (L, 1); + cb = g_new (lua_event_callback_t, 1); // g_freed by mcabber + cb->L = L; + cb->reference = luaL_ref (L, LUA_REGISTRYINDEX); + + event->data = cb; + event->callback = lua_event_callback; + event->desc = desc; + + lua_events = g_slist_prepend (lua_events, event); + + lua_pushstring (L, event->id); + return 1; + } else { // List + GSList *events = evs_geteventslist (0); + GSList *event; + + lua_newtable (L); + for (event = events; event; event = g_slist_next (event)) { + lua_pushstring (L, event->data); + luaL_ref (L, -2); + g_free (event->data); + } + g_slist_free (events); + + return 1; + } +} + // MCABBER COMMANDS /// completion type @@ -1072,6 +1172,7 @@ reg ( fileoption ) reg ( add_feature ) reg ( del_feature ) + reg ( event ) reg ( parse_args ) reg ( add_category ) reg ( del_category ) @@ -1161,6 +1262,14 @@ return NULL; } +static void lua_events_destroy (eviqs *event, gpointer ignore) +{ + lua_event_callback_t *cb = event->data; + evs_callback (event->id, EVS_CONTEXT_CANCEL); // ignore + luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference); + evs_del (event->id); +} + static void lua_timers_destroy (guint source, gpointer ignore) { g_source_remove (source); @@ -1202,6 +1311,10 @@ g_slist_free (lua_timers); lua_timers = NULL; + g_slist_foreach (lua_events, (GFunc) lua_events_destroy, NULL); + g_slist_free (lua_events); + lua_events = NULL; + g_slist_foreach (lua_added_features, (GFunc) lua_features_destroy, NULL); g_slist_free (lua_added_features); lua_added_features = NULL;