Mercurial > ~mikael > mcabber > hg
diff mcabber/mcabber/hooks.c @ 1795:47699a09ceb3
Rework the hook system
This hook system implementation should be more efficient and a little
more flexible.
author | Mikael Berthe <mikael@lilotux.net> |
---|---|
date | Sat, 20 Mar 2010 14:38:17 +0100 |
parents | 1f913c92c9b2 |
children | 5e2db25fdb17 |
line wrap: on
line diff
--- a/mcabber/mcabber/hooks.c Mon Mar 15 17:58:32 2010 +0200 +++ b/mcabber/mcabber/hooks.c Sat Mar 20 14:38:17 2010 +0100 @@ -41,39 +41,140 @@ typedef struct { hk_handler_t handler; - guint32 flags; - gpointer userdata; + gint priority; + gpointer userdata; + guint hid; } hook_list_data_t; -static GSList *hk_handler_queue = NULL; +static GHashTable *hk_handler_hash = NULL; -void hk_add_handler(hk_handler_t handler, guint32 flags, gpointer userdata) +// _new_hook_id() +// Return a unique Hook Id +static guint _new_hook_id(void) +{ + static guint hidcounter; + + return ++hidcounter; +} + +// _new_hook_queue(hookname) +// Create a new hash table entry with a GSList pointer for the specified hook +static GSList **_new_hook_queue(const gchar *hookname) { - hook_list_data_t *h = g_new(hook_list_data_t, 1); - h->handler = handler; - h->flags = flags; - h->userdata = userdata; - hk_handler_queue = g_slist_append(hk_handler_queue, h); + GSList **p; + // Create the hash table if needed. + if (!hk_handler_hash) { + hk_handler_hash = g_hash_table_new_full(&g_str_hash, &g_str_equal, + &g_free, &g_free); + if (!hk_handler_hash) { + scr_log_print(LPRINT_LOGNORM, "Couldn't create hook hash table!"); + return NULL; + } + } + + // Add a queue for the requested hook + p = g_new(GSList*, 1); + *p = NULL; + g_hash_table_insert(hk_handler_hash, g_strdup(hookname), p); + + return p; +} + +static gint _hk_compare_prio(hook_list_data_t *a, hook_list_data_t *b) +{ + if (a->priority > b->priority) + return 1; + return 0; } -static gint hk_queue_search_cb(hook_list_data_t *a, hook_list_data_t *b) +// hk_add_handler(handler, hookname, priority, userdata) +// Create a hook handler and a hook hash entry if needed. +// Return the handler id. +guint hk_add_handler(hk_handler_t handler, const gchar *hookname, + gint priority, gpointer userdata) { - if (a->handler == b->handler && a->userdata == b->userdata) + GSList **hqueue = NULL; + hook_list_data_t *h = g_new(hook_list_data_t, 1); + + h->handler = handler; + h->priority = priority; + h->userdata = userdata; + h->hid = _new_hook_id(); + + if (hk_handler_hash) + hqueue = g_hash_table_lookup(hk_handler_hash, hookname); + + if (!hqueue) + hqueue = _new_hook_queue(hookname); + + if (!hqueue) return 0; - else - return 1; + + *hqueue = g_slist_insert_sorted(*hqueue, h, (GCompareFunc)_hk_compare_prio); + + return h->hid; +} + +static gint _hk_queue_search_cb(hook_list_data_t *a, guint *hid) +{ + if (a->hid == *hid) + return 0; + return 1; } -void hk_del_handler(hk_handler_t handler, gpointer userdata) +// hk_del_handler(hookname, hook_id) +// Remove the handler with specified hook id from the hookname queue. +// The hash entry is removed if the queue is empty. +void hk_del_handler(const gchar *hookname, guint hid) { - hook_list_data_t h = { handler, 0, userdata }; - GSList *el = g_slist_find_custom(hk_handler_queue, &h, - (GCompareFunc) hk_queue_search_cb); + GSList **hqueue; + GSList *el; + + if (!hid) + return; + + hqueue = g_hash_table_lookup(hk_handler_hash, hookname); + + if (!hqueue) { + scr_log_print(LPRINT_LOGNORM, "*ERROR*: Couldn't remove hook handler!"); + return; + } + + el = g_slist_find_custom(*hqueue, &hid, + (GCompareFunc)_hk_queue_search_cb); if (el) { g_free(el->data); - hk_handler_queue = g_slist_delete_link(hk_handler_queue, el); + *hqueue = g_slist_delete_link(*hqueue, el); + // Remove hook hash table entry if the hook queue is empty + if (!*hqueue) + g_hash_table_remove(hk_handler_hash, hookname); } } + +// hk_run_handlers(hookname, args) +// Process all hooks for the "hookname" event. +// Note that the processing is interrupted as soon as one of the handlers +// do not return HOOK_HANDLER_RESULT_ALLOW_MORE_HOOKS (i.e. 0). +guint hk_run_handlers(const gchar *hookname, hk_arg_t *args) +{ + GSList **hqueue; + GSList *h; + guint ret = 0; + + if (!hk_handler_hash) + return 0; + + hqueue = g_hash_table_lookup(hk_handler_hash, hookname); + if (!hqueue) + return 0; // Should we use a special code? + + for (h = *hqueue; h; h = g_slist_next(h)) { + hook_list_data_t *data = h->data; + ret = (data->handler)(hookname, args, data->userdata); + if (ret) break; + } + return ret; +} #endif static char *extcmd; @@ -238,26 +339,16 @@ #ifdef MODULES_ENABLE { - GSList *h = hk_handler_queue; - if (h) { - // We can use a const array for keys/static values, so modules - // can do fast known to them args check by just comparing pointers... - hk_arg_t args[] = { - { "hook", "hook-message-in" }, - { "jid", bjid }, - { "resource", resname }, - { "message", wmsg }, - { "groupchat", is_groupchat ? "true" : "false" }, - { "urgent", urgent ? "true" : "false" }, - { NULL, NULL }, - }; - while (h) { - hook_list_data_t *data = h->data; - if (data->flags & HOOK_MESSAGE_IN) - (data->handler) (HOOK_MESSAGE_IN, args, data->userdata); - h = g_slist_next(h); - } - } + hk_arg_t args[] = { + { "jid", bjid }, + { "resource", resname }, + { "message", wmsg }, + { "groupchat", is_groupchat ? "true" : "false" }, + { "urgent", urgent ? "true" : "false" }, + { NULL, NULL }, + }; + hk_run_handlers(HOOK_MESSAGE_IN, args); + // TODO: check (and use) return value } #endif @@ -342,21 +433,13 @@ #ifdef MODULES_ENABLE { - GSList *h = hk_handler_queue; - if (h) { - hk_arg_t args[] = { - { "hook", "hook-message-out" }, - { "jid", bjid }, - { "message", wmsg }, - { NULL, NULL }, - }; - while (h) { - hook_list_data_t *data = h->data; - if (data->flags & HOOK_MESSAGE_OUT) - (data->handler) (HOOK_MESSAGE_OUT, args, data->userdata); - h = g_slist_next(h); - } - } + hk_arg_t args[] = { + { "jid", bjid }, + { "message", wmsg }, + { NULL, NULL }, + }; + hk_run_handlers(HOOK_MESSAGE_OUT, args); + // TODO: check (and use) return value } #endif @@ -433,28 +516,20 @@ #ifdef MODULES_ENABLE { - GSList *h = hk_handler_queue; - if (h) { - char os[2] = " \0"; - char ns[2] = " \0"; - hk_arg_t args[] = { - { "hook", "hook-status-change" }, - { "jid", bjid }, - { "resource", rn }, - { "old_status", os }, - { "new_status", ns }, - { "message", status_msg ? status_msg : "" }, - { NULL, NULL }, - }; - os[0] = imstatus2char[oldstat]; - ns[0] = imstatus2char[status]; - while (h) { - hook_list_data_t *data = h->data; - if (data->flags & HOOK_STATUS_CHANGE) - (data->handler) (HOOK_STATUS_CHANGE, args, data->userdata); - h = g_slist_next(h); - } - } + char os[2] = " \0"; + char ns[2] = " \0"; + hk_arg_t args[] = { + { "jid", bjid }, + { "resource", rn }, + { "old_status", os }, + { "new_status", ns }, + { "message", status_msg ? status_msg : "" }, + { NULL, NULL }, + }; + os[0] = imstatus2char[oldstat]; + ns[0] = imstatus2char[status]; + + hk_run_handlers(HOOK_STATUS_CHANGE, args); } #endif @@ -471,23 +546,15 @@ #ifdef MODULES_ENABLE { - GSList *h = hk_handler_queue; - if (h) { - char ns[2] = " \0"; - hk_arg_t args[] = { - { "hook", "hook-my-status-change" }, - { "new_status", ns }, - { "message", msg ? msg : "" }, - { NULL, NULL }, - }; - ns[0] = imstatus2char[new_status]; - while (h) { - hook_list_data_t *data = h->data; - if (data->flags & HOOK_MY_STATUS_CHANGE) - (data->handler) (HOOK_MY_STATUS_CHANGE, args, data->userdata); - h = g_slist_next(h); - } - } + char ns[2] = " \0"; + hk_arg_t args[] = { + { "new_status", ns }, + { "message", msg ? msg : "" }, + { NULL, NULL }, + }; + ns[0] = imstatus2char[new_status]; + + hk_run_handlers(HOOK_MY_STATUS_CHANGE, args); } #endif @@ -501,19 +568,10 @@ #ifdef MODULES_ENABLE { - GSList *h = hk_handler_queue; - if (h) { - hk_arg_t args[] = { - { "hook", "hook-post-connect" }, - { NULL, NULL }, - }; - while (h) { - hook_list_data_t *data = h->data; - if (data->flags & HOOK_POST_CONNECT) - (data->handler) (HOOK_POST_CONNECT, args, data->userdata); - h = g_slist_next(h); - } - } + hk_arg_t args[] = { + { NULL, NULL }, + }; + hk_run_handlers(HOOK_POST_CONNECT, args); } #endif @@ -537,19 +595,10 @@ #ifdef MODULES_ENABLE { - GSList *h = hk_handler_queue; - if (h) { - hk_arg_t args[] = { - { "hook", "hook-pre-disconnect" }, - { NULL, NULL }, - }; - while (h) { - hook_list_data_t *data = h->data; - if (data->flags & HOOK_PRE_DISCONNECT) - (data->handler) (HOOK_PRE_DISCONNECT, args, data->userdata); - h = g_slist_next(h); - } - } + hk_arg_t args[] = { + { NULL, NULL }, + }; + hk_run_handlers(HOOK_PRE_DISCONNECT, args); } #endif