1 #include "xed-message-bus.h"
2
3 #include <string.h>
4 #include <stdarg.h>
5 #include <gobject/gvaluecollector.h>
6
7 /**
8 * XedMessageCallback:
9 * @bus: the #XedMessageBus on which the message was sent
10 * @message: the #XedMessage which was sent
11 * @userdata: the supplied user data when connecting the callback
12 *
13 * Callback signature used for connecting callback functions to be called
14 * when a message is received (see xed_message_bus_connect()).
15 *
16 */
17
18 /**
19 * SECTION:xed-message-bus
20 * @short_description: internal message communication bus
21 * @include: xed/xed-message-bus.h
22 *
23 * xed has a communication bus very similar to DBus. Its primary use is to
24 * allow easy communication between plugins, but it can also be used to expose
25 * xed functionality to external applications by providing DBus bindings for
26 * the internal xed message bus.
27 *
28 * There are two different communication busses available. The default bus
29 * (see xed_message_bus_get_default()) is an application wide communication
30 * bus. In addition, each #XedWindow has a separate, private bus
31 * (see xed_window_get_message_bus()). This makes it easier for plugins to
32 * communicate to other plugins in the same window.
33 *
34 * The concept of the message bus is very simple. You can register a message
35 * type on the bus, specified as a Method at a specific Object Path with a
36 * certain set of Method Arguments. You can then connect callback functions
37 * for this message type on the bus. Whenever a message with the Object Path
38 * and Method for which callbacks are connected is sent over the bus, the
39 * callbacks are called. There is no distinction between Methods and Signals
40 * (signals are simply messages where sender and receiver have switched places).
41 *
42 * <example>
43 * <title>Registering a message type</title>
44 * <programlisting>
45 * XedMessageBus *bus = xed_message_bus_get_default ();
46 *
47 * // Register 'method' at '/plugins/example' with one required
48 * // string argument 'arg1'
49 * XedMessageType *message_type = xed_message_bus_register ("/plugins/example", "method",
50 * 0,
51 * "arg1", G_TYPE_STRING,
52 * NULL);
53 * </programlisting>
54 * </example>
55 * <example>
56 * <title>Connecting a callback</title>
57 * <programlisting>
58 * static void
59 * example_method_cb (XedMessageBus *bus,
60 * XedMessage *message,
61 * gpointer userdata)
62 * {
63 * gchar *arg1 = NULL;
64 *
65 * xed_message_get (message, "arg1", &arg1, NULL);
66 * g_message ("Evoked /plugins/example.method with: %s", arg1);
67 * g_free (arg1);
68 * }
69 *
70 * XedMessageBus *bus = xed_message_bus_get_default ();
71 *
72 * guint id = xed_message_bus_connect (bus,
73 * "/plugins/example", "method",
74 * example_method_cb,
75 * NULL,
76 * NULL);
77 *
78 * </programlisting>
79 * </example>
80 * <example>
81 * <title>Sending a message</title>
82 * <programlisting>
83 * XedMessageBus *bus = xed_message_bus_get_default ();
84 *
85 * xed_message_bus_send (bus,
86 * "/plugins/example", "method",
87 * "arg1", "Hello World",
88 * NULL);
89 * </programlisting>
90 * </example>
91 */
92
93 typedef struct
94 {
95 gchar *object_path;
96 gchar *method;
97
98 GList *listeners;
99 } Message;
100
101 typedef struct
102 {
103 guint id;
104 gboolean blocked;
105
106 GDestroyNotify destroy_data;
107 XedMessageCallback callback;
108 gpointer userdata;
109 } Listener;
110
111 typedef struct
112 {
113 Message *message;
114 GList *listener;
115 } IdMap;
116
117 struct _XedMessageBusPrivate
118 {
119 GHashTable *messages;
120 GHashTable *idmap;
121
122 GList *message_queue;
123 guint idle_id;
124
125 guint next_id;
126
127 GHashTable *types; /* mapping from identifier to XedMessageType */
128 };
129
130 /* signals */
131 enum
132 {
133 DISPATCH,
134 REGISTERED,
135 UNREGISTERED,
136 LAST_SIGNAL
137 };
138
139 static guint message_bus_signals[LAST_SIGNAL] = { 0 };
140
141 static void xed_message_bus_dispatch_real (XedMessageBus *bus,
142 XedMessage *message);
143
G_DEFINE_TYPE_WITH_PRIVATE(XedMessageBus,xed_message_bus,G_TYPE_OBJECT)144 G_DEFINE_TYPE_WITH_PRIVATE (XedMessageBus, xed_message_bus, G_TYPE_OBJECT)
145
146 static void
147 listener_free (Listener *listener)
148 {
149 if (listener->destroy_data)
150 {
151 listener->destroy_data (listener->userdata);
152 }
153
154 g_free (listener);
155 }
156
157 static void
message_free(Message * message)158 message_free (Message *message)
159 {
160 g_free (message->method);
161 g_free (message->object_path);
162
163 g_list_foreach (message->listeners, (GFunc)listener_free, NULL);
164 g_list_free (message->listeners);
165
166 g_free (message);
167 }
168
169 static void
message_queue_free(GList * queue)170 message_queue_free (GList *queue)
171 {
172 g_list_foreach (queue, (GFunc)g_object_unref, NULL);
173 g_list_free (queue);
174 }
175
176 static void
xed_message_bus_finalize(GObject * object)177 xed_message_bus_finalize (GObject *object)
178 {
179 XedMessageBus *bus = XED_MESSAGE_BUS (object);
180
181 if (bus->priv->idle_id != 0)
182 {
183 g_source_remove (bus->priv->idle_id);
184 }
185
186 message_queue_free (bus->priv->message_queue);
187
188 g_hash_table_destroy (bus->priv->messages);
189 g_hash_table_destroy (bus->priv->idmap);
190 g_hash_table_destroy (bus->priv->types);
191
192 G_OBJECT_CLASS (xed_message_bus_parent_class)->finalize (object);
193 }
194
195 static void
xed_message_bus_class_init(XedMessageBusClass * klass)196 xed_message_bus_class_init (XedMessageBusClass *klass)
197 {
198 GObjectClass *object_class = G_OBJECT_CLASS (klass);
199
200 object_class->finalize = xed_message_bus_finalize;
201
202 klass->dispatch = xed_message_bus_dispatch_real;
203
204 /**
205 * XedMessageBus::dispatch:
206 * @bus: a #XedMessageBus
207 * @message: the #XedMessage to dispatch
208 *
209 * The "dispatch" signal is emitted when a message is to be dispatched.
210 * The message is dispatched in the default handler of this signal.
211 * Primary use of this signal is to customize the dispatch of a message
212 * (for instance to automatically dispatch all messages over DBus).
213 *2
214 */
215 message_bus_signals[DISPATCH] =
216 g_signal_new ("dispatch",
217 G_OBJECT_CLASS_TYPE (object_class),
218 G_SIGNAL_RUN_LAST,
219 G_STRUCT_OFFSET (XedMessageBusClass, dispatch),
220 NULL, NULL,
221 g_cclosure_marshal_VOID__OBJECT,
222 G_TYPE_NONE,
223 1,
224 XED_TYPE_MESSAGE);
225
226 /**
227 * XedMessageBus::registered:
228 * @bus: a #XedMessageBus
229 * @message_type: the registered #XedMessageType
230 *
231 * The "registered" signal is emitted when a message has been registered
232 * on the bus.
233 *
234 */
235 message_bus_signals[REGISTERED] =
236 g_signal_new ("registered",
237 G_OBJECT_CLASS_TYPE (object_class),
238 G_SIGNAL_RUN_LAST,
239 G_STRUCT_OFFSET (XedMessageBusClass, registered),
240 NULL, NULL,
241 g_cclosure_marshal_VOID__BOXED,
242 G_TYPE_NONE,
243 1,
244 XED_TYPE_MESSAGE_TYPE);
245
246 /**
247 * XedMessageBus::unregistered:
248 * @bus: a #XedMessageBus
249 * @message_type: the unregistered #XedMessageType
250 *
251 * The "unregistered" signal is emitted when a message has been
252 * unregistered from the bus.
253 *
254 */
255 message_bus_signals[UNREGISTERED] =
256 g_signal_new ("unregistered",
257 G_OBJECT_CLASS_TYPE (object_class),
258 G_SIGNAL_RUN_LAST,
259 G_STRUCT_OFFSET (XedMessageBusClass, unregistered),
260 NULL, NULL,
261 g_cclosure_marshal_VOID__BOXED,
262 G_TYPE_NONE,
263 1,
264 XED_TYPE_MESSAGE_TYPE);
265 }
266
267 static Message *
message_new(XedMessageBus * bus,const gchar * object_path,const gchar * method)268 message_new (XedMessageBus *bus,
269 const gchar *object_path,
270 const gchar *method)
271 {
272 Message *message = g_new (Message, 1);
273
274 message->object_path = g_strdup (object_path);
275 message->method = g_strdup (method);
276 message->listeners = NULL;
277
278 g_hash_table_insert (bus->priv->messages, xed_message_type_identifier (object_path, method), message);
279 return message;
280 }
281
282 static Message *
lookup_message(XedMessageBus * bus,const gchar * object_path,const gchar * method,gboolean create)283 lookup_message (XedMessageBus *bus,
284 const gchar *object_path,
285 const gchar *method,
286 gboolean create)
287 {
288 gchar *identifier;
289 Message *message;
290
291 identifier = xed_message_type_identifier (object_path, method);
292 message = (Message *)g_hash_table_lookup (bus->priv->messages, identifier);
293 g_free (identifier);
294
295 if (!message && !create)
296 {
297 return NULL;
298 }
299
300 if (!message)
301 {
302 message = message_new (bus, object_path, method);
303 }
304
305 return message;
306 }
307
308 static guint
add_listener(XedMessageBus * bus,Message * message,XedMessageCallback callback,gpointer userdata,GDestroyNotify destroy_data)309 add_listener (XedMessageBus *bus,
310 Message *message,
311 XedMessageCallback callback,
312 gpointer userdata,
313 GDestroyNotify destroy_data)
314 {
315 Listener *listener;
316 IdMap *idmap;
317
318 listener = g_new (Listener, 1);
319 listener->id = ++bus->priv->next_id;
320 listener->callback = callback;
321 listener->userdata = userdata;
322 listener->blocked = FALSE;
323 listener->destroy_data = destroy_data;
324
325 message->listeners = g_list_append (message->listeners, listener);
326
327 idmap = g_new (IdMap, 1);
328 idmap->message = message;
329 idmap->listener = g_list_last (message->listeners);
330
331 g_hash_table_insert (bus->priv->idmap, GINT_TO_POINTER (listener->id), idmap);
332 return listener->id;
333 }
334
335 static void
remove_listener(XedMessageBus * bus,Message * message,GList * listener)336 remove_listener (XedMessageBus *bus,
337 Message *message,
338 GList *listener)
339 {
340 Listener *lst;
341
342 lst = (Listener *)listener->data;
343
344 /* remove from idmap */
345 g_hash_table_remove (bus->priv->idmap, GINT_TO_POINTER (lst->id));
346 listener_free (lst);
347
348 /* remove from list of listeners */
349 message->listeners = g_list_delete_link (message->listeners, listener);
350
351 if (!message->listeners)
352 {
353 /* remove message because it does not have any listeners */
354 g_hash_table_remove (bus->priv->messages, message);
355 }
356 }
357
358 static void
block_listener(XedMessageBus * bus,Message * message,GList * listener)359 block_listener (XedMessageBus *bus,
360 Message *message,
361 GList *listener)
362 {
363 Listener *lst;
364
365 lst = (Listener *)listener->data;
366 lst->blocked = TRUE;
367 }
368
369 static void
unblock_listener(XedMessageBus * bus,Message * message,GList * listener)370 unblock_listener (XedMessageBus *bus,
371 Message *message,
372 GList *listener)
373 {
374 Listener *lst;
375
376 lst = (Listener *)listener->data;
377 lst->blocked = FALSE;
378 }
379
380 static void
dispatch_message_real(XedMessageBus * bus,Message * msg,XedMessage * message)381 dispatch_message_real (XedMessageBus *bus,
382 Message *msg,
383 XedMessage *message)
384 {
385 GList *item;
386
387 for (item = msg->listeners; item; item = item->next)
388 {
389 Listener *listener = (Listener *)item->data;
390
391 if (!listener->blocked)
392 {
393 listener->callback (bus, message, listener->userdata);
394 }
395 }
396 }
397
398 static void
xed_message_bus_dispatch_real(XedMessageBus * bus,XedMessage * message)399 xed_message_bus_dispatch_real (XedMessageBus *bus,
400 XedMessage *message)
401 {
402 const gchar *object_path;
403 const gchar *method;
404 Message *msg;
405
406 object_path = xed_message_get_object_path (message);
407 method = xed_message_get_method (message);
408
409 msg = lookup_message (bus, object_path, method, FALSE);
410
411 if (msg)
412 {
413 dispatch_message_real (bus, msg, message);
414 }
415 }
416
417 static void
dispatch_message(XedMessageBus * bus,XedMessage * message)418 dispatch_message (XedMessageBus *bus,
419 XedMessage *message)
420 {
421 g_signal_emit (bus, message_bus_signals[DISPATCH], 0, message);
422 }
423
424 static gboolean
idle_dispatch(XedMessageBus * bus)425 idle_dispatch (XedMessageBus *bus)
426 {
427 GList *list;
428 GList *item;
429
430 /* make sure to set idle_id to 0 first so that any new async messages
431 will be queued properly */
432 bus->priv->idle_id = 0;
433
434 /* reverse queue to get correct delivery order */
435 list = g_list_reverse (bus->priv->message_queue);
436 bus->priv->message_queue = NULL;
437
438 for (item = list; item; item = item->next)
439 {
440 XedMessage *msg = XED_MESSAGE (item->data);
441
442 dispatch_message (bus, msg);
443 }
444
445 message_queue_free (list);
446 return FALSE;
447 }
448
449 typedef void (*MatchCallback) (XedMessageBus *, Message *, GList *);
450
451 static void
process_by_id(XedMessageBus * bus,guint id,MatchCallback processor)452 process_by_id (XedMessageBus *bus,
453 guint id,
454 MatchCallback processor)
455 {
456 IdMap *idmap;
457
458 idmap = (IdMap *)g_hash_table_lookup (bus->priv->idmap, GINT_TO_POINTER (id));
459
460 if (idmap == NULL)
461 {
462 g_warning ("No handler registered with id `%d'", id);
463 return;
464 }
465
466 processor (bus, idmap->message, idmap->listener);
467 }
468
469 static void
process_by_match(XedMessageBus * bus,const gchar * object_path,const gchar * method,XedMessageCallback callback,gpointer userdata,MatchCallback processor)470 process_by_match (XedMessageBus *bus,
471 const gchar *object_path,
472 const gchar *method,
473 XedMessageCallback callback,
474 gpointer userdata,
475 MatchCallback processor)
476 {
477 Message *message;
478 GList *item;
479
480 message = lookup_message (bus, object_path, method, FALSE);
481
482 if (!message)
483 {
484 g_warning ("No such handler registered for %s.%s", object_path, method);
485 return;
486 }
487
488 for (item = message->listeners; item; item = item->next)
489 {
490 Listener *listener = (Listener *)item->data;
491
492 if (listener->callback == callback && listener->userdata == userdata)
493 {
494 processor (bus, message, item);
495 return;
496 }
497 }
498
499 g_warning ("No such handler registered for %s.%s", object_path, method);
500 }
501
502 static void
xed_message_bus_init(XedMessageBus * self)503 xed_message_bus_init (XedMessageBus *self)
504 {
505 self->priv = xed_message_bus_get_instance_private (self);
506
507 self->priv->messages = g_hash_table_new_full (g_str_hash,
508 g_str_equal,
509 (GDestroyNotify)g_free,
510 (GDestroyNotify)message_free);
511
512 self->priv->idmap = g_hash_table_new_full (g_direct_hash,
513 g_direct_equal,
514 NULL,
515 (GDestroyNotify)g_free);
516
517 self->priv->types = g_hash_table_new_full (g_str_hash,
518 g_str_equal,
519 (GDestroyNotify)g_free,
520 (GDestroyNotify)xed_message_type_unref);
521 }
522
523 /**
524 * xed_message_bus_get_default:
525 *
526 * Get the default application #XedMessageBus.
527 *
528 * Return value: (transfer none): the default #XedMessageBus
529 *
530 */
531 XedMessageBus *
xed_message_bus_get_default(void)532 xed_message_bus_get_default (void)
533 {
534 static XedMessageBus *default_bus = NULL;
535
536 if (G_UNLIKELY (default_bus == NULL))
537 {
538 default_bus = g_object_new (XED_TYPE_MESSAGE_BUS, NULL);
539 g_object_add_weak_pointer (G_OBJECT (default_bus), (gpointer) &default_bus);
540 }
541
542 return default_bus;
543 }
544
545 /**
546 * xed_message_bus_new:
547 *
548 * Create a new message bus. Use xed_message_bus_get_default() to get the
549 * default, application wide, message bus. Creating a new bus is useful for
550 * associating a specific bus with for instance a #XedWindow.
551 *
552 * Return value: a new #XedMessageBus
553 *
554 */
555 XedMessageBus *
xed_message_bus_new(void)556 xed_message_bus_new (void)
557 {
558 return XED_MESSAGE_BUS (g_object_new (XED_TYPE_MESSAGE_BUS, NULL));
559 }
560
561 /**
562 * xed_message_bus_lookup:
563 * @bus: a #XedMessageBus
564 * @object_path: the object path
565 * @method: the method
566 *
567 * Get the registered #XedMessageType for @method at @object_path. The
568 * returned #XedMessageType is owned by the bus and should not be unreffed.
569 *
570 * Return value: the registered #XedMessageType or %NULL if no message type
571 * is registered for @method at @object_path
572 *
573 */
574 XedMessageType *
xed_message_bus_lookup(XedMessageBus * bus,const gchar * object_path,const gchar * method)575 xed_message_bus_lookup (XedMessageBus *bus,
576 const gchar *object_path,
577 const gchar *method)
578 {
579 gchar *identifier;
580 XedMessageType *message_type;
581
582 g_return_val_if_fail (XED_IS_MESSAGE_BUS (bus), NULL);
583 g_return_val_if_fail (object_path != NULL, NULL);
584 g_return_val_if_fail (method != NULL, NULL);
585
586 identifier = xed_message_type_identifier (object_path, method);
587 message_type = XED_MESSAGE_TYPE (g_hash_table_lookup (bus->priv->types, identifier));
588
589 g_free (identifier);
590 return message_type;
591 }
592
593 /**
594 * xed_message_bus_register:
595 * @bus: a #XedMessageBus
596 * @object_path: the object path
597 * @method: the method to register
598 * @num_optional: the number of optional arguments
599 * @...: NULL terminated list of key/gtype method argument pairs
600 *
601 * Register a message on the bus. A message must be registered on the bus before
602 * it can be send. This function registers the type arguments for @method at
603 * @object_path. The arguments are specified with the variable arguments which
604 * should contain pairs of const gchar *key and GType terminated by %NULL. The
605 * last @num_optional arguments are registered as optional (and are thus not
606 * required when sending a message).
607 *
608 * This function emits a #XedMessageBus::registered signal.
609 *
610 * Return value: the registered #XedMessageType. The returned reference is
611 * owned by the bus. If you want to keep it alive after
612 * unregistering, use xed_message_type_ref().
613 *
614 */
615 XedMessageType *
xed_message_bus_register(XedMessageBus * bus,const gchar * object_path,const gchar * method,guint num_optional,...)616 xed_message_bus_register (XedMessageBus *bus,
617 const gchar *object_path,
618 const gchar *method,
619 guint num_optional,
620 ...)
621 {
622 gchar *identifier;
623 va_list var_args;
624 XedMessageType *message_type;
625
626 g_return_val_if_fail (XED_IS_MESSAGE_BUS (bus), NULL);
627 g_return_val_if_fail (xed_message_type_is_valid_object_path (object_path), NULL);
628
629 if (xed_message_bus_is_registered (bus, object_path, method))
630 {
631 g_warning ("Message type for '%s.%s' is already registered", object_path, method);
632 return NULL;
633 }
634
635 identifier = xed_message_type_identifier (object_path, method);
636
637 va_start (var_args, num_optional);
638 message_type = xed_message_type_new_valist (object_path, method, num_optional, var_args);
639 va_end (var_args);
640
641 if (message_type)
642 {
643 g_hash_table_insert (bus->priv->types, identifier, message_type);
644 g_signal_emit (bus, message_bus_signals[REGISTERED], 0, message_type);
645 }
646 else
647 {
648 g_free (identifier);
649 }
650
651 return message_type;
652 }
653
654 static void
xed_message_bus_unregister_real(XedMessageBus * bus,XedMessageType * message_type,gboolean remove_from_store)655 xed_message_bus_unregister_real (XedMessageBus *bus,
656 XedMessageType *message_type,
657 gboolean remove_from_store)
658 {
659 gchar *identifier;
660
661 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
662
663 identifier = xed_message_type_identifier (xed_message_type_get_object_path (message_type),
664 xed_message_type_get_method (message_type));
665
666 /* Keep message type alive for signal emission */
667 xed_message_type_ref (message_type);
668
669 if (!remove_from_store || g_hash_table_remove (bus->priv->types, identifier))
670 {
671 g_signal_emit (bus, message_bus_signals[UNREGISTERED], 0, message_type);
672 }
673
674 xed_message_type_unref (message_type);
675 g_free (identifier);
676 }
677
678 /**
679 * xed_message_bus_unregister:
680 * @bus: a #XedMessageBus
681 * @message_type: the #XedMessageType to unregister
682 *
683 * Unregisters a previously registered message type. This is especially useful
684 * for plugins which should unregister message types when they are deactivated.
685 *
686 * This function emits the #XedMessageBus::unregistered signal.
687 *
688 */
689 void
xed_message_bus_unregister(XedMessageBus * bus,XedMessageType * message_type)690 xed_message_bus_unregister (XedMessageBus *bus,
691 XedMessageType *message_type)
692 {
693 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
694 xed_message_bus_unregister_real (bus, message_type, TRUE);
695 }
696
697 typedef struct
698 {
699 XedMessageBus *bus;
700 const gchar *object_path;
701 } UnregisterInfo;
702
703 static gboolean
unregister_each(const gchar * identifier,XedMessageType * message_type,UnregisterInfo * info)704 unregister_each (const gchar *identifier,
705 XedMessageType *message_type,
706 UnregisterInfo *info)
707 {
708 if (strcmp (xed_message_type_get_object_path (message_type),
709 info->object_path) == 0)
710 {
711 xed_message_bus_unregister_real (info->bus, message_type, FALSE);
712 return TRUE;
713 }
714
715 return FALSE;
716 }
717
718 /**
719 * xed_message_bus_unregister_all:
720 * @bus: a #XedMessageBus
721 * @object_path: the object path
722 *
723 * Unregisters all message types for @object_path. This is especially useful for
724 * plugins which should unregister message types when they are deactivated.
725 *
726 * This function emits the #XedMessageBus::unregistered signal for all
727 * unregistered message types.
728 *
729 */
730 void
xed_message_bus_unregister_all(XedMessageBus * bus,const gchar * object_path)731 xed_message_bus_unregister_all (XedMessageBus *bus,
732 const gchar *object_path)
733 {
734 UnregisterInfo info = {bus, object_path};
735
736 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
737 g_return_if_fail (object_path != NULL);
738
739 g_hash_table_foreach_remove (bus->priv->types, (GHRFunc)unregister_each, &info);
740 }
741
742 /**
743 * xed_message_bus_is_registered:
744 * @bus: a #XedMessageBus
745 * @object_path: the object path
746 * @method: the method
747 *
748 * Check whether a message type @method at @object_path is registered on the
749 * bus.
750 *
751 * Return value: %TRUE if the @method at @object_path is a registered message
752 * type on the bus
753 *
754 */
755 gboolean
xed_message_bus_is_registered(XedMessageBus * bus,const gchar * object_path,const gchar * method)756 xed_message_bus_is_registered (XedMessageBus *bus,
757 const gchar *object_path,
758 const gchar *method)
759 {
760 gchar *identifier;
761 gboolean ret;
762
763 g_return_val_if_fail (XED_IS_MESSAGE_BUS (bus), FALSE);
764 g_return_val_if_fail (object_path != NULL, FALSE);
765 g_return_val_if_fail (method != NULL, FALSE);
766
767 identifier = xed_message_type_identifier (object_path, method);
768 ret = g_hash_table_lookup (bus->priv->types, identifier) != NULL;
769
770 g_free(identifier);
771 return ret;
772 }
773
774 typedef struct
775 {
776 XedMessageBusForeach func;
777 gpointer userdata;
778 } ForeachInfo;
779
780 static void
foreach_type(const gchar * key,XedMessageType * message_type,ForeachInfo * info)781 foreach_type (const gchar *key,
782 XedMessageType *message_type,
783 ForeachInfo *info)
784 {
785 xed_message_type_ref (message_type);
786 info->func (message_type, info->userdata);
787 xed_message_type_unref (message_type);
788 }
789
790 /**
791 * xed_message_bus_foreach:
792 * @bus: the #XedMessageBus
793 * @func: (scope call): the callback function
794 * @userdata: the user data to supply to the callback function
795 *
796 * Calls @func for each message type registered on the bus
797 *
798 */
799 void
xed_message_bus_foreach(XedMessageBus * bus,XedMessageBusForeach func,gpointer userdata)800 xed_message_bus_foreach (XedMessageBus *bus,
801 XedMessageBusForeach func,
802 gpointer userdata)
803 {
804 ForeachInfo info = {func, userdata};
805
806 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
807 g_return_if_fail (func != NULL);
808
809 g_hash_table_foreach (bus->priv->types, (GHFunc)foreach_type, &info);
810 }
811
812 /**
813 * xed_message_bus_connect:
814 * @bus: a #XedMessageBus
815 * @object_path: the object path
816 * @method: the method
817 * @callback: function to be called when message @method at @object_path is sent
818 * @userdata: userdata to use for the callback
819 * @destroy_data: function to evoke with @userdata as argument when @userdata
820 * needs to be freed
821 *
822 * Connect a callback handler to be evoked when message @method at @object_path
823 * is sent over the bus.
824 *
825 * Return value: the callback identifier
826 *
827 */
828 guint
xed_message_bus_connect(XedMessageBus * bus,const gchar * object_path,const gchar * method,XedMessageCallback callback,gpointer userdata,GDestroyNotify destroy_data)829 xed_message_bus_connect (XedMessageBus *bus,
830 const gchar *object_path,
831 const gchar *method,
832 XedMessageCallback callback,
833 gpointer userdata,
834 GDestroyNotify destroy_data)
835 {
836 Message *message;
837
838 g_return_val_if_fail (XED_IS_MESSAGE_BUS (bus), 0);
839 g_return_val_if_fail (object_path != NULL, 0);
840 g_return_val_if_fail (method != NULL, 0);
841 g_return_val_if_fail (callback != NULL, 0);
842
843 /* lookup the message and create if it does not exist yet */
844 message = lookup_message (bus, object_path, method, TRUE);
845
846 return add_listener (bus, message, callback, userdata, destroy_data);
847 }
848
849 /**
850 * xed_message_bus_disconnect:
851 * @bus: a #XedMessageBus
852 * @id: the callback id as returned by xed_message_bus_connect()
853 *
854 * Disconnects a previously connected message callback.
855 *
856 */
857 void
xed_message_bus_disconnect(XedMessageBus * bus,guint id)858 xed_message_bus_disconnect (XedMessageBus *bus,
859 guint id)
860 {
861 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
862
863 process_by_id (bus, id, remove_listener);
864 }
865
866 /**
867 * xed_message_bus_disconnect_by_func:
868 * @bus: a #XedMessageBus
869 * @object_path: the object path
870 * @method: the method
871 * @callback: (scope call): the connected callback
872 * @userdata: the userdata with which the callback was connected
873 *
874 * Disconnects a previously connected message callback by matching the
875 * provided callback function and userdata. See also
876 * xed_message_bus_disconnect().
877 *
878 */
879 void
xed_message_bus_disconnect_by_func(XedMessageBus * bus,const gchar * object_path,const gchar * method,XedMessageCallback callback,gpointer userdata)880 xed_message_bus_disconnect_by_func (XedMessageBus *bus,
881 const gchar *object_path,
882 const gchar *method,
883 XedMessageCallback callback,
884 gpointer userdata)
885 {
886 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
887
888 process_by_match (bus, object_path, method, callback, userdata, remove_listener);
889 }
890
891 /**
892 * xed_message_bus_block:
893 * @bus: a #XedMessageBus
894 * @id: the callback id
895 *
896 * Blocks evoking the callback specified by @id. Unblock the callback by
897 * using xed_message_bus_unblock().
898 *
899 */
900 void
xed_message_bus_block(XedMessageBus * bus,guint id)901 xed_message_bus_block (XedMessageBus *bus,
902 guint id)
903 {
904 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
905
906 process_by_id (bus, id, block_listener);
907 }
908
909 /**
910 * xed_message_bus_block_by_func:
911 * @bus: a #XedMessageBus
912 * @object_path: the object path
913 * @method: the method
914 * @callback: (scope call): the callback to block
915 * @userdata: the userdata with which the callback was connected
916 *
917 * Blocks evoking the callback that matches provided @callback and @userdata.
918 * Unblock the callback using xed_message_bus_unblock_by_func().
919 *
920 */
921 void
xed_message_bus_block_by_func(XedMessageBus * bus,const gchar * object_path,const gchar * method,XedMessageCallback callback,gpointer userdata)922 xed_message_bus_block_by_func (XedMessageBus *bus,
923 const gchar *object_path,
924 const gchar *method,
925 XedMessageCallback callback,
926 gpointer userdata)
927 {
928 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
929
930 process_by_match (bus, object_path, method, callback, userdata, block_listener);
931 }
932
933 /**
934 * xed_message_bus_unblock:
935 * @bus: a #XedMessageBus
936 * @id: the callback id
937 *
938 * Unblocks the callback specified by @id.
939 *
940 */
941 void
xed_message_bus_unblock(XedMessageBus * bus,guint id)942 xed_message_bus_unblock (XedMessageBus *bus,
943 guint id)
944 {
945 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
946
947 process_by_id (bus, id, unblock_listener);
948 }
949
950 /**
951 * xed_message_bus_unblock_by_func:
952 * @bus: a #XedMessageBus
953 * @object_path: the object path
954 * @method: the method
955 * @callback: (scope call): the callback to block
956 * @userdata: the userdata with which the callback was connected
957 *
958 * Unblocks the callback that matches provided @callback and @userdata.
959 *
960 */
961 void
xed_message_bus_unblock_by_func(XedMessageBus * bus,const gchar * object_path,const gchar * method,XedMessageCallback callback,gpointer userdata)962 xed_message_bus_unblock_by_func (XedMessageBus *bus,
963 const gchar *object_path,
964 const gchar *method,
965 XedMessageCallback callback,
966 gpointer userdata)
967 {
968 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
969
970 process_by_match (bus, object_path, method, callback, userdata, unblock_listener);
971 }
972
973 static gboolean
validate_message(XedMessage * message)974 validate_message (XedMessage *message)
975 {
976 if (!xed_message_validate (message))
977 {
978 g_warning ("Message '%s.%s' is invalid", xed_message_get_object_path (message),
979 xed_message_get_method (message));
980 return FALSE;
981 }
982
983 return TRUE;
984 }
985
986 static void
send_message_real(XedMessageBus * bus,XedMessage * message)987 send_message_real (XedMessageBus *bus,
988 XedMessage *message)
989 {
990 if (!validate_message (message))
991 {
992 return;
993 }
994
995 bus->priv->message_queue = g_list_prepend (bus->priv->message_queue, g_object_ref (message));
996
997 if (bus->priv->idle_id == 0)
998 {
999 bus->priv->idle_id = g_idle_add_full (G_PRIORITY_HIGH,
1000 (GSourceFunc)idle_dispatch,
1001 bus,
1002 NULL);
1003 }
1004 }
1005
1006 /**
1007 * xed_message_bus_send_message:
1008 * @bus: a #XedMessageBus
1009 * @message: the message to send
1010 *
1011 * This sends the provided @message asynchronously over the bus. To send
1012 * a message synchronously, use xed_message_bus_send_message_sync(). The
1013 * convenience function xed_message_bus_send() can be used to easily send
1014 * a message without constructing the message object explicitly first.
1015 *
1016 */
1017 void
xed_message_bus_send_message(XedMessageBus * bus,XedMessage * message)1018 xed_message_bus_send_message (XedMessageBus *bus,
1019 XedMessage *message)
1020 {
1021 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
1022 g_return_if_fail (XED_IS_MESSAGE (message));
1023
1024 send_message_real (bus, message);
1025 }
1026
1027 static void
send_message_sync_real(XedMessageBus * bus,XedMessage * message)1028 send_message_sync_real (XedMessageBus *bus,
1029 XedMessage *message)
1030 {
1031 if (!validate_message (message))
1032 {
1033 return;
1034 }
1035
1036 dispatch_message (bus, message);
1037 }
1038
1039 /**
1040 * xed_message_bus_send_message_sync:
1041 * @bus: a #XedMessageBus
1042 * @message: the message to send
1043 *
1044 * This sends the provided @message synchronously over the bus. To send
1045 * a message asynchronously, use xed_message_bus_send_message(). The
1046 * convenience function xed_message_bus_send_sync() can be used to easily send
1047 * a message without constructing the message object explicitly first.
1048 *
1049 */
1050 void
xed_message_bus_send_message_sync(XedMessageBus * bus,XedMessage * message)1051 xed_message_bus_send_message_sync (XedMessageBus *bus,
1052 XedMessage *message)
1053 {
1054 g_return_if_fail (XED_IS_MESSAGE_BUS (bus));
1055 g_return_if_fail (XED_IS_MESSAGE (message));
1056
1057 send_message_sync_real (bus, message);
1058 }
1059
1060 static XedMessage *
create_message(XedMessageBus * bus,const gchar * object_path,const gchar * method,va_list var_args)1061 create_message (XedMessageBus *bus,
1062 const gchar *object_path,
1063 const gchar *method,
1064 va_list var_args)
1065 {
1066 XedMessageType *message_type;
1067
1068 message_type = xed_message_bus_lookup (bus, object_path, method);
1069
1070 if (!message_type)
1071 {
1072 g_warning ("Could not find message type for '%s.%s'", object_path, method);
1073 return NULL;
1074 }
1075
1076 return xed_message_type_instantiate_valist (message_type, var_args);
1077 }
1078
1079 /**
1080 * xed_message_bus_send:
1081 * @bus: a #XedMessageBus
1082 * @object_path: the object path
1083 * @method: the method
1084 * @...: NULL terminated list of key/value pairs
1085 *
1086 * This provides a convenient way to quickly send a message @method at
1087 * @object_path asynchronously over the bus. The variable argument list
1088 * specifies key (string) value pairs used to construct the message arguments.
1089 * To send a message synchronously use xed_message_bus_send_sync().
1090 *
1091 */
1092 void
xed_message_bus_send(XedMessageBus * bus,const gchar * object_path,const gchar * method,...)1093 xed_message_bus_send (XedMessageBus *bus,
1094 const gchar *object_path,
1095 const gchar *method,
1096 ...)
1097 {
1098 va_list var_args;
1099 XedMessage *message;
1100
1101 va_start (var_args, method);
1102
1103 message = create_message (bus, object_path, method, var_args);
1104
1105 if (message)
1106 {
1107 send_message_real (bus, message);
1108 g_object_unref (message);
1109 }
1110 else
1111 {
1112 g_warning ("Could not instantiate message");
1113 }
1114
1115 va_end (var_args);
1116 }
1117
1118 /**
1119 * xed_message_bus_send_sync:
1120 * @bus: a #XedMessageBus
1121 * @object_path: the object path
1122 * @method: the method
1123 * @...: NULL terminated list of key/value pairs
1124 *
1125 * This provides a convenient way to quickly send a message @method at
1126 * @object_path synchronously over the bus. The variable argument list
1127 * specifies key (string) value pairs used to construct the message
1128 * arguments. To send a message asynchronously use xed_message_bus_send().
1129 *
1130 * Return value: (transfer full): the constructed #XedMessage. The caller owns a reference
1131 * to the #XedMessage and should call g_object_unref() when
1132 * it is no longer needed
1133 */
1134 XedMessage *
xed_message_bus_send_sync(XedMessageBus * bus,const gchar * object_path,const gchar * method,...)1135 xed_message_bus_send_sync (XedMessageBus *bus,
1136 const gchar *object_path,
1137 const gchar *method,
1138 ...)
1139 {
1140 va_list var_args;
1141 XedMessage *message;
1142
1143 va_start (var_args, method);
1144 message = create_message (bus, object_path, method, var_args);
1145
1146 if (message)
1147 send_message_sync_real (bus, message);
1148
1149 va_end (var_args);
1150
1151 return message;
1152 }
1153
1154 // ex:ts=8:noet:
1155