#include "pluma-message-bus.h" #include #include #include /** * PlumaMessageCallback: * @bus: the #PlumaMessageBus on which the message was sent * @message: the #PlumaMessage which was sent * @userdata: the supplied user data when connecting the callback * * Callback signature used for connecting callback functions to be called * when a message is received (see pluma_message_bus_connect()). * */ /** * SECTION:pluma-message-bus * @short_description: internal message communication bus * @include: pluma/pluma-message-bus.h * * pluma has a communication bus very similar to DBus. Its primary use is to * allow easy communication between plugins, but it can also be used to expose * pluma functionality to external applications by providing DBus bindings for * the internal pluma message bus. * * There are two different communication busses available. The default bus * (see pluma_message_bus_get_default()) is an application wide communication * bus. In addition, each #PlumaWindow has a separate, private bus * (see pluma_window_get_message_bus()). This makes it easier for plugins to * communicate to other plugins in the same window. * * The concept of the message bus is very simple. You can register a message * type on the bus, specified as a Method at a specific Object Path with a * certain set of Method Arguments. You can then connect callback functions * for this message type on the bus. Whenever a message with the Object Path * and Method for which callbacks are connected is sent over the bus, the * callbacks are called. There is no distinction between Methods and Signals * (signals are simply messages where sender and receiver have switched places). * * * Registering a message type * * PlumaMessageBus *bus = pluma_message_bus_get_default (); * * // Register 'method' at '/plugins/example' with one required * // string argument 'arg1' * PlumaMessageType *message_type = pluma_message_bus_register ("/plugins/example", "method", * 0, * "arg1", G_TYPE_STRING, * NULL); * * * * Connecting a callback * * static void * example_method_cb (PlumaMessageBus *bus, * PlumaMessage *message, * gpointer userdata) * { * gchar *arg1 = NULL; * * pluma_message_get (message, "arg1", &arg1, NULL); * g_message ("Evoked /plugins/example.method with: %s", arg1); * g_free (arg1); * } * * PlumaMessageBus *bus = pluma_message_bus_get_default (); * * guint id = pluma_message_bus_connect (bus, * "/plugins/example", "method", * example_method_cb, * NULL, * NULL); * * * * * Sending a message * * PlumaMessageBus *bus = pluma_message_bus_get_default (); * * pluma_message_bus_send (bus, * "/plugins/example", "method", * "arg1", "Hello World", * NULL); * * */ typedef struct { gchar *object_path; gchar *method; GList *listeners; } Message; typedef struct { guint id; gboolean blocked; GDestroyNotify destroy_data; PlumaMessageCallback callback; gpointer userdata; } Listener; typedef struct { Message *message; GList *listener; } IdMap; struct _PlumaMessageBusPrivate { GHashTable *messages; GHashTable *idmap; GList *message_queue; guint idle_id; guint next_id; GHashTable *types; /* mapping from identifier to PlumaMessageType */ }; /* signals */ enum { DISPATCH, REGISTERED, UNREGISTERED, LAST_SIGNAL }; static guint message_bus_signals[LAST_SIGNAL] = { 0 }; static void pluma_message_bus_dispatch_real (PlumaMessageBus *bus, PlumaMessage *message); G_DEFINE_TYPE_WITH_PRIVATE (PlumaMessageBus, pluma_message_bus, G_TYPE_OBJECT) static void listener_free (Listener *listener) { if (listener->destroy_data) listener->destroy_data (listener->userdata); g_free (listener); } static void message_free (Message *message) { g_free (message->method); g_free (message->object_path); g_list_free_full (message->listeners, (GDestroyNotify) listener_free); g_free (message); } static void pluma_message_bus_finalize (GObject *object) { PlumaMessageBus *bus = PLUMA_MESSAGE_BUS (object); if (bus->priv->idle_id != 0) g_source_remove (bus->priv->idle_id); g_list_free_full (bus->priv->message_queue, g_object_unref); g_hash_table_destroy (bus->priv->messages); g_hash_table_destroy (bus->priv->idmap); g_hash_table_destroy (bus->priv->types); G_OBJECT_CLASS (pluma_message_bus_parent_class)->finalize (object); } static void pluma_message_bus_class_init (PlumaMessageBusClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = pluma_message_bus_finalize; klass->dispatch = pluma_message_bus_dispatch_real; /** * PlumaMessageBus::dispatch: * @bus: a #PlumaMessageBus * @message: the #PlumaMessage to dispatch * * The "dispatch" signal is emitted when a message is to be dispatched. * The message is dispatched in the default handler of this signal. * Primary use of this signal is to customize the dispatch of a message * (for instance to automatically dispatch all messages over DBus). *2 */ message_bus_signals[DISPATCH] = g_signal_new ("dispatch", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (PlumaMessageBusClass, dispatch), NULL, NULL, NULL, G_TYPE_NONE, 1, PLUMA_TYPE_MESSAGE); /** * PlumaMessageBus::registered: * @bus: a #PlumaMessageBus * @message_type: the registered #PlumaMessageType * * The "registered" signal is emitted when a message has been registered * on the bus. * */ message_bus_signals[REGISTERED] = g_signal_new ("registered", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (PlumaMessageBusClass, registered), NULL, NULL, NULL, G_TYPE_NONE, 1, PLUMA_TYPE_MESSAGE_TYPE); /** * PlumaMessageBus::unregistered: * @bus: a #PlumaMessageBus * @message_type: the unregistered #PlumaMessageType * * The "unregistered" signal is emitted when a message has been * unregistered from the bus. * */ message_bus_signals[UNREGISTERED] = g_signal_new ("unregistered", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (PlumaMessageBusClass, unregistered), NULL, NULL, NULL, G_TYPE_NONE, 1, PLUMA_TYPE_MESSAGE_TYPE); } static Message * message_new (PlumaMessageBus *bus, const gchar *object_path, const gchar *method) { Message *message = g_new (Message, 1); message->object_path = g_strdup (object_path); message->method = g_strdup (method); message->listeners = NULL; g_hash_table_insert (bus->priv->messages, pluma_message_type_identifier (object_path, method), message); return message; } static Message * lookup_message (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, gboolean create) { gchar *identifier; Message *message; identifier = pluma_message_type_identifier (object_path, method); message = (Message *)g_hash_table_lookup (bus->priv->messages, identifier); g_free (identifier); if (!message && !create) return NULL; if (!message) message = message_new (bus, object_path, method); return message; } static guint add_listener (PlumaMessageBus *bus, Message *message, PlumaMessageCallback callback, gpointer userdata, GDestroyNotify destroy_data) { Listener *listener; IdMap *idmap; listener = g_new (Listener, 1); listener->id = ++bus->priv->next_id; listener->callback = callback; listener->userdata = userdata; listener->blocked = FALSE; listener->destroy_data = destroy_data; message->listeners = g_list_append (message->listeners, listener); idmap = g_new (IdMap, 1); idmap->message = message; idmap->listener = g_list_last (message->listeners); g_hash_table_insert (bus->priv->idmap, GINT_TO_POINTER (listener->id), idmap); return listener->id; } static void remove_listener (PlumaMessageBus *bus, Message *message, GList *listener) { Listener *lst; lst = (Listener *)listener->data; /* remove from idmap */ g_hash_table_remove (bus->priv->idmap, GINT_TO_POINTER (lst->id)); listener_free (lst); /* remove from list of listeners */ message->listeners = g_list_delete_link (message->listeners, listener); if (!message->listeners) { /* remove message because it does not have any listeners */ g_hash_table_remove (bus->priv->messages, message); } } static void block_listener (PlumaMessageBus *bus, Message *message, GList *listener) { Listener *lst; lst = (Listener *)listener->data; lst->blocked = TRUE; } static void unblock_listener (PlumaMessageBus *bus, Message *message, GList *listener) { Listener *lst; lst = (Listener *)listener->data; lst->blocked = FALSE; } static void dispatch_message_real (PlumaMessageBus *bus, Message *msg, PlumaMessage *message) { GList *item; for (item = msg->listeners; item; item = item->next) { Listener *listener = (Listener *)item->data; if (!listener->blocked) listener->callback (bus, message, listener->userdata); } } static void pluma_message_bus_dispatch_real (PlumaMessageBus *bus, PlumaMessage *message) { const gchar *object_path; const gchar *method; Message *msg; object_path = pluma_message_get_object_path (message); method = pluma_message_get_method (message); msg = lookup_message (bus, object_path, method, FALSE); if (msg) dispatch_message_real (bus, msg, message); } static void dispatch_message (PlumaMessageBus *bus, PlumaMessage *message) { g_signal_emit (bus, message_bus_signals[DISPATCH], 0, message); } static gboolean idle_dispatch (PlumaMessageBus *bus) { GList *list; GList *item; /* make sure to set idle_id to 0 first so that any new async messages will be queued properly */ bus->priv->idle_id = 0; /* reverse queue to get correct delivery order */ list = g_list_reverse (bus->priv->message_queue); bus->priv->message_queue = NULL; for (item = list; item; item = item->next) { PlumaMessage *msg = PLUMA_MESSAGE (item->data); dispatch_message (bus, msg); } g_list_free_full (list, g_object_unref); return FALSE; } typedef void (*MatchCallback) (PlumaMessageBus *, Message *, GList *); static void process_by_id (PlumaMessageBus *bus, guint id, MatchCallback processor) { IdMap *idmap; idmap = (IdMap *)g_hash_table_lookup (bus->priv->idmap, GINT_TO_POINTER (id)); if (idmap == NULL) { g_warning ("No handler registered with id `%d'", id); return; } processor (bus, idmap->message, idmap->listener); } static void process_by_match (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, PlumaMessageCallback callback, gpointer userdata, MatchCallback processor) { Message *message; GList *item; message = lookup_message (bus, object_path, method, FALSE); if (!message) { g_warning ("No such handler registered for %s.%s", object_path, method); return; } for (item = message->listeners; item; item = item->next) { Listener *listener = (Listener *)item->data; if (listener->callback == callback && listener->userdata == userdata) { processor (bus, message, item); return; } } g_warning ("No such handler registered for %s.%s", object_path, method); } static void pluma_message_bus_init (PlumaMessageBus *self) { self->priv = pluma_message_bus_get_instance_private (self); self->priv->messages = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)message_free); self->priv->idmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)g_free); self->priv->types = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)pluma_message_type_unref); } /** * pluma_message_bus_get_default: * * Get the default application #PlumaMessageBus. * * Return value: (transfer none): the default #PlumaMessageBus * */ PlumaMessageBus * pluma_message_bus_get_default (void) { static PlumaMessageBus *default_bus = NULL; if (G_UNLIKELY (default_bus == NULL)) { default_bus = g_object_new (PLUMA_TYPE_MESSAGE_BUS, NULL); g_object_add_weak_pointer (G_OBJECT (default_bus), (gpointer) &default_bus); } return default_bus; } /** * pluma_message_bus_new: * * Create a new message bus. Use pluma_message_bus_get_default() to get the * default, application wide, message bus. Creating a new bus is useful for * associating a specific bus with for instance a #PlumaWindow. * * Return value: a new #PlumaMessageBus * */ PlumaMessageBus * pluma_message_bus_new (void) { return PLUMA_MESSAGE_BUS (g_object_new (PLUMA_TYPE_MESSAGE_BUS, NULL)); } /** * pluma_message_bus_lookup: * @bus: a #PlumaMessageBus * @object_path: the object path * @method: the method * * Get the registered #PlumaMessageType for @method at @object_path. The * returned #PlumaMessageType is owned by the bus and should not be unreffed. * * Return value: the registered #PlumaMessageType or %NULL if no message type * is registered for @method at @object_path * */ PlumaMessageType * pluma_message_bus_lookup (PlumaMessageBus *bus, const gchar *object_path, const gchar *method) { gchar *identifier; PlumaMessageType *message_type; g_return_val_if_fail (PLUMA_IS_MESSAGE_BUS (bus), NULL); g_return_val_if_fail (object_path != NULL, NULL); g_return_val_if_fail (method != NULL, NULL); identifier = pluma_message_type_identifier (object_path, method); message_type = PLUMA_MESSAGE_TYPE (g_hash_table_lookup (bus->priv->types, identifier)); g_free (identifier); return message_type; } /** * pluma_message_bus_register: * @bus: a #PlumaMessageBus * @object_path: the object path * @method: the method to register * @num_optional: the number of optional arguments * @...: NULL terminated list of key/gtype method argument pairs * * Register a message on the bus. A message must be registered on the bus before * it can be send. This function registers the type arguments for @method at * @object_path. The arguments are specified with the variable arguments which * should contain pairs of const gchar *key and GType terminated by %NULL. The * last @num_optional arguments are registered as optional (and are thus not * required when sending a message). * * This function emits a #PlumaMessageBus::registered signal. * * Return value: the registered #PlumaMessageType. The returned reference is * owned by the bus. If you want to keep it alive after * unregistering, use pluma_message_type_ref(). * */ PlumaMessageType * pluma_message_bus_register (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, guint num_optional, ...) { gchar *identifier; va_list var_args; PlumaMessageType *message_type; g_return_val_if_fail (PLUMA_IS_MESSAGE_BUS (bus), NULL); g_return_val_if_fail (pluma_message_type_is_valid_object_path (object_path), NULL); if (pluma_message_bus_is_registered (bus, object_path, method)) { g_warning ("Message type for '%s.%s' is already registered", object_path, method); return NULL; } identifier = pluma_message_type_identifier (object_path, method); g_hash_table_lookup (bus->priv->types, identifier); va_start (var_args, num_optional); message_type = pluma_message_type_new_valist (object_path, method, num_optional, var_args); va_end (var_args); if (message_type) { g_hash_table_insert (bus->priv->types, identifier, message_type); g_signal_emit (bus, message_bus_signals[REGISTERED], 0, message_type); } else { g_free (identifier); } return message_type; } static void pluma_message_bus_unregister_real (PlumaMessageBus *bus, PlumaMessageType *message_type, gboolean remove_from_store) { gchar *identifier; g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); identifier = pluma_message_type_identifier (pluma_message_type_get_object_path (message_type), pluma_message_type_get_method (message_type)); /* Keep message type alive for signal emission */ pluma_message_type_ref (message_type); if (!remove_from_store || g_hash_table_remove (bus->priv->types, identifier)) g_signal_emit (bus, message_bus_signals[UNREGISTERED], 0, message_type); pluma_message_type_unref (message_type); g_free (identifier); } /** * pluma_message_bus_unregister: * @bus: a #PlumaMessageBus * @message_type: the #PlumaMessageType to unregister * * Unregisters a previously registered message type. This is especially useful * for plugins which should unregister message types when they are deactivated. * * This function emits the #PlumaMessageBus::unregistered signal. * */ void pluma_message_bus_unregister (PlumaMessageBus *bus, PlumaMessageType *message_type) { g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); pluma_message_bus_unregister_real (bus, message_type, TRUE); } typedef struct { PlumaMessageBus *bus; const gchar *object_path; } UnregisterInfo; static gboolean unregister_each (const gchar *identifier, PlumaMessageType *message_type, UnregisterInfo *info) { if (strcmp (pluma_message_type_get_object_path (message_type), info->object_path) == 0) { pluma_message_bus_unregister_real (info->bus, message_type, FALSE); return TRUE; } return FALSE; } /** * pluma_message_bus_unregister_all: * @bus: a #PlumaMessageBus * @object_path: the object path * * Unregisters all message types for @object_path. This is especially useful for * plugins which should unregister message types when they are deactivated. * * This function emits the #PlumaMessageBus::unregistered signal for all * unregistered message types. * */ void pluma_message_bus_unregister_all (PlumaMessageBus *bus, const gchar *object_path) { UnregisterInfo info = {bus, object_path}; g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); g_return_if_fail (object_path != NULL); g_hash_table_foreach_remove (bus->priv->types, (GHRFunc)unregister_each, &info); } /** * pluma_message_bus_is_registered: * @bus: a #PlumaMessageBus * @object_path: the object path * @method: the method * * Check whether a message type @method at @object_path is registered on the * bus. * * Return value: %TRUE if the @method at @object_path is a registered message * type on the bus * */ gboolean pluma_message_bus_is_registered (PlumaMessageBus *bus, const gchar *object_path, const gchar *method) { gchar *identifier; gboolean ret; g_return_val_if_fail (PLUMA_IS_MESSAGE_BUS (bus), FALSE); g_return_val_if_fail (object_path != NULL, FALSE); g_return_val_if_fail (method != NULL, FALSE); identifier = pluma_message_type_identifier (object_path, method); ret = g_hash_table_lookup (bus->priv->types, identifier) != NULL; g_free(identifier); return ret; } typedef struct { PlumaMessageBusForeach func; gpointer userdata; } ForeachInfo; static void foreach_type (const gchar *key, PlumaMessageType *message_type, ForeachInfo *info) { pluma_message_type_ref (message_type); info->func (message_type, info->userdata); pluma_message_type_unref (message_type); } /** * pluma_message_bus_foreach: * @bus: the #PlumaMessagebus * @func: (scope call): the callback function * @userdata: the user data to supply to the callback function * * Calls @func for each message type registered on the bus * */ void pluma_message_bus_foreach (PlumaMessageBus *bus, PlumaMessageBusForeach func, gpointer userdata) { ForeachInfo info = {func, userdata}; g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); g_return_if_fail (func != NULL); g_hash_table_foreach (bus->priv->types, (GHFunc)foreach_type, &info); } /** * pluma_message_bus_connect: * @bus: a #PlumaMessageBus * @object_path: the object path * @method: the method * @callback: function to be called when message @method at @object_path is sent * @userdata: userdata to use for the callback * @destroy_data: function to evoke with @userdata as argument when @userdata * needs to be freed * * Connect a callback handler to be evoked when message @method at @object_path * is sent over the bus. * * Return value: the callback identifier * */ guint pluma_message_bus_connect (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, PlumaMessageCallback callback, gpointer userdata, GDestroyNotify destroy_data) { Message *message; g_return_val_if_fail (PLUMA_IS_MESSAGE_BUS (bus), 0); g_return_val_if_fail (object_path != NULL, 0); g_return_val_if_fail (method != NULL, 0); g_return_val_if_fail (callback != NULL, 0); /* lookup the message and create if it does not exist yet */ message = lookup_message (bus, object_path, method, TRUE); return add_listener (bus, message, callback, userdata, destroy_data); } /** * pluma_message_bus_disconnect: * @bus: a #PlumaMessageBus * @id: the callback id as returned by pluma_message_bus_connect() * * Disconnects a previously connected message callback. * */ void pluma_message_bus_disconnect (PlumaMessageBus *bus, guint id) { g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); process_by_id (bus, id, remove_listener); } /** * pluma_message_bus_disconnect_by_func: * @bus: a #PlumaMessageBus * @object_path: the object path * @method: the method * @callback: (scope call): the connected callback * @userdata: the userdata with which the callback was connected * * Disconnects a previously connected message callback by matching the * provided callback function and userdata. See also * pluma_message_bus_disconnect(). * */ void pluma_message_bus_disconnect_by_func (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, PlumaMessageCallback callback, gpointer userdata) { g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); process_by_match (bus, object_path, method, callback, userdata, remove_listener); } /** * pluma_message_bus_block: * @bus: a #PlumaMessageBus * @id: the callback id * * Blocks evoking the callback specified by @id. Unblock the callback by * using pluma_message_bus_unblock(). * */ void pluma_message_bus_block (PlumaMessageBus *bus, guint id) { g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); process_by_id (bus, id, block_listener); } /** * pluma_message_bus_block_by_func: * @bus: a #PlumaMessageBus * @object_path: the object path * @method: the method * @callback: (scope call): the callback to block * @userdata: the userdata with which the callback was connected * * Blocks evoking the callback that matches provided @callback and @userdata. * Unblock the callback using pluma_message_unblock_by_func(). * */ void pluma_message_bus_block_by_func (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, PlumaMessageCallback callback, gpointer userdata) { g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); process_by_match (bus, object_path, method, callback, userdata, block_listener); } /** * pluma_message_bus_unblock: * @bus: a #PlumaMessageBus * @id: the callback id * * Unblocks the callback specified by @id. * */ void pluma_message_bus_unblock (PlumaMessageBus *bus, guint id) { g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); process_by_id (bus, id, unblock_listener); } /** * pluma_message_bus_unblock_by_func: * @bus: a #PlumaMessageBus * @object_path: the object path * @method: the method * @callback: (scope call): the callback to block * @userdata: the userdata with which the callback was connected * * Unblocks the callback that matches provided @callback and @userdata. * */ void pluma_message_bus_unblock_by_func (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, PlumaMessageCallback callback, gpointer userdata) { g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); process_by_match (bus, object_path, method, callback, userdata, unblock_listener); } static gboolean validate_message (PlumaMessage *message) { if (!pluma_message_validate (message)) { g_warning ("Message '%s.%s' is invalid", pluma_message_get_object_path (message), pluma_message_get_method (message)); return FALSE; } return TRUE; } static void send_message_real (PlumaMessageBus *bus, PlumaMessage *message) { if (!validate_message (message)) { return; } bus->priv->message_queue = g_list_prepend (bus->priv->message_queue, g_object_ref (message)); if (bus->priv->idle_id == 0) bus->priv->idle_id = g_idle_add_full (G_PRIORITY_HIGH, (GSourceFunc)idle_dispatch, bus, NULL); } /** * pluma_message_bus_send_message: * @bus: a #PlumaMessageBus * @message: the message to send * * This sends the provided @message asynchronously over the bus. To send * a message synchronously, use pluma_message_bus_send_message_sync(). The * convenience function pluma_message_bus_send() can be used to easily send * a message without constructing the message object explicitly first. * */ void pluma_message_bus_send_message (PlumaMessageBus *bus, PlumaMessage *message) { g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); g_return_if_fail (PLUMA_IS_MESSAGE (message)); send_message_real (bus, message); } static void send_message_sync_real (PlumaMessageBus *bus, PlumaMessage *message) { if (!validate_message (message)) { return; } dispatch_message (bus, message); } /** * pluma_message_bus_send_message_sync: * @bus: a #PlumaMessageBus * @message: the message to send * * This sends the provided @message synchronously over the bus. To send * a message asynchronously, use pluma_message_bus_send_message(). The * convenience function pluma_message_bus_send_sync() can be used to easily send * a message without constructing the message object explicitly first. * */ void pluma_message_bus_send_message_sync (PlumaMessageBus *bus, PlumaMessage *message) { g_return_if_fail (PLUMA_IS_MESSAGE_BUS (bus)); g_return_if_fail (PLUMA_IS_MESSAGE (message)); send_message_sync_real (bus, message); } static PlumaMessage * create_message (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, va_list var_args) { PlumaMessageType *message_type; message_type = pluma_message_bus_lookup (bus, object_path, method); if (!message_type) { g_warning ("Could not find message type for '%s.%s'", object_path, method); return NULL; } return pluma_message_type_instantiate_valist (message_type, var_args); } /** * pluma_message_bus_send: * @bus: a #PlumaMessageBus * @object_path: the object path * @method: the method * @...: NULL terminated list of key/value pairs * * This provides a convenient way to quickly send a message @method at * @object_path asynchronously over the bus. The variable argument list * specifies key (string) value pairs used to construct the message arguments. * To send a message synchronously use pluma_message_bus_send_sync(). * */ void pluma_message_bus_send (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, ...) { va_list var_args; PlumaMessage *message; va_start (var_args, method); message = create_message (bus, object_path, method, var_args); if (message) { send_message_real (bus, message); g_object_unref (message); } else { g_warning ("Could not instantiate message"); } va_end (var_args); } /** * pluma_message_bus_send_sync: * @bus: a #PlumaMessageBus * @object_path: the object path * @method: the method * @...: NULL terminated list of key/value pairs * * This provides a convenient way to quickly send a message @method at * @object_path synchronously over the bus. The variable argument list * specifies key (string) value pairs used to construct the message * arguments. To send a message asynchronously use pluma_message_bus_send(). * * Return value: (transfer full): the constructed #PlumaMessage. The caller owns a reference * to the #PlumaMessage and should call g_object_unref() when * it is no longer needed */ PlumaMessage * pluma_message_bus_send_sync (PlumaMessageBus *bus, const gchar *object_path, const gchar *method, ...) { va_list var_args; PlumaMessage *message; va_start (var_args, method); message = create_message (bus, object_path, method, var_args); if (message) send_message_sync_real (bus, message); va_end (var_args); return message; } // ex:ts=8:noet: