Mercurial > ~mikael > mcabber > hg
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) |