Mercurial > pidgin.yaz
comparison src/signals.c @ 6485:70d5122bc3ff
[gaim-migrate @ 6999]
Removed the old event system and replaced it with a much better signal
system. There will most likely be some bugs in this, but it seems to be
working for now. Plugins can now generate their own signals, and other
plugins can find those plugins and connect to them. This could give
plugins a form of IPC. It's also useful for other things. It's rather
flexible, except for the damn marshalling, but there's no way around that
that I or the glib people can see.
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Mon, 18 Aug 2003 01:03:43 +0000 |
parents | |
children | e5e8d21bd4d8 |
comparison
equal
deleted
inserted
replaced
6484:5ced8e111473 | 6485:70d5122bc3ff |
---|---|
1 /** | |
2 * @file signal.h Signal API | |
3 * @ingroup core | |
4 * | |
5 * gaim | |
6 * | |
7 * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org> | |
8 * | |
9 * This program is free software; you can redistribute it and/or modify | |
10 * it under the terms of the GNU General Public License as published by | |
11 * the Free Software Foundation; either version 2 of the License, or | |
12 * (at your option) any later version. | |
13 * | |
14 * This program is distributed in the hope that it will be useful, | |
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 * GNU General Public License for more details. | |
18 * | |
19 * You should have received a copy of the GNU General Public License | |
20 * along with this program; if not, write to the Free Software | |
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 */ | |
23 #include "internal.h" | |
24 | |
25 #include "debug.h" | |
26 #include "signals.h" | |
27 | |
28 typedef struct | |
29 { | |
30 void *instance; | |
31 | |
32 GHashTable *signals; | |
33 size_t signal_count; | |
34 | |
35 gulong next_signal_id; | |
36 | |
37 } GaimInstanceData; | |
38 | |
39 typedef struct | |
40 { | |
41 gulong id; | |
42 | |
43 GaimSignalMarshalFunc marshal; | |
44 | |
45 GList *handlers; | |
46 size_t handler_count; | |
47 | |
48 gulong next_handler_id; | |
49 | |
50 } GaimSignalData; | |
51 | |
52 typedef struct | |
53 { | |
54 gulong id; | |
55 GaimCallback cb; | |
56 void *handle; | |
57 void *data; | |
58 | |
59 } GaimSignalHandlerData; | |
60 | |
61 static GHashTable *instance_table = NULL; | |
62 | |
63 static void | |
64 destroy_instance_data(GaimInstanceData *instance_data) | |
65 { | |
66 g_hash_table_destroy(instance_data->signals); | |
67 | |
68 g_free(instance_data); | |
69 } | |
70 | |
71 static void | |
72 destroy_signal_data(GaimSignalData *signal_data) | |
73 { | |
74 GaimSignalHandlerData *handler_data; | |
75 GList *l; | |
76 | |
77 for (l = signal_data->handlers; l != NULL; l = l->next) | |
78 { | |
79 handler_data = (GaimSignalHandlerData *)l->data; | |
80 | |
81 g_free(l->data); | |
82 } | |
83 | |
84 g_list_free(signal_data->handlers); | |
85 | |
86 g_free(signal_data); | |
87 } | |
88 | |
89 gulong | |
90 gaim_signal_register(void *instance, const char *signal, | |
91 GaimSignalMarshalFunc marshal) | |
92 { | |
93 GaimInstanceData *instance_data; | |
94 GaimSignalData *signal_data; | |
95 | |
96 g_return_val_if_fail(instance != NULL, 0); | |
97 g_return_val_if_fail(signal != NULL, 0); | |
98 g_return_val_if_fail(marshal != NULL, 0); | |
99 | |
100 instance_data = | |
101 (GaimInstanceData *)g_hash_table_lookup(instance_table, instance); | |
102 | |
103 if (instance_data == NULL) | |
104 { | |
105 instance_data = g_new0(GaimInstanceData, 1); | |
106 | |
107 instance_data->instance = instance; | |
108 instance_data->next_signal_id = 1; | |
109 | |
110 instance_data->signals = | |
111 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, | |
112 (GDestroyNotify)destroy_signal_data); | |
113 | |
114 g_hash_table_insert(instance_table, instance, instance_data); | |
115 } | |
116 | |
117 signal_data = g_new0(GaimSignalData, 1); | |
118 signal_data->id = instance_data->next_signal_id; | |
119 signal_data->marshal = marshal; | |
120 signal_data->next_handler_id = 1; | |
121 | |
122 g_hash_table_insert(instance_data->signals, | |
123 g_strdup(signal), signal_data); | |
124 | |
125 instance_data->next_signal_id++; | |
126 instance_data->signal_count++; | |
127 | |
128 return signal_data->id; | |
129 } | |
130 | |
131 void | |
132 gaim_signal_unregister(void *instance, const char *signal) | |
133 { | |
134 GaimInstanceData *instance_data; | |
135 | |
136 g_return_if_fail(instance != NULL); | |
137 g_return_if_fail(signal != NULL); | |
138 | |
139 instance_data = | |
140 (GaimInstanceData *)g_hash_table_lookup(instance_table, instance); | |
141 | |
142 g_return_if_fail(instance_data != NULL); | |
143 | |
144 g_hash_table_remove(instance_data->signals, signal); | |
145 | |
146 instance_data->signal_count--; | |
147 | |
148 if (instance_data->signal_count == 0) | |
149 { | |
150 /* Unregister the instance. */ | |
151 g_hash_table_remove(instance_table, instance); | |
152 } | |
153 } | |
154 | |
155 void | |
156 gaim_signals_unregister_by_instance(void *instance) | |
157 { | |
158 gboolean found; | |
159 | |
160 g_return_if_fail(instance != NULL); | |
161 | |
162 found = g_hash_table_remove(instance_table, instance); | |
163 | |
164 /* | |
165 * Makes things easier (more annoying?) for developers who don't have | |
166 * things registering and unregistering in the right order :) | |
167 */ | |
168 g_return_if_fail(found); | |
169 } | |
170 | |
171 gulong | |
172 gaim_signal_connect(void *instance, const char *signal, void *handle, | |
173 GaimCallback func, void *data) | |
174 { | |
175 GaimInstanceData *instance_data; | |
176 GaimSignalData *signal_data; | |
177 GaimSignalHandlerData *handler_data; | |
178 | |
179 g_return_val_if_fail(instance != NULL, 0); | |
180 g_return_val_if_fail(signal != NULL, 0); | |
181 g_return_val_if_fail(handle != NULL, 0); | |
182 g_return_val_if_fail(func != NULL, 0); | |
183 | |
184 /* Get the instance data */ | |
185 instance_data = | |
186 (GaimInstanceData *)g_hash_table_lookup(instance_table, instance); | |
187 | |
188 g_return_val_if_fail(instance_data != NULL, 0); | |
189 | |
190 /* Get the signal data */ | |
191 signal_data = | |
192 (GaimSignalData *)g_hash_table_lookup(instance_data->signals, signal); | |
193 | |
194 if (signal_data == NULL) | |
195 { | |
196 gaim_debug(GAIM_DEBUG_ERROR, "signals", | |
197 "Signal data for %s not found!\n", signal); | |
198 return 0; | |
199 } | |
200 | |
201 /* Create the signal handler data */ | |
202 handler_data = g_new0(GaimSignalHandlerData, 1); | |
203 handler_data->id = signal_data->next_handler_id; | |
204 handler_data->cb = func; | |
205 handler_data->handle = handle; | |
206 handler_data->data = data; | |
207 | |
208 signal_data->handlers = g_list_append(signal_data->handlers, handler_data); | |
209 signal_data->handler_count++; | |
210 signal_data->next_handler_id++; | |
211 | |
212 return handler_data->id; | |
213 } | |
214 | |
215 void | |
216 gaim_signal_disconnect(void *instance, const char *signal, | |
217 void *handle, GaimCallback func) | |
218 { | |
219 GaimInstanceData *instance_data; | |
220 GaimSignalData *signal_data; | |
221 GaimSignalHandlerData *handler_data; | |
222 GList *l; | |
223 gboolean found = FALSE; | |
224 | |
225 g_return_if_fail(instance != NULL); | |
226 g_return_if_fail(signal != NULL); | |
227 g_return_if_fail(handle != NULL); | |
228 g_return_if_fail(func != NULL); | |
229 | |
230 /* Get the instance data */ | |
231 instance_data = | |
232 (GaimInstanceData *)g_hash_table_lookup(instance_table, instance); | |
233 | |
234 g_return_if_fail(instance_data != NULL); | |
235 | |
236 /* Get the signal data */ | |
237 signal_data = | |
238 (GaimSignalData *)g_hash_table_lookup(instance_data->signals, signal); | |
239 | |
240 if (signal_data == NULL) | |
241 { | |
242 gaim_debug(GAIM_DEBUG_ERROR, "signals", | |
243 "Signal data for %s not found!\n", signal); | |
244 return; | |
245 } | |
246 | |
247 /* Find the handler data. */ | |
248 for (l = signal_data->handlers; l != NULL; l = l->next) | |
249 { | |
250 handler_data = (GaimSignalHandlerData *)l->data; | |
251 | |
252 if (handler_data->handle == handle && handler_data->cb == func) | |
253 { | |
254 g_free(handler_data); | |
255 | |
256 signal_data->handlers = g_list_remove(signal_data->handlers, | |
257 handler_data); | |
258 signal_data->handler_count--; | |
259 | |
260 found = TRUE; | |
261 | |
262 break; | |
263 } | |
264 } | |
265 | |
266 /* See note somewhere about this actually helping developers.. */ | |
267 g_return_if_fail(found); | |
268 } | |
269 | |
270 /* | |
271 * TODO: Make this all more efficient by storing a list of handlers, keyed | |
272 * to a handle. | |
273 */ | |
274 static void | |
275 disconnect_handle_from_signals(const char *signal, | |
276 GaimSignalData *signal_data, void *handle) | |
277 { | |
278 GList *l, *l_next; | |
279 GaimSignalHandlerData *handler_data; | |
280 | |
281 for (l = signal_data->handlers; l != NULL; l = l_next) | |
282 { | |
283 handler_data = (GaimSignalHandlerData *)l->data; | |
284 l_next = l->next; | |
285 | |
286 if (handler_data->handle == handle) | |
287 { | |
288 g_free(handler_data); | |
289 | |
290 signal_data->handler_count--; | |
291 signal_data->handlers = g_list_remove(signal_data->handlers, | |
292 handler_data); | |
293 } | |
294 } | |
295 } | |
296 | |
297 static void | |
298 disconnect_handle_from_instance(void *instance, | |
299 GaimInstanceData *instance_data, | |
300 void *handle) | |
301 { | |
302 g_hash_table_foreach(instance_data->signals, | |
303 (GHFunc)disconnect_handle_from_signals, handle); | |
304 } | |
305 | |
306 void | |
307 gaim_signals_disconnect_by_handle(void *handle) | |
308 { | |
309 g_return_if_fail(handle != NULL); | |
310 | |
311 g_hash_table_foreach(instance_table, | |
312 (GHFunc)disconnect_handle_from_instance, handle); | |
313 } | |
314 | |
315 void | |
316 gaim_signal_emit(void *instance, const char *signal, ...) | |
317 { | |
318 va_list args; | |
319 | |
320 va_start(args, signal); | |
321 gaim_signal_emit_vargs(instance, signal, args); | |
322 va_end(args); | |
323 } | |
324 | |
325 void | |
326 gaim_signal_emit_vargs(void *instance, const char *signal, va_list args) | |
327 { | |
328 GaimInstanceData *instance_data; | |
329 GaimSignalData *signal_data; | |
330 GaimSignalHandlerData *handler_data; | |
331 GList *l; | |
332 | |
333 g_return_if_fail(instance != NULL); | |
334 g_return_if_fail(signal != NULL); | |
335 | |
336 instance_data = | |
337 (GaimInstanceData *)g_hash_table_lookup(instance_table, instance); | |
338 | |
339 g_return_if_fail(instance_data != NULL); | |
340 | |
341 signal_data = | |
342 (GaimSignalData *)g_hash_table_lookup(instance_data->signals, signal); | |
343 | |
344 if (signal_data == NULL) | |
345 { | |
346 gaim_debug(GAIM_DEBUG_ERROR, "signals", | |
347 "Signal data for %s not found!\n", signal); | |
348 return; | |
349 } | |
350 | |
351 for (l = signal_data->handlers; l != NULL; l = l->next) | |
352 { | |
353 handler_data = (GaimSignalHandlerData *)l->data; | |
354 | |
355 signal_data->marshal(handler_data->cb, args, handler_data->data, NULL); | |
356 } | |
357 } | |
358 | |
359 void * | |
360 gaim_signal_emit_return_1(void *instance, const char *signal, ...) | |
361 { | |
362 void *ret_val; | |
363 va_list args; | |
364 | |
365 va_start(args, signal); | |
366 ret_val = gaim_signal_emit_vargs_return_1(instance, signal, args); | |
367 va_end(args); | |
368 | |
369 return ret_val; | |
370 } | |
371 | |
372 void * | |
373 gaim_signal_emit_vargs_return_1(void *instance, const char *signal, | |
374 va_list args) | |
375 { | |
376 GaimInstanceData *instance_data; | |
377 GaimSignalData *signal_data; | |
378 GaimSignalHandlerData *handler_data; | |
379 void *ret_val = NULL; | |
380 GList *l; | |
381 | |
382 g_return_val_if_fail(instance != NULL, NULL); | |
383 g_return_val_if_fail(signal != NULL, NULL); | |
384 | |
385 instance_data = | |
386 (GaimInstanceData *)g_hash_table_lookup(instance_table, instance); | |
387 | |
388 g_return_val_if_fail(instance_data != NULL, NULL); | |
389 | |
390 signal_data = | |
391 (GaimSignalData *)g_hash_table_lookup(instance_data->signals, signal); | |
392 | |
393 if (signal_data == NULL) | |
394 { | |
395 gaim_debug(GAIM_DEBUG_ERROR, "signals", | |
396 "Signal data for %s not found!\n", signal); | |
397 return 0; | |
398 } | |
399 | |
400 for (l = signal_data->handlers; l != NULL; l = l->next) | |
401 { | |
402 handler_data = (GaimSignalHandlerData *)l->data; | |
403 | |
404 signal_data->marshal(handler_data->cb, args, handler_data->data, | |
405 &ret_val); | |
406 } | |
407 | |
408 return ret_val; | |
409 } | |
410 | |
411 void | |
412 gaim_signals_init() | |
413 { | |
414 g_return_if_fail(instance_table == NULL); | |
415 | |
416 instance_table = | |
417 g_hash_table_new_full(g_direct_hash, g_direct_equal, | |
418 NULL, (GDestroyNotify)destroy_instance_data); | |
419 } | |
420 | |
421 void | |
422 gaim_signals_uninit() | |
423 { | |
424 g_return_if_fail(instance_table != NULL); | |
425 | |
426 g_hash_table_destroy(instance_table); | |
427 instance_table = NULL; | |
428 } | |
429 | |
430 /************************************************************************** | |
431 * Marshallers | |
432 **************************************************************************/ | |
433 void | |
434 gaim_marshal_VOID(GaimCallback cb, va_list args, void *data, | |
435 void **return_val) | |
436 { | |
437 ((void (*)(void *))cb)(data); | |
438 } | |
439 | |
440 void | |
441 gaim_marshal_VOID__POINTER(GaimCallback cb, va_list args, void *data, | |
442 void **return_val) | |
443 { | |
444 ((void (*)(void *, void *))cb)(va_arg(args, void *), data); | |
445 } | |
446 | |
447 void | |
448 gaim_marshal_VOID__POINTER_POINTER(GaimCallback cb, va_list args, | |
449 void *data, void **return_val) | |
450 { | |
451 ((void (*)(void *, void *, void *))cb)(va_arg(args, void *), | |
452 va_arg(args, void *), | |
453 data); | |
454 } | |
455 | |
456 void | |
457 gaim_marshal_VOID__POINTER_POINTER_UINT(GaimCallback cb, va_list args, | |
458 void *data, void **return_val) | |
459 { | |
460 ((void (*)(void *, void *, guint, void *))cb)(va_arg(args, void *), | |
461 va_arg(args, void *), | |
462 va_arg(args, guint), | |
463 data); | |
464 } | |
465 | |
466 void | |
467 gaim_marshal_VOID__POINTER_POINTER_POINTER(GaimCallback cb, va_list args, | |
468 void *data, void **return_val) | |
469 { | |
470 ((void (*)(void *, void *, void *, void *))cb)(va_arg(args, void *), | |
471 va_arg(args, void *), | |
472 va_arg(args, void *), | |
473 data); | |
474 } | |
475 | |
476 void | |
477 gaim_marshal_VOID__POINTER_POINTER_POINTER_POINTER(GaimCallback cb, | |
478 va_list args, | |
479 void *data, | |
480 void **return_val) | |
481 { | |
482 ((void (*)(void *, void *, void *, void *, void *))cb)( | |
483 va_arg(args, void *), va_arg(args, void *), | |
484 va_arg(args, void *), va_arg(args, void *), data); | |
485 } | |
486 void | |
487 gaim_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT(GaimCallback cb, | |
488 va_list args, | |
489 void *data, | |
490 void **return_val) | |
491 { | |
492 ((void (*)(void *, void *, void *, guint, guint, void *))cb)( | |
493 va_arg(args, void *), va_arg(args, void *), | |
494 va_arg(args, void *), va_arg(args, guint), | |
495 va_arg(args, guint), data); | |
496 } | |
497 | |
498 void | |
499 gaim_marshal_BOOLEAN__POINTER(GaimCallback cb, va_list args, void *data, | |
500 void **return_val) | |
501 { | |
502 gboolean ret_val; | |
503 | |
504 ret_val = ((gboolean (*)(void *, void *))cb)(va_arg(args, void *), data); | |
505 | |
506 if (return_val != NULL) | |
507 *return_val = GINT_TO_POINTER(ret_val); | |
508 } | |
509 | |
510 void | |
511 gaim_marshal_BOOLEAN__POINTER_POINTER(GaimCallback cb, va_list args, | |
512 void *data, void **return_val) | |
513 { | |
514 gboolean ret_val; | |
515 | |
516 ret_val = ((gboolean (*)(void *, void *, void *))cb)(va_arg(args, void *), | |
517 va_arg(args, void *), | |
518 data); | |
519 | |
520 if (return_val != NULL) | |
521 *return_val = GINT_TO_POINTER(ret_val); | |
522 } | |
523 | |
524 void | |
525 gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_UINT(GaimCallback cb, | |
526 va_list args, | |
527 void *data, | |
528 void **return_val) | |
529 { | |
530 gboolean ret_val; | |
531 | |
532 ret_val = ((gboolean (*)(void *, void *, void *, guint, void *))cb)( | |
533 va_arg(args, void *), va_arg(args, void *), | |
534 va_arg(args, void *), va_arg(args, guint), | |
535 data); | |
536 | |
537 if (return_val != NULL) | |
538 *return_val = GINT_TO_POINTER(ret_val); | |
539 } | |
540 | |
541 void | |
542 gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER(GaimCallback cb, | |
543 va_list args, | |
544 void *data, | |
545 void **return_val) | |
546 { | |
547 gboolean ret_val; | |
548 | |
549 ret_val = ((gboolean (*)(void *, void *, void *, void *, void *))cb)( | |
550 va_arg(args, void *), va_arg(args, void *), | |
551 va_arg(args, void *), va_arg(args, void *), | |
552 data); | |
553 | |
554 if (return_val != NULL) | |
555 *return_val = GINT_TO_POINTER(ret_val); | |
556 } | |
557 | |
558 void | |
559 gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER( | |
560 GaimCallback cb, va_list args, void *data, void **return_val) | |
561 { | |
562 gboolean ret_val; | |
563 | |
564 ret_val = | |
565 ((gboolean (*)(void *, void *, void *, void *, void *, void *))cb)( | |
566 va_arg(args, void *), va_arg(args, void *), | |
567 va_arg(args, void *), va_arg(args, void *), | |
568 va_arg(args, void *), data); | |
569 | |
570 if (return_val != NULL) | |
571 *return_val = GINT_TO_POINTER(ret_val); | |
572 } |