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 }