comparison 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
comparison
equal deleted inserted replaced
1794:58d1390f28ca 1795:47699a09ceb3
39 #ifdef MODULES_ENABLE 39 #ifdef MODULES_ENABLE
40 #include <glib.h> 40 #include <glib.h>
41 41
42 typedef struct { 42 typedef struct {
43 hk_handler_t handler; 43 hk_handler_t handler;
44 guint32 flags; 44 gint priority;
45 gpointer userdata; 45 gpointer userdata;
46 guint hid;
46 } hook_list_data_t; 47 } hook_list_data_t;
47 48
48 static GSList *hk_handler_queue = NULL; 49 static GHashTable *hk_handler_hash = NULL;
49 50
50 void hk_add_handler(hk_handler_t handler, guint32 flags, gpointer userdata) 51 // _new_hook_id()
51 { 52 // Return a unique Hook Id
53 static guint _new_hook_id(void)
54 {
55 static guint hidcounter;
56
57 return ++hidcounter;
58 }
59
60 // _new_hook_queue(hookname)
61 // Create a new hash table entry with a GSList pointer for the specified hook
62 static GSList **_new_hook_queue(const gchar *hookname)
63 {
64 GSList **p;
65 // Create the hash table if needed.
66 if (!hk_handler_hash) {
67 hk_handler_hash = g_hash_table_new_full(&g_str_hash, &g_str_equal,
68 &g_free, &g_free);
69 if (!hk_handler_hash) {
70 scr_log_print(LPRINT_LOGNORM, "Couldn't create hook hash table!");
71 return NULL;
72 }
73 }
74
75 // Add a queue for the requested hook
76 p = g_new(GSList*, 1);
77 *p = NULL;
78 g_hash_table_insert(hk_handler_hash, g_strdup(hookname), p);
79
80 return p;
81 }
82
83 static gint _hk_compare_prio(hook_list_data_t *a, hook_list_data_t *b)
84 {
85 if (a->priority > b->priority)
86 return 1;
87 return 0;
88 }
89
90 // hk_add_handler(handler, hookname, priority, userdata)
91 // Create a hook handler and a hook hash entry if needed.
92 // Return the handler id.
93 guint hk_add_handler(hk_handler_t handler, const gchar *hookname,
94 gint priority, gpointer userdata)
95 {
96 GSList **hqueue = NULL;
52 hook_list_data_t *h = g_new(hook_list_data_t, 1); 97 hook_list_data_t *h = g_new(hook_list_data_t, 1);
98
53 h->handler = handler; 99 h->handler = handler;
54 h->flags = flags; 100 h->priority = priority;
55 h->userdata = userdata; 101 h->userdata = userdata;
56 hk_handler_queue = g_slist_append(hk_handler_queue, h); 102 h->hid = _new_hook_id();
57 } 103
58 104 if (hk_handler_hash)
59 static gint hk_queue_search_cb(hook_list_data_t *a, hook_list_data_t *b) 105 hqueue = g_hash_table_lookup(hk_handler_hash, hookname);
60 { 106
61 if (a->handler == b->handler && a->userdata == b->userdata) 107 if (!hqueue)
108 hqueue = _new_hook_queue(hookname);
109
110 if (!hqueue)
62 return 0; 111 return 0;
63 else 112
64 return 1; 113 *hqueue = g_slist_insert_sorted(*hqueue, h, (GCompareFunc)_hk_compare_prio);
65 } 114
66 115 return h->hid;
67 void hk_del_handler(hk_handler_t handler, gpointer userdata) 116 }
68 { 117
69 hook_list_data_t h = { handler, 0, userdata }; 118 static gint _hk_queue_search_cb(hook_list_data_t *a, guint *hid)
70 GSList *el = g_slist_find_custom(hk_handler_queue, &h, 119 {
71 (GCompareFunc) hk_queue_search_cb); 120 if (a->hid == *hid)
121 return 0;
122 return 1;
123 }
124
125 // hk_del_handler(hookname, hook_id)
126 // Remove the handler with specified hook id from the hookname queue.
127 // The hash entry is removed if the queue is empty.
128 void hk_del_handler(const gchar *hookname, guint hid)
129 {
130 GSList **hqueue;
131 GSList *el;
132
133 if (!hid)
134 return;
135
136 hqueue = g_hash_table_lookup(hk_handler_hash, hookname);
137
138 if (!hqueue) {
139 scr_log_print(LPRINT_LOGNORM, "*ERROR*: Couldn't remove hook handler!");
140 return;
141 }
142
143 el = g_slist_find_custom(*hqueue, &hid,
144 (GCompareFunc)_hk_queue_search_cb);
72 if (el) { 145 if (el) {
73 g_free(el->data); 146 g_free(el->data);
74 hk_handler_queue = g_slist_delete_link(hk_handler_queue, el); 147 *hqueue = g_slist_delete_link(*hqueue, el);
75 } 148 // Remove hook hash table entry if the hook queue is empty
149 if (!*hqueue)
150 g_hash_table_remove(hk_handler_hash, hookname);
151 }
152 }
153
154 // hk_run_handlers(hookname, args)
155 // Process all hooks for the "hookname" event.
156 // Note that the processing is interrupted as soon as one of the handlers
157 // do not return HOOK_HANDLER_RESULT_ALLOW_MORE_HOOKS (i.e. 0).
158 guint hk_run_handlers(const gchar *hookname, hk_arg_t *args)
159 {
160 GSList **hqueue;
161 GSList *h;
162 guint ret = 0;
163
164 if (!hk_handler_hash)
165 return 0;
166
167 hqueue = g_hash_table_lookup(hk_handler_hash, hookname);
168 if (!hqueue)
169 return 0; // Should we use a special code?
170
171 for (h = *hqueue; h; h = g_slist_next(h)) {
172 hook_list_data_t *data = h->data;
173 ret = (data->handler)(hookname, args, data->userdata);
174 if (ret) break;
175 }
176 return ret;
76 } 177 }
77 #endif 178 #endif
78 179
79 static char *extcmd; 180 static char *extcmd;
80 181
236 if (settings_opt_get_int("eventcmd_use_nickname")) 337 if (settings_opt_get_int("eventcmd_use_nickname"))
237 ename = roster_getname(bjid); 338 ename = roster_getname(bjid);
238 339
239 #ifdef MODULES_ENABLE 340 #ifdef MODULES_ENABLE
240 { 341 {
241 GSList *h = hk_handler_queue; 342 hk_arg_t args[] = {
242 if (h) { 343 { "jid", bjid },
243 // We can use a const array for keys/static values, so modules 344 { "resource", resname },
244 // can do fast known to them args check by just comparing pointers... 345 { "message", wmsg },
245 hk_arg_t args[] = { 346 { "groupchat", is_groupchat ? "true" : "false" },
246 { "hook", "hook-message-in" }, 347 { "urgent", urgent ? "true" : "false" },
247 { "jid", bjid }, 348 { NULL, NULL },
248 { "resource", resname }, 349 };
249 { "message", wmsg }, 350 hk_run_handlers(HOOK_MESSAGE_IN, args);
250 { "groupchat", is_groupchat ? "true" : "false" }, 351 // TODO: check (and use) return value
251 { "urgent", urgent ? "true" : "false" },
252 { NULL, NULL },
253 };
254 while (h) {
255 hook_list_data_t *data = h->data;
256 if (data->flags & HOOK_MESSAGE_IN)
257 (data->handler) (HOOK_MESSAGE_IN, args, data->userdata);
258 h = g_slist_next(h);
259 }
260 }
261 } 352 }
262 #endif 353 #endif
263 354
264 // External command 355 // External command
265 // - We do not call hk_ext_cmd() for history lines in MUC 356 // - We do not call hk_ext_cmd() for history lines in MUC
340 if (!nick) 431 if (!nick)
341 hlog_write_message(bjid, timestamp, 1, msg); 432 hlog_write_message(bjid, timestamp, 1, msg);
342 433
343 #ifdef MODULES_ENABLE 434 #ifdef MODULES_ENABLE
344 { 435 {
345 GSList *h = hk_handler_queue; 436 hk_arg_t args[] = {
346 if (h) { 437 { "jid", bjid },
347 hk_arg_t args[] = { 438 { "message", wmsg },
348 { "hook", "hook-message-out" }, 439 { NULL, NULL },
349 { "jid", bjid }, 440 };
350 { "message", wmsg }, 441 hk_run_handlers(HOOK_MESSAGE_OUT, args);
351 { NULL, NULL }, 442 // TODO: check (and use) return value
352 };
353 while (h) {
354 hook_list_data_t *data = h->data;
355 if (data->flags & HOOK_MESSAGE_OUT)
356 (data->handler) (HOOK_MESSAGE_OUT, args, data->userdata);
357 h = g_slist_next(h);
358 }
359 }
360 } 443 }
361 #endif 444 #endif
362 445
363 // External command 446 // External command
364 hk_ext_cmd(bjid, 'M', 'S', NULL); 447 hk_ext_cmd(bjid, 'M', 'S', NULL);
431 scr_draw_roster(); 514 scr_draw_roster();
432 hlog_write_status(bjid, timestamp, status, status_msg); 515 hlog_write_status(bjid, timestamp, status, status_msg);
433 516
434 #ifdef MODULES_ENABLE 517 #ifdef MODULES_ENABLE
435 { 518 {
436 GSList *h = hk_handler_queue; 519 char os[2] = " \0";
437 if (h) { 520 char ns[2] = " \0";
438 char os[2] = " \0"; 521 hk_arg_t args[] = {
439 char ns[2] = " \0"; 522 { "jid", bjid },
440 hk_arg_t args[] = { 523 { "resource", rn },
441 { "hook", "hook-status-change" }, 524 { "old_status", os },
442 { "jid", bjid }, 525 { "new_status", ns },
443 { "resource", rn }, 526 { "message", status_msg ? status_msg : "" },
444 { "old_status", os }, 527 { NULL, NULL },
445 { "new_status", ns }, 528 };
446 { "message", status_msg ? status_msg : "" }, 529 os[0] = imstatus2char[oldstat];
447 { NULL, NULL }, 530 ns[0] = imstatus2char[status];
448 }; 531
449 os[0] = imstatus2char[oldstat]; 532 hk_run_handlers(HOOK_STATUS_CHANGE, args);
450 ns[0] = imstatus2char[status];
451 while (h) {
452 hook_list_data_t *data = h->data;
453 if (data->flags & HOOK_STATUS_CHANGE)
454 (data->handler) (HOOK_STATUS_CHANGE, args, data->userdata);
455 h = g_slist_next(h);
456 }
457 }
458 } 533 }
459 #endif 534 #endif
460 535
461 // External command 536 // External command
462 hk_ext_cmd(ename ? ename : bjid, 'S', imstatus2char[status], NULL); 537 hk_ext_cmd(ename ? ename : bjid, 'S', imstatus2char[status], NULL);
469 imstatus2char[old_status], imstatus2char[new_status], 544 imstatus2char[old_status], imstatus2char[new_status],
470 (msg ? msg : "")); 545 (msg ? msg : ""));
471 546
472 #ifdef MODULES_ENABLE 547 #ifdef MODULES_ENABLE
473 { 548 {
474 GSList *h = hk_handler_queue; 549 char ns[2] = " \0";
475 if (h) { 550 hk_arg_t args[] = {
476 char ns[2] = " \0"; 551 { "new_status", ns },
477 hk_arg_t args[] = { 552 { "message", msg ? msg : "" },
478 { "hook", "hook-my-status-change" }, 553 { NULL, NULL },
479 { "new_status", ns }, 554 };
480 { "message", msg ? msg : "" }, 555 ns[0] = imstatus2char[new_status];
481 { NULL, NULL }, 556
482 }; 557 hk_run_handlers(HOOK_MY_STATUS_CHANGE, args);
483 ns[0] = imstatus2char[new_status];
484 while (h) {
485 hook_list_data_t *data = h->data;
486 if (data->flags & HOOK_MY_STATUS_CHANGE)
487 (data->handler) (HOOK_MY_STATUS_CHANGE, args, data->userdata);
488 h = g_slist_next(h);
489 }
490 }
491 } 558 }
492 #endif 559 #endif
493 560
494 //hlog_write_status(NULL, 0, status); 561 //hlog_write_status(NULL, 0, status);
495 } 562 }
499 const char *hook_command; 566 const char *hook_command;
500 char *cmdline; 567 char *cmdline;
501 568
502 #ifdef MODULES_ENABLE 569 #ifdef MODULES_ENABLE
503 { 570 {
504 GSList *h = hk_handler_queue; 571 hk_arg_t args[] = {
505 if (h) { 572 { NULL, NULL },
506 hk_arg_t args[] = { 573 };
507 { "hook", "hook-post-connect" }, 574 hk_run_handlers(HOOK_POST_CONNECT, args);
508 { NULL, NULL },
509 };
510 while (h) {
511 hook_list_data_t *data = h->data;
512 if (data->flags & HOOK_POST_CONNECT)
513 (data->handler) (HOOK_POST_CONNECT, args, data->userdata);
514 h = g_slist_next(h);
515 }
516 }
517 } 575 }
518 #endif 576 #endif
519 577
520 hook_command = settings_opt_get("hook-post-connect"); 578 hook_command = settings_opt_get("hook-post-connect");
521 if (!hook_command) 579 if (!hook_command)
535 const char *hook_command; 593 const char *hook_command;
536 char *cmdline; 594 char *cmdline;
537 595
538 #ifdef MODULES_ENABLE 596 #ifdef MODULES_ENABLE
539 { 597 {
540 GSList *h = hk_handler_queue; 598 hk_arg_t args[] = {
541 if (h) { 599 { NULL, NULL },
542 hk_arg_t args[] = { 600 };
543 { "hook", "hook-pre-disconnect" }, 601 hk_run_handlers(HOOK_PRE_DISCONNECT, args);
544 { NULL, NULL },
545 };
546 while (h) {
547 hook_list_data_t *data = h->data;
548 if (data->flags & HOOK_PRE_DISCONNECT)
549 (data->handler) (HOOK_PRE_DISCONNECT, args, data->userdata);
550 h = g_slist_next(h);
551 }
552 }
553 } 602 }
554 #endif 603 #endif
555 604
556 hook_command = settings_opt_get("hook-pre-disconnect"); 605 hook_command = settings_opt_get("hook-pre-disconnect");
557 if (!hook_command) 606 if (!hook_command)