| /* GDBus - GLib D-Bus Library | |
| * | |
| * Copyright (C) 2008-2010 Red Hat, Inc. | |
| * | |
| * SPDX-License-Identifier: LGPL-2.1-or-later | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General | |
| * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
| * | |
| * Author: David Zeuthen <davidz@redhat.com> | |
| */ | |
| #include "config.h" | |
| #include "gcancellable.h" | |
| #include "gdbusobjectmanager.h" | |
| #include "gdbusobjectmanagerclient.h" | |
| #include "gdbusobject.h" | |
| #include "gdbusprivate.h" | |
| #include "gioenumtypes.h" | |
| #include "gioerror.h" | |
| #include "ginitable.h" | |
| #include "gasyncresult.h" | |
| #include "gasyncinitable.h" | |
| #include "gdbusconnection.h" | |
| #include "gdbusutils.h" | |
| #include "gdbusobject.h" | |
| #include "gdbusobjectproxy.h" | |
| #include "gdbusproxy.h" | |
| #include "gdbusinterface.h" | |
| #include "glibintl.h" | |
| #include "gmarshal-internal.h" | |
| /** | |
| * GDBusObjectManagerClient: | |
| * | |
| * `GDBusObjectManagerClient` is used to create, monitor and delete object | |
| * proxies for remote objects exported by a [class@Gio.DBusObjectManagerServer] | |
| * (or any code implementing the | |
| * [org.freedesktop.DBus.ObjectManager](http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) | |
| * interface). | |
| * | |
| * Once an instance of this type has been created, you can connect to | |
| * the [signal@Gio.DBusObjectManager::object-added] and | |
| * [signal@Gio.DBusObjectManager::object-removed signals] and inspect the | |
| * [class@Gio.DBusObjectProxy] objects returned by | |
| * [method@Gio.DBusObjectManager.get_objects]. | |
| * | |
| * If the name for a `GDBusObjectManagerClient` is not owned by anyone at | |
| * object construction time, the default behavior is to request the | |
| * message bus to launch an owner for the name. This behavior can be | |
| * disabled using the `G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START` | |
| * flag. It’s also worth noting that this only works if the name of | |
| * interest is activatable in the first place. E.g. in some cases it | |
| * is not possible to launch an owner for the requested name. In this | |
| * case, `GDBusObjectManagerClient` object construction still succeeds but | |
| * there will be no object proxies | |
| * (e.g. [method@Gio.DBusObjectManager.get_objects] returns the empty list) and | |
| * the [property@Gio.DBusObjectManagerClient:name-owner] property is `NULL`. | |
| * | |
| * The owner of the requested name can come and go (for example | |
| * consider a system service being restarted) – `GDBusObjectManagerClient` | |
| * handles this case too; simply connect to the [signal@GObject.Object::notify] | |
| * signal to watch for changes on the | |
| * [property@Gio.DBusObjectManagerClient:name-owner] property. When the name | |
| * owner vanishes, the behavior is that | |
| * [property@Gio.DBusObjectManagerClient:name-owner] is set to `NULL` (this | |
| * includes emission of the [signal@GObject.Object::notify] signal) and then | |
| * [signal@Gio.DBusObjectManager::object-removed] signals are synthesized | |
| * for all currently existing object proxies. Since | |
| * [property@Gio.DBusObjectManagerClient:name-owner] is `NULL` when this | |
| * happens, you can use this information to disambiguate a synthesized signal | |
| * from a genuine signal caused by object removal on the remote | |
| * [iface@Gio.DBusObjectManager]. Similarly, when a new name owner appears, | |
| * [signal@Gio.DBusObjectManager::object-added] signals are synthesized | |
| * while [property@Gio.DBusObjectManagerClient:name-owner] is still `NULL`. Only | |
| * when all object proxies have been added, the | |
| * [property@Gio.DBusObjectManagerClient:name-owner] is set to the new name | |
| * owner (this includes emission of the [signal@GObject.Object::notify] signal). | |
| * Furthermore, you are guaranteed that | |
| * [property@Gio.DBusObjectManagerClient:name-owner] will alternate between a | |
| * name owner (e.g. `:1.42`) and `NULL` even in the case where | |
| * the name of interest is atomically replaced | |
| * | |
| * Ultimately, `GDBusObjectManagerClient` is used to obtain | |
| * [class@Gio.DBusProxy] instances. All signals (including the | |
| * `org.freedesktop.DBus.Properties::PropertiesChanged` signal) | |
| * delivered to [class@Gio.DBusProxy] instances are guaranteed to originate | |
| * from the name owner. This guarantee along with the behavior | |
| * described above, means that certain race conditions including the | |
| * “half the proxy is from the old owner and the other half is from | |
| * the new owner” problem cannot happen. | |
| * | |
| * To avoid having the application connect to signals on the returned | |
| * [class@Gio.DBusObjectProxy] and [class@Gio.DBusProxy] objects, the | |
| * [signal@Gio.DBusObject::interface-added], | |
| * [signal@Gio.DBusObject::interface-removed], | |
| * [signal@Gio.DBusProxy::g-properties-changed] and | |
| * [signal@Gio.DBusProxy::g-signal] signals | |
| * are also emitted on the `GDBusObjectManagerClient` instance managing these | |
| * objects. The signals emitted are | |
| * [signal@Gio.DBusObjectManager::interface-added], | |
| * [signal@Gio.DBusObjectManager::interface-removed], | |
| * [signal@Gio.DBusObjectManagerClient::interface-proxy-properties-changed] and | |
| * [signal@Gio.DBusObjectManagerClient::interface-proxy-signal]. | |
| * | |
| * Note that all callbacks and signals are emitted in the | |
| * thread-default main context (see | |
| * [method@GLib.MainContext.push_thread_default]) that the | |
| * `GDBusObjectManagerClient` object was constructed in. Additionally, the | |
| * [class@Gio.DBusObjectProxy] and [class@Gio.DBusProxy] objects | |
| * originating from the `GDBusObjectManagerClient` object will be created in | |
| * the same context and, consequently, will deliver signals in the | |
| * same main loop. | |
| * | |
| * Since: 2.30 | |
| */ | |
| struct _GDBusObjectManagerClientPrivate | |
| { | |
| GMutex lock; | |
| GBusType bus_type; | |
| GDBusConnection *connection; | |
| gchar *object_path; | |
| gchar *name; | |
| gchar *name_owner; | |
| GDBusObjectManagerClientFlags flags; | |
| GDBusProxy *control_proxy; | |
| GHashTable *map_object_path_to_object_proxy; | |
| guint signal_subscription_id; | |
| gchar *match_rule; | |
| GDBusProxyTypeFunc get_proxy_type_func; | |
| gpointer get_proxy_type_user_data; | |
| GDestroyNotify get_proxy_type_destroy_notify; | |
| gulong name_owner_signal_id; | |
| gulong signal_signal_id; | |
| GCancellable *cancel; | |
| }; | |
| enum | |
| { | |
| PROP_0, | |
| PROP_BUS_TYPE, | |
| PROP_CONNECTION, | |
| PROP_FLAGS, | |
| PROP_OBJECT_PATH, | |
| PROP_NAME, | |
| PROP_NAME_OWNER, | |
| PROP_GET_PROXY_TYPE_FUNC, | |
| PROP_GET_PROXY_TYPE_USER_DATA, | |
| PROP_GET_PROXY_TYPE_DESTROY_NOTIFY | |
| }; | |
| enum | |
| { | |
| INTERFACE_PROXY_SIGNAL_SIGNAL, | |
| INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL, | |
| LAST_SIGNAL | |
| }; | |
| static guint signals[LAST_SIGNAL] = { 0 }; | |
| static void initable_iface_init (GInitableIface *initable_iface); | |
| static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); | |
| static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface); | |
| G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerClient, g_dbus_object_manager_client, G_TYPE_OBJECT, | |
| G_ADD_PRIVATE (GDBusObjectManagerClient) | |
| G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) | |
| G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) | |
| G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init)) | |
| static void maybe_unsubscribe_signals (GDBusObjectManagerClient *manager); | |
| static void on_control_proxy_g_signal (GDBusProxy *proxy, | |
| const gchar *sender_name, | |
| const gchar *signal_name, | |
| GVariant *parameters, | |
| gpointer user_data); | |
| static void process_get_all_result (GDBusObjectManagerClient *manager, | |
| GVariant *value, | |
| const gchar *name_owner); | |
| static void | |
| g_dbus_object_manager_client_dispose (GObject *object) | |
| { | |
| GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (object); | |
| if (manager->priv->cancel != NULL) | |
| { | |
| g_cancellable_cancel (manager->priv->cancel); | |
| g_clear_object (&manager->priv->cancel); | |
| } | |
| G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->dispose (object); | |
| } | |
| static void | |
| g_dbus_object_manager_client_finalize (GObject *object) | |
| { | |
| GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (object); | |
| maybe_unsubscribe_signals (manager); | |
| g_hash_table_unref (manager->priv->map_object_path_to_object_proxy); | |
| if (manager->priv->control_proxy != NULL && manager->priv->signal_signal_id != 0) | |
| g_signal_handler_disconnect (manager->priv->control_proxy, | |
| manager->priv->signal_signal_id); | |
| manager->priv->signal_signal_id = 0; | |
| if (manager->priv->control_proxy != NULL && manager->priv->name_owner_signal_id != 0) | |
| g_signal_handler_disconnect (manager->priv->control_proxy, | |
| manager->priv->name_owner_signal_id); | |
| manager->priv->name_owner_signal_id = 0; | |
| g_clear_object (&manager->priv->control_proxy); | |
| if (manager->priv->connection != NULL) | |
| g_object_unref (manager->priv->connection); | |
| g_free (manager->priv->object_path); | |
| g_free (manager->priv->name); | |
| g_free (manager->priv->name_owner); | |
| if (manager->priv->get_proxy_type_destroy_notify != NULL) | |
| manager->priv->get_proxy_type_destroy_notify (manager->priv->get_proxy_type_user_data); | |
| g_mutex_clear (&manager->priv->lock); | |
| if (G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize != NULL) | |
| G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize (object); | |
| } | |
| static void | |
| g_dbus_object_manager_client_get_property (GObject *_object, | |
| guint prop_id, | |
| GValue *value, | |
| GParamSpec *pspec) | |
| { | |
| GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object); | |
| switch (prop_id) | |
| { | |
| case PROP_CONNECTION: | |
| g_value_set_object (value, g_dbus_object_manager_client_get_connection (manager)); | |
| break; | |
| case PROP_OBJECT_PATH: | |
| g_value_set_string (value, g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager))); | |
| break; | |
| case PROP_NAME: | |
| g_value_set_string (value, g_dbus_object_manager_client_get_name (manager)); | |
| break; | |
| case PROP_FLAGS: | |
| g_value_set_flags (value, g_dbus_object_manager_client_get_flags (manager)); | |
| break; | |
| case PROP_NAME_OWNER: | |
| g_value_take_string (value, g_dbus_object_manager_client_get_name_owner (manager)); | |
| break; | |
| default: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (manager, prop_id, pspec); | |
| break; | |
| } | |
| } | |
| static void | |
| g_dbus_object_manager_client_set_property (GObject *_object, | |
| guint prop_id, | |
| const GValue *value, | |
| GParamSpec *pspec) | |
| { | |
| GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object); | |
| const gchar *name; | |
| switch (prop_id) | |
| { | |
| case PROP_BUS_TYPE: | |
| manager->priv->bus_type = g_value_get_enum (value); | |
| break; | |
| case PROP_CONNECTION: | |
| if (g_value_get_object (value) != NULL) | |
| { | |
| g_assert (manager->priv->connection == NULL); | |
| g_assert (G_IS_DBUS_CONNECTION (g_value_get_object (value))); | |
| manager->priv->connection = g_value_dup_object (value); | |
| } | |
| break; | |
| case PROP_OBJECT_PATH: | |
| g_assert (manager->priv->object_path == NULL); | |
| g_assert (g_variant_is_object_path (g_value_get_string (value))); | |
| manager->priv->object_path = g_value_dup_string (value); | |
| break; | |
| case PROP_NAME: | |
| g_assert (manager->priv->name == NULL); | |
| name = g_value_get_string (value); | |
| g_assert (name == NULL || g_dbus_is_name (name)); | |
| manager->priv->name = g_strdup (name); | |
| break; | |
| case PROP_FLAGS: | |
| manager->priv->flags = g_value_get_flags (value); | |
| break; | |
| case PROP_GET_PROXY_TYPE_FUNC: | |
| manager->priv->get_proxy_type_func = g_value_get_pointer (value); | |
| break; | |
| case PROP_GET_PROXY_TYPE_USER_DATA: | |
| manager->priv->get_proxy_type_user_data = g_value_get_pointer (value); | |
| break; | |
| case PROP_GET_PROXY_TYPE_DESTROY_NOTIFY: | |
| manager->priv->get_proxy_type_destroy_notify = g_value_get_pointer (value); | |
| break; | |
| default: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (manager, prop_id, pspec); | |
| break; | |
| } | |
| } | |
| static void | |
| g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass) | |
| { | |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); | |
| gobject_class->dispose = g_dbus_object_manager_client_dispose; | |
| gobject_class->finalize = g_dbus_object_manager_client_finalize; | |
| gobject_class->set_property = g_dbus_object_manager_client_set_property; | |
| gobject_class->get_property = g_dbus_object_manager_client_get_property; | |
| /** | |
| * GDBusObjectManagerClient:connection: | |
| * | |
| * The #GDBusConnection to use. | |
| * | |
| * Since: 2.30 | |
| */ | |
| g_object_class_install_property (gobject_class, | |
| PROP_CONNECTION, | |
| g_param_spec_object ("connection", NULL, NULL, | |
| G_TYPE_DBUS_CONNECTION, | |
| G_PARAM_READABLE | | |
| G_PARAM_WRITABLE | | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GDBusObjectManagerClient:bus-type: | |
| * | |
| * If this property is not %G_BUS_TYPE_NONE, then | |
| * #GDBusObjectManagerClient:connection must be %NULL and will be set to the | |
| * #GDBusConnection obtained by calling g_bus_get() with the value | |
| * of this property. | |
| * | |
| * Since: 2.30 | |
| */ | |
| g_object_class_install_property (gobject_class, | |
| PROP_BUS_TYPE, | |
| g_param_spec_enum ("bus-type", NULL, NULL, | |
| G_TYPE_BUS_TYPE, | |
| G_BUS_TYPE_NONE, | |
| G_PARAM_WRITABLE | | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_NAME | | |
| G_PARAM_STATIC_BLURB | | |
| G_PARAM_STATIC_NICK)); | |
| /** | |
| * GDBusObjectManagerClient:flags: | |
| * | |
| * Flags from the #GDBusObjectManagerClientFlags enumeration. | |
| * | |
| * Since: 2.30 | |
| */ | |
| g_object_class_install_property (gobject_class, | |
| PROP_FLAGS, | |
| g_param_spec_flags ("flags", NULL, NULL, | |
| G_TYPE_DBUS_OBJECT_MANAGER_CLIENT_FLAGS, | |
| G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, | |
| G_PARAM_READABLE | | |
| G_PARAM_WRITABLE | | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_NAME | | |
| G_PARAM_STATIC_BLURB | | |
| G_PARAM_STATIC_NICK)); | |
| /** | |
| * GDBusObjectManagerClient:object-path: | |
| * | |
| * The object path the manager is for. | |
| * | |
| * Since: 2.30 | |
| */ | |
| g_object_class_install_property (gobject_class, | |
| PROP_OBJECT_PATH, | |
| g_param_spec_string ("object-path", NULL, NULL, | |
| NULL, | |
| G_PARAM_READABLE | | |
| G_PARAM_WRITABLE | | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GDBusObjectManagerClient:name: | |
| * | |
| * The well-known name or unique name that the manager is for. | |
| * | |
| * Since: 2.30 | |
| */ | |
| g_object_class_install_property (gobject_class, | |
| PROP_NAME, | |
| g_param_spec_string ("name", NULL, NULL, | |
| NULL, | |
| G_PARAM_READABLE | | |
| G_PARAM_WRITABLE | | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GDBusObjectManagerClient:name-owner: | |
| * | |
| * The unique name that owns #GDBusObjectManagerClient:name or %NULL if | |
| * no-one is currently owning the name. Connect to the | |
| * #GObject::notify signal to track changes to this property. | |
| * | |
| * Since: 2.30 | |
| */ | |
| g_object_class_install_property (gobject_class, | |
| PROP_NAME_OWNER, | |
| g_param_spec_string ("name-owner", NULL, NULL, | |
| NULL, | |
| G_PARAM_READABLE | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GDBusObjectManagerClient:get-proxy-type-func: | |
| * | |
| * The #GDBusProxyTypeFunc to use when determining what #GType to | |
| * use for interface proxies or %NULL. | |
| * | |
| * Since: 2.30 | |
| */ | |
| g_object_class_install_property (gobject_class, | |
| PROP_GET_PROXY_TYPE_FUNC, | |
| g_param_spec_pointer ("get-proxy-type-func", NULL, NULL, | |
| G_PARAM_READABLE | | |
| G_PARAM_WRITABLE | | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GDBusObjectManagerClient:get-proxy-type-user-data: | |
| * | |
| * The #gpointer user_data to pass to #GDBusObjectManagerClient:get-proxy-type-func. | |
| * | |
| * Since: 2.30 | |
| */ | |
| g_object_class_install_property (gobject_class, | |
| PROP_GET_PROXY_TYPE_USER_DATA, | |
| g_param_spec_pointer ("get-proxy-type-user-data", NULL, NULL, | |
| G_PARAM_READABLE | | |
| G_PARAM_WRITABLE | | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GDBusObjectManagerClient:get-proxy-type-destroy-notify: | |
| * | |
| * A #GDestroyNotify for the #gpointer user_data in #GDBusObjectManagerClient:get-proxy-type-user-data. | |
| * | |
| * Since: 2.30 | |
| */ | |
| g_object_class_install_property (gobject_class, | |
| PROP_GET_PROXY_TYPE_DESTROY_NOTIFY, | |
| g_param_spec_pointer ("get-proxy-type-destroy-notify", NULL, NULL, | |
| G_PARAM_READABLE | | |
| G_PARAM_WRITABLE | | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GDBusObjectManagerClient::interface-proxy-signal: | |
| * @manager: The #GDBusObjectManagerClient emitting the signal. | |
| * @object_proxy: The #GDBusObjectProxy on which an interface is emitting a D-Bus signal. | |
| * @interface_proxy: The #GDBusProxy that is emitting a D-Bus signal. | |
| * @sender_name: The sender of the signal or NULL if the connection is not a bus connection. | |
| * @signal_name: The signal name. | |
| * @parameters: A #GVariant tuple with parameters for the signal. | |
| * | |
| * Emitted when a D-Bus signal is received on @interface_proxy. | |
| * | |
| * This signal exists purely as a convenience to avoid having to | |
| * connect signals to all interface proxies managed by @manager. | |
| * | |
| * This signal is emitted in the | |
| * [thread-default main context][g-main-context-push-thread-default] | |
| * that @manager was constructed in. | |
| * | |
| * Since: 2.30 | |
| */ | |
| signals[INTERFACE_PROXY_SIGNAL_SIGNAL] = | |
| g_signal_new (I_("interface-proxy-signal"), | |
| G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, | |
| G_SIGNAL_RUN_LAST, | |
| G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_signal), | |
| NULL, | |
| NULL, | |
| _g_cclosure_marshal_VOID__OBJECT_OBJECT_STRING_STRING_VARIANT, | |
| G_TYPE_NONE, | |
| 5, | |
| G_TYPE_DBUS_OBJECT_PROXY, | |
| G_TYPE_DBUS_PROXY, | |
| G_TYPE_STRING, | |
| G_TYPE_STRING, | |
| G_TYPE_VARIANT); | |
| g_signal_set_va_marshaller (signals[INTERFACE_PROXY_SIGNAL_SIGNAL], | |
| G_TYPE_FROM_CLASS (klass), | |
| _g_cclosure_marshal_VOID__OBJECT_OBJECT_STRING_STRING_VARIANTv); | |
| /** | |
| * GDBusObjectManagerClient::interface-proxy-properties-changed: | |
| * @manager: The #GDBusObjectManagerClient emitting the signal. | |
| * @object_proxy: The #GDBusObjectProxy on which an interface has properties that are changing. | |
| * @interface_proxy: The #GDBusProxy that has properties that are changing. | |
| * @changed_properties: A #GVariant containing the properties that changed (type: `a{sv}`). | |
| * @invalidated_properties: (array zero-terminated=1) (element-type utf8): A %NULL terminated | |
| * array of properties that were invalidated. | |
| * | |
| * Emitted when one or more D-Bus properties on proxy changes. The | |
| * local cache has already been updated when this signal fires. Note | |
| * that both @changed_properties and @invalidated_properties are | |
| * guaranteed to never be %NULL (either may be empty though). | |
| * | |
| * This signal exists purely as a convenience to avoid having to | |
| * connect signals to all interface proxies managed by @manager. | |
| * | |
| * This signal is emitted in the | |
| * [thread-default main context][g-main-context-push-thread-default] | |
| * that @manager was constructed in. | |
| * | |
| * Since: 2.30 | |
| */ | |
| signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL] = | |
| g_signal_new (I_("interface-proxy-properties-changed"), | |
| G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, | |
| G_SIGNAL_RUN_LAST, | |
| G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_properties_changed), | |
| NULL, | |
| NULL, | |
| _g_cclosure_marshal_VOID__OBJECT_OBJECT_VARIANT_BOXED, | |
| G_TYPE_NONE, | |
| 4, | |
| G_TYPE_DBUS_OBJECT_PROXY, | |
| G_TYPE_DBUS_PROXY, | |
| G_TYPE_VARIANT, | |
| G_TYPE_STRV); | |
| g_signal_set_va_marshaller (signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL], | |
| G_TYPE_FROM_CLASS (klass), | |
| _g_cclosure_marshal_VOID__OBJECT_OBJECT_VARIANT_BOXEDv); | |
| } | |
| static void | |
| g_dbus_object_manager_client_init (GDBusObjectManagerClient *manager) | |
| { | |
| manager->priv = g_dbus_object_manager_client_get_instance_private (manager); | |
| g_mutex_init (&manager->priv->lock); | |
| manager->priv->map_object_path_to_object_proxy = g_hash_table_new_full (g_str_hash, | |
| g_str_equal, | |
| g_free, | |
| (GDestroyNotify) g_object_unref); | |
| manager->priv->cancel = g_cancellable_new (); | |
| } | |
| /* ---------------------------------------------------------------------------------------------------- */ | |
| /** | |
| * g_dbus_object_manager_client_new_sync: | |
| * @connection: A #GDBusConnection. | |
| * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. | |
| * @name: (nullable): The owner of the control object (unique or well-known name), or %NULL when not using a message bus connection. | |
| * @object_path: The object path of the control object. | |
| * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. | |
| * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. | |
| * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL. | |
| * @cancellable: (nullable): A #GCancellable or %NULL | |
| * @error: Return location for error or %NULL. | |
| * | |
| * Creates a new #GDBusObjectManagerClient object. | |
| * | |
| * This is a synchronous failable constructor - the calling thread is | |
| * blocked until a reply is received. See g_dbus_object_manager_client_new() | |
| * for the asynchronous version. | |
| * | |
| * Returns: (transfer full) (type GDBusObjectManagerClient): A | |
| * #GDBusObjectManagerClient object or %NULL if @error is set. Free | |
| * with g_object_unref(). | |
| * | |
| * Since: 2.30 | |
| */ | |
| GDBusObjectManager * | |
| g_dbus_object_manager_client_new_sync (GDBusConnection *connection, | |
| GDBusObjectManagerClientFlags flags, | |
| const gchar *name, | |
| const gchar *object_path, | |
| GDBusProxyTypeFunc get_proxy_type_func, | |
| gpointer get_proxy_type_user_data, | |
| GDestroyNotify get_proxy_type_destroy_notify, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GInitable *initable; | |
| g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); | |
| g_return_val_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || | |
| g_dbus_is_name (name), NULL); | |
| g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); | |
| g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
| initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, | |
| cancellable, | |
| error, | |
| "connection", connection, | |
| "flags", flags, | |
| "name", name, | |
| "object-path", object_path, | |
| "get-proxy-type-func", get_proxy_type_func, | |
| "get-proxy-type-user-data", get_proxy_type_user_data, | |
| "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify, | |
| NULL); | |
| if (initable != NULL) | |
| return G_DBUS_OBJECT_MANAGER (initable); | |
| else | |
| return NULL; | |
| } | |
| /** | |
| * g_dbus_object_manager_client_new: | |
| * @connection: A #GDBusConnection. | |
| * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. | |
| * @name: The owner of the control object (unique or well-known name). | |
| * @object_path: The object path of the control object. | |
| * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. | |
| * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. | |
| * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL. | |
| * @cancellable: (nullable): A #GCancellable or %NULL | |
| * @callback: A #GAsyncReadyCallback to call when the request is satisfied. | |
| * @user_data: The data to pass to @callback. | |
| * | |
| * Asynchronously creates a new #GDBusObjectManagerClient object. | |
| * | |
| * This is an asynchronous failable constructor. When the result is | |
| * ready, @callback will be invoked in the | |
| * [thread-default main context][g-main-context-push-thread-default] | |
| * of the thread you are calling this method from. You can | |
| * then call g_dbus_object_manager_client_new_finish() to get the result. See | |
| * g_dbus_object_manager_client_new_sync() for the synchronous version. | |
| * | |
| * Since: 2.30 | |
| */ | |
| void | |
| g_dbus_object_manager_client_new (GDBusConnection *connection, | |
| GDBusObjectManagerClientFlags flags, | |
| const gchar *name, | |
| const gchar *object_path, | |
| GDBusProxyTypeFunc get_proxy_type_func, | |
| gpointer get_proxy_type_user_data, | |
| GDestroyNotify get_proxy_type_destroy_notify, | |
| GCancellable *cancellable, | |
| GAsyncReadyCallback callback, | |
| gpointer user_data) | |
| { | |
| g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); | |
| g_return_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || | |
| g_dbus_is_name (name)); | |
| g_return_if_fail (g_variant_is_object_path (object_path)); | |
| g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, | |
| G_PRIORITY_DEFAULT, | |
| cancellable, | |
| callback, | |
| user_data, | |
| "connection", connection, | |
| "flags", flags, | |
| "name", name, | |
| "object-path", object_path, | |
| "get-proxy-type-func", get_proxy_type_func, | |
| "get-proxy-type-user-data", get_proxy_type_user_data, | |
| "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify, | |
| NULL); | |
| } | |
| /** | |
| * g_dbus_object_manager_client_new_finish: | |
| * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new(). | |
| * @error: Return location for error or %NULL. | |
| * | |
| * Finishes an operation started with g_dbus_object_manager_client_new(). | |
| * | |
| * Returns: (transfer full) (type GDBusObjectManagerClient): A | |
| * #GDBusObjectManagerClient object or %NULL if @error is set. Free | |
| * with g_object_unref(). | |
| * | |
| * Since: 2.30 | |
| */ | |
| GDBusObjectManager * | |
| g_dbus_object_manager_client_new_finish (GAsyncResult *res, | |
| GError **error) | |
| { | |
| GObject *object; | |
| GObject *source_object; | |
| source_object = g_async_result_get_source_object (res); | |
| g_assert (source_object != NULL); | |
| object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), | |
| res, | |
| error); | |
| g_object_unref (source_object); | |
| if (object != NULL) | |
| return G_DBUS_OBJECT_MANAGER (object); | |
| else | |
| return NULL; | |
| } | |
| /* ---------------------------------------------------------------------------------------------------- */ | |
| /** | |
| * g_dbus_object_manager_client_new_for_bus_sync: | |
| * @bus_type: A #GBusType. | |
| * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. | |
| * @name: The owner of the control object (unique or well-known name). | |
| * @object_path: The object path of the control object. | |
| * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. | |
| * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. | |
| * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL. | |
| * @cancellable: (nullable): A #GCancellable or %NULL | |
| * @error: Return location for error or %NULL. | |
| * | |
| * Like g_dbus_object_manager_client_new_sync() but takes a #GBusType instead | |
| * of a #GDBusConnection. | |
| * | |
| * This is a synchronous failable constructor - the calling thread is | |
| * blocked until a reply is received. See g_dbus_object_manager_client_new_for_bus() | |
| * for the asynchronous version. | |
| * | |
| * Returns: (transfer full) (type GDBusObjectManagerClient): A | |
| * #GDBusObjectManagerClient object or %NULL if @error is set. Free | |
| * with g_object_unref(). | |
| * | |
| * Since: 2.30 | |
| */ | |
| GDBusObjectManager * | |
| g_dbus_object_manager_client_new_for_bus_sync (GBusType bus_type, | |
| GDBusObjectManagerClientFlags flags, | |
| const gchar *name, | |
| const gchar *object_path, | |
| GDBusProxyTypeFunc get_proxy_type_func, | |
| gpointer get_proxy_type_user_data, | |
| GDestroyNotify get_proxy_type_destroy_notify, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GInitable *initable; | |
| g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, NULL); | |
| g_return_val_if_fail (g_dbus_is_name (name), NULL); | |
| g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); | |
| g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
| initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, | |
| cancellable, | |
| error, | |
| "bus-type", bus_type, | |
| "flags", flags, | |
| "name", name, | |
| "object-path", object_path, | |
| "get-proxy-type-func", get_proxy_type_func, | |
| "get-proxy-type-user-data", get_proxy_type_user_data, | |
| "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify, | |
| NULL); | |
| if (initable != NULL) | |
| return G_DBUS_OBJECT_MANAGER (initable); | |
| else | |
| return NULL; | |
| } | |
| /** | |
| * g_dbus_object_manager_client_new_for_bus: | |
| * @bus_type: A #GBusType. | |
| * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. | |
| * @name: The owner of the control object (unique or well-known name). | |
| * @object_path: The object path of the control object. | |
| * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. | |
| * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. | |
| * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL. | |
| * @cancellable: (nullable): A #GCancellable or %NULL | |
| * @callback: A #GAsyncReadyCallback to call when the request is satisfied. | |
| * @user_data: The data to pass to @callback. | |
| * | |
| * Like g_dbus_object_manager_client_new() but takes a #GBusType instead of a | |
| * #GDBusConnection. | |
| * | |
| * This is an asynchronous failable constructor. When the result is | |
| * ready, @callback will be invoked in the | |
| * [thread-default main loop][g-main-context-push-thread-default] | |
| * of the thread you are calling this method from. You can | |
| * then call g_dbus_object_manager_client_new_for_bus_finish() to get the result. See | |
| * g_dbus_object_manager_client_new_for_bus_sync() for the synchronous version. | |
| * | |
| * Since: 2.30 | |
| */ | |
| void | |
| g_dbus_object_manager_client_new_for_bus (GBusType bus_type, | |
| GDBusObjectManagerClientFlags flags, | |
| const gchar *name, | |
| const gchar *object_path, | |
| GDBusProxyTypeFunc get_proxy_type_func, | |
| gpointer get_proxy_type_user_data, | |
| GDestroyNotify get_proxy_type_destroy_notify, | |
| GCancellable *cancellable, | |
| GAsyncReadyCallback callback, | |
| gpointer user_data) | |
| { | |
| g_return_if_fail (bus_type != G_BUS_TYPE_NONE); | |
| g_return_if_fail (g_dbus_is_name (name)); | |
| g_return_if_fail (g_variant_is_object_path (object_path)); | |
| g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, | |
| G_PRIORITY_DEFAULT, | |
| cancellable, | |
| callback, | |
| user_data, | |
| "bus-type", bus_type, | |
| "flags", flags, | |
| "name", name, | |
| "object-path", object_path, | |
| "get-proxy-type-func", get_proxy_type_func, | |
| "get-proxy-type-user-data", get_proxy_type_user_data, | |
| "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify, | |
| NULL); | |
| } | |
| /** | |
| * g_dbus_object_manager_client_new_for_bus_finish: | |
| * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new_for_bus(). | |
| * @error: Return location for error or %NULL. | |
| * | |
| * Finishes an operation started with g_dbus_object_manager_client_new_for_bus(). | |
| * | |
| * Returns: (transfer full) (type GDBusObjectManagerClient): A | |
| * #GDBusObjectManagerClient object or %NULL if @error is set. Free | |
| * with g_object_unref(). | |
| * | |
| * Since: 2.30 | |
| */ | |
| GDBusObjectManager * | |
| g_dbus_object_manager_client_new_for_bus_finish (GAsyncResult *res, | |
| GError **error) | |
| { | |
| GObject *object; | |
| GObject *source_object; | |
| source_object = g_async_result_get_source_object (res); | |
| g_assert (source_object != NULL); | |
| object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), | |
| res, | |
| error); | |
| g_object_unref (source_object); | |
| if (object != NULL) | |
| return G_DBUS_OBJECT_MANAGER (object); | |
| else | |
| return NULL; | |
| } | |
| /* ---------------------------------------------------------------------------------------------------- */ | |
| /** | |
| * g_dbus_object_manager_client_get_connection: | |
| * @manager: A #GDBusObjectManagerClient | |
| * | |
| * Gets the #GDBusConnection used by @manager. | |
| * | |
| * Returns: (transfer none): A #GDBusConnection object. Do not free, | |
| * the object belongs to @manager. | |
| * | |
| * Since: 2.30 | |
| */ | |
| GDBusConnection * | |
| g_dbus_object_manager_client_get_connection (GDBusObjectManagerClient *manager) | |
| { | |
| GDBusConnection *ret; | |
| g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); | |
| g_mutex_lock (&manager->priv->lock); | |
| ret = manager->priv->connection; | |
| g_mutex_unlock (&manager->priv->lock); | |
| return ret; | |
| } | |
| /** | |
| * g_dbus_object_manager_client_get_name: | |
| * @manager: A #GDBusObjectManagerClient | |
| * | |
| * Gets the name that @manager is for, or %NULL if not a message bus | |
| * connection. | |
| * | |
| * Returns: A unique or well-known name. Do not free, the string | |
| * belongs to @manager. | |
| * | |
| * Since: 2.30 | |
| */ | |
| const gchar * | |
| g_dbus_object_manager_client_get_name (GDBusObjectManagerClient *manager) | |
| { | |
| const gchar *ret; | |
| g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); | |
| g_mutex_lock (&manager->priv->lock); | |
| ret = manager->priv->name; | |
| g_mutex_unlock (&manager->priv->lock); | |
| return ret; | |
| } | |
| /** | |
| * g_dbus_object_manager_client_get_flags: | |
| * @manager: A #GDBusObjectManagerClient | |
| * | |
| * Gets the flags that @manager was constructed with. | |
| * | |
| * Returns: Zero of more flags from the #GDBusObjectManagerClientFlags | |
| * enumeration. | |
| * | |
| * Since: 2.30 | |
| */ | |
| GDBusObjectManagerClientFlags | |
| g_dbus_object_manager_client_get_flags (GDBusObjectManagerClient *manager) | |
| { | |
| GDBusObjectManagerClientFlags ret; | |
| g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE); | |
| g_mutex_lock (&manager->priv->lock); | |
| ret = manager->priv->flags; | |
| g_mutex_unlock (&manager->priv->lock); | |
| return ret; | |
| } | |
| /** | |
| * g_dbus_object_manager_client_get_name_owner: | |
| * @manager: A #GDBusObjectManagerClient. | |
| * | |
| * The unique name that owns the name that @manager is for or %NULL if | |
| * no-one currently owns that name. You can connect to the | |
| * #GObject::notify signal to track changes to the | |
| * #GDBusObjectManagerClient:name-owner property. | |
| * | |
| * Returns: (nullable): The name owner or %NULL if no name owner | |
| * exists. Free with g_free(). | |
| * | |
| * Since: 2.30 | |
| */ | |
| gchar * | |
| g_dbus_object_manager_client_get_name_owner (GDBusObjectManagerClient *manager) | |
| { | |
| gchar *ret; | |
| g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); | |
| g_mutex_lock (&manager->priv->lock); | |
| ret = g_strdup (manager->priv->name_owner); | |
| g_mutex_unlock (&manager->priv->lock); | |
| return ret; | |
| } | |
| /* ---------------------------------------------------------------------------------------------------- */ | |
| /* signal handler for all objects we manage - we dispatch signals | |
| * from here to the objects | |
| */ | |
| static void | |
| signal_cb (GDBusConnection *connection, | |
| const gchar *sender_name, | |
| const gchar *object_path, | |
| const gchar *interface_name, | |
| const gchar *signal_name, | |
| GVariant *parameters, | |
| gpointer user_data) | |
| { | |
| GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (user_data); | |
| GDBusObjectProxy *object_proxy; | |
| GDBusInterface *interface; | |
| g_mutex_lock (&manager->priv->lock); | |
| object_proxy = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); | |
| if (object_proxy == NULL) | |
| { | |
| g_mutex_unlock (&manager->priv->lock); | |
| goto out; | |
| } | |
| g_object_ref (object_proxy); | |
| g_mutex_unlock (&manager->priv->lock); | |
| //g_debug ("yay, signal_cb %s %s: %s\n", signal_name, object_path, g_variant_print (parameters, TRUE)); | |
| g_object_ref (manager); | |
| if (g_strcmp0 (interface_name, DBUS_INTERFACE_PROPERTIES) == 0) | |
| { | |
| if (g_strcmp0 (signal_name, "PropertiesChanged") == 0) | |
| { | |
| const gchar *properties_interface_name; | |
| GVariant *changed_properties; | |
| const gchar **invalidated_properties; | |
| g_variant_get (parameters, | |
| "(&s@a{sv}^a&s)", | |
| &properties_interface_name, | |
| &changed_properties, | |
| &invalidated_properties); | |
| interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), properties_interface_name); | |
| if (interface != NULL) | |
| { | |
| GVariantIter property_iter; | |
| const gchar *property_name; | |
| GVariant *property_value; | |
| guint n; | |
| /* update caches... */ | |
| g_variant_iter_init (&property_iter, changed_properties); | |
| while (g_variant_iter_next (&property_iter, | |
| "{&sv}", | |
| &property_name, | |
| &property_value)) | |
| { | |
| g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface), | |
| property_name, | |
| property_value); | |
| g_variant_unref (property_value); | |
| } | |
| for (n = 0; invalidated_properties[n] != NULL; n++) | |
| { | |
| g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface), | |
| invalidated_properties[n], | |
| NULL); | |
| } | |
| /* ... and then synthesize the signal */ | |
| g_signal_emit_by_name (interface, | |
| "g-properties-changed", | |
| changed_properties, | |
| invalidated_properties); | |
| g_signal_emit (manager, | |
| signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL], | |
| 0, | |
| object_proxy, | |
| interface, | |
| changed_properties, | |
| invalidated_properties); | |
| g_object_unref (interface); | |
| } | |
| g_variant_unref (changed_properties); | |
| g_free (invalidated_properties); | |
| } | |
| } | |
| else | |
| { | |
| /* regular signal - just dispatch it */ | |
| interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), interface_name); | |
| if (interface != NULL) | |
| { | |
| g_signal_emit_by_name (interface, | |
| "g-signal", | |
| sender_name, | |
| signal_name, | |
| parameters); | |
| g_signal_emit (manager, | |
| signals[INTERFACE_PROXY_SIGNAL_SIGNAL], | |
| 0, | |
| object_proxy, | |
| interface, | |
| sender_name, | |
| signal_name, | |
| parameters); | |
| g_object_unref (interface); | |
| } | |
| } | |
| g_object_unref (manager); | |
| out: | |
| g_clear_object (&object_proxy); | |
| } | |
| static void | |
| subscribe_signals (GDBusObjectManagerClient *manager, | |
| const gchar *name_owner) | |
| { | |
| GError *error = NULL; | |
| GVariant *ret; | |
| g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager)); | |
| g_return_if_fail (manager->priv->signal_subscription_id == 0); | |
| g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner)); | |
| if (name_owner != NULL) | |
| { | |
| /* Only add path_namespace if it's non-'/'. This removes a no-op key from | |
| * the match rule, and also works around a D-Bus bug where | |
| * path_namespace='/' matches nothing in D-Bus versions < 1.6.18. | |
| * | |
| * See: https://bugs.freedesktop.org/show_bug.cgi?id=70799 */ | |
| if (g_str_equal (manager->priv->object_path, "/")) | |
| { | |
| manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s'", | |
| name_owner); | |
| } | |
| else | |
| { | |
| manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s',path_namespace='%s'", | |
| name_owner, manager->priv->object_path); | |
| } | |
| /* The bus daemon may not implement path_namespace so gracefully | |
| * handle this by using a fallback triggered if @error is set. */ | |
| ret = g_dbus_connection_call_sync (manager->priv->connection, | |
| DBUS_SERVICE_DBUS, | |
| DBUS_PATH_DBUS, | |
| DBUS_INTERFACE_DBUS, | |
| "AddMatch", | |
| g_variant_new ("(s)", | |
| manager->priv->match_rule), | |
| NULL, /* reply_type */ | |
| G_DBUS_CALL_FLAGS_NONE, | |
| -1, /* default timeout */ | |
| NULL, /* TODO: Cancellable */ | |
| &error); | |
| /* yay, bus daemon supports path_namespace */ | |
| if (ret != NULL) | |
| g_variant_unref (ret); | |
| } | |
| if (error == NULL) | |
| { | |
| /* still need to ask GDBusConnection for the callbacks */ | |
| manager->priv->signal_subscription_id = | |
| g_dbus_connection_signal_subscribe (manager->priv->connection, | |
| name_owner, | |
| NULL, /* interface */ | |
| NULL, /* member */ | |
| NULL, /* path - TODO: really want wildcard support here */ | |
| NULL, /* arg0 */ | |
| G_DBUS_SIGNAL_FLAGS_NONE | | |
| G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, | |
| signal_cb, | |
| manager, | |
| NULL); /* user_data_free_func */ | |
| } | |
| else | |
| { | |
| /* TODO: we could report this to the user | |
| g_warning ("Message bus daemon does not support path_namespace: %s (%s %d)", | |
| error->message, | |
| g_quark_to_string (error->domain), | |
| error->code); | |
| */ | |
| g_error_free (error); | |
| /* no need to call RemoveMatch when done since it didn't work */ | |
| g_free (manager->priv->match_rule); | |
| manager->priv->match_rule = NULL; | |
| /* Fallback is to subscribe to *all* signals from the name owner which | |
| * is rather wasteful. It's probably not a big practical problem because | |
| * users typically want all objects that the name owner supplies. | |
| */ | |
| manager->priv->signal_subscription_id = | |
| g_dbus_connection_signal_subscribe (manager->priv->connection, | |
| name_owner, | |
| NULL, /* interface */ | |
| NULL, /* member */ | |
| NULL, /* path - TODO: really want wildcard support here */ | |
| NULL, /* arg0 */ | |
| G_DBUS_SIGNAL_FLAGS_NONE, | |
| signal_cb, | |
| manager, | |
| NULL); /* user_data_free_func */ | |
| } | |
| } | |
| static void | |
| maybe_unsubscribe_signals (GDBusObjectManagerClient *manager) | |
| { | |
| g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager)); | |
| if (manager->priv->signal_subscription_id > 0) | |
| { | |
| g_dbus_connection_signal_unsubscribe (manager->priv->connection, | |
| manager->priv->signal_subscription_id); | |
| manager->priv->signal_subscription_id = 0; | |
| } | |
| if (manager->priv->match_rule != NULL) | |
| { | |
| /* Since the AddMatch call succeeded this is guaranteed to not | |
| * fail - therefore, don't bother checking the return value | |
| */ | |
| g_dbus_connection_call (manager->priv->connection, | |
| DBUS_SERVICE_DBUS, | |
| DBUS_PATH_DBUS, | |
| DBUS_INTERFACE_DBUS, | |
| "RemoveMatch", | |
| g_variant_new ("(s)", | |
| manager->priv->match_rule), | |
| NULL, /* reply_type */ | |
| G_DBUS_CALL_FLAGS_NONE, | |
| -1, /* default timeout */ | |
| NULL, /* GCancellable */ | |
| NULL, /* GAsyncReadyCallback */ | |
| NULL); /* user data */ | |
| g_free (manager->priv->match_rule); | |
| manager->priv->match_rule = NULL; | |
| } | |
| } | |
| /* ---------------------------------------------------------------------------------------------------- */ | |
| static GWeakRef * | |
| weak_ref_new (GObject *object) | |
| { | |
| GWeakRef *weak_ref = g_new0 (GWeakRef, 1); | |
| g_weak_ref_init (weak_ref, object); | |
| return g_steal_pointer (&weak_ref); | |
| } | |
| static void | |
| weak_ref_free (GWeakRef *weak_ref) | |
| { | |
| g_weak_ref_clear (weak_ref); | |
| g_free (weak_ref); | |
| } | |
| static void | |
| on_get_managed_objects_finish (GObject *source, | |
| GAsyncResult *result, | |
| gpointer user_data) | |
| { | |
| GDBusProxy *proxy = G_DBUS_PROXY (source); | |
| GWeakRef *manager_weak = user_data; | |
| GDBusObjectManagerClient *manager; | |
| GError *error = NULL; | |
| GVariant *value = NULL; | |
| gchar *new_name_owner = NULL; | |
| value = g_dbus_proxy_call_finish (proxy, result, &error); | |
| manager = G_DBUS_OBJECT_MANAGER_CLIENT (g_weak_ref_get (manager_weak)); | |
| /* Manager got disposed, nothing to do */ | |
| if (manager == NULL) | |
| { | |
| goto out; | |
| } | |
| new_name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy); | |
| if (value == NULL) | |
| { | |
| maybe_unsubscribe_signals (manager); | |
| if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) | |
| { | |
| g_warning ("Error calling GetManagedObjects() when name owner %s for name %s came back: %s", | |
| new_name_owner, | |
| manager->priv->name, | |
| error->message); | |
| } | |
| } | |
| else | |
| { | |
| process_get_all_result (manager, value, new_name_owner); | |
| } | |
| /* do the :name-owner notify *AFTER* emitting ::object-proxy-added signals - this | |
| * way the user knows that the signals were emitted because the name owner came back | |
| */ | |
| g_mutex_lock (&manager->priv->lock); | |
| manager->priv->name_owner = g_steal_pointer (&new_name_owner); | |
| g_mutex_unlock (&manager->priv->lock); | |
| g_object_notify (G_OBJECT (manager), "name-owner"); | |
| g_object_unref (manager); | |
| out: | |
| g_clear_error (&error); | |
| g_clear_pointer (&value, g_variant_unref); | |
| weak_ref_free (manager_weak); | |
| } | |
| static void | |
| on_notify_g_name_owner (GObject *object, | |
| GParamSpec *pspec, | |
| gpointer user_data) | |
| { | |
| GWeakRef *manager_weak = user_data; | |
| GDBusObjectManagerClient *manager = NULL; | |
| gchar *old_name_owner; | |
| gchar *new_name_owner; | |
| manager = G_DBUS_OBJECT_MANAGER_CLIENT (g_weak_ref_get (manager_weak)); | |
| if (manager == NULL) | |
| return; | |
| g_mutex_lock (&manager->priv->lock); | |
| old_name_owner = manager->priv->name_owner; | |
| new_name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy); | |
| manager->priv->name_owner = NULL; | |
| if (g_strcmp0 (old_name_owner, new_name_owner) != 0) | |
| { | |
| GPtrArray *proxies; | |
| /* remote manager changed; nuke all local proxies */ | |
| proxies = g_hash_table_steal_all_values ( | |
| manager->priv->map_object_path_to_object_proxy); | |
| g_mutex_unlock (&manager->priv->lock); | |
| /* do the :name-owner notify with a NULL name - this way the user knows | |
| * the ::object-proxy-removed following is because the name owner went | |
| * away | |
| */ | |
| g_object_notify (G_OBJECT (manager), "name-owner"); | |
| for (guint i = 0; i < proxies->len; ++i) | |
| { | |
| GDBusObjectProxy *object_proxy = | |
| G_DBUS_OBJECT_PROXY (g_ptr_array_index (proxies, i)); | |
| g_signal_emit_by_name (manager, "object-removed", object_proxy); | |
| } | |
| g_clear_pointer (&proxies, g_ptr_array_unref); | |
| /* nuke local filter */ | |
| maybe_unsubscribe_signals (manager); | |
| } | |
| else | |
| { | |
| g_mutex_unlock (&manager->priv->lock); | |
| } | |
| if (new_name_owner != NULL) | |
| { | |
| //g_debug ("repopulating for %s", new_name_owner); | |
| subscribe_signals (manager, | |
| new_name_owner); | |
| g_dbus_proxy_call (manager->priv->control_proxy, | |
| "GetManagedObjects", | |
| NULL, /* parameters */ | |
| G_DBUS_CALL_FLAGS_NONE, | |
| -1, | |
| manager->priv->cancel, | |
| on_get_managed_objects_finish, | |
| weak_ref_new (G_OBJECT (manager))); | |
| } | |
| g_free (new_name_owner); | |
| g_free (old_name_owner); | |
| g_object_unref (manager); | |
| } | |
| static gboolean | |
| initable_init (GInitable *initable, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (initable); | |
| gboolean ret; | |
| GVariant *value; | |
| GDBusProxyFlags proxy_flags; | |
| ret = FALSE; | |
| if (manager->priv->bus_type != G_BUS_TYPE_NONE) | |
| { | |
| g_assert (manager->priv->connection == NULL); | |
| manager->priv->connection = g_bus_get_sync (manager->priv->bus_type, cancellable, error); | |
| if (manager->priv->connection == NULL) | |
| goto out; | |
| } | |
| proxy_flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; | |
| if (manager->priv->flags & G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START) | |
| proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START; | |
| manager->priv->control_proxy = g_dbus_proxy_new_sync (manager->priv->connection, | |
| proxy_flags, | |
| NULL, /* GDBusInterfaceInfo* */ | |
| manager->priv->name, | |
| manager->priv->object_path, | |
| DBUS_INTERFACE_OBJECT_MANAGER, | |
| cancellable, | |
| error); | |
| if (manager->priv->control_proxy == NULL) | |
| goto out; | |
| /* Use weak refs here. The @control_proxy will emit its signals in the current | |
| * #GMainContext (since we constructed it just above). However, the user may | |
| * drop the last external reference to this #GDBusObjectManagerClient in | |
| * another thread between a signal being emitted and scheduled in an idle | |
| * callback in this #GMainContext, and that idle callback being invoked. We | |
| * can’t use a strong reference here, as there’s no | |
| * g_dbus_object_manager_client_disconnect() (or similar) method to tell us | |
| * when the last external reference to this object has been dropped, so we | |
| * can’t break a strong reference count cycle. So use weak refs. */ | |
| manager->priv->name_owner_signal_id = | |
| g_signal_connect_data (G_OBJECT (manager->priv->control_proxy), | |
| "notify::g-name-owner", | |
| G_CALLBACK (on_notify_g_name_owner), | |
| weak_ref_new (G_OBJECT (manager)), | |
| (GClosureNotify) weak_ref_free, | |
| G_CONNECT_DEFAULT); | |
| manager->priv->signal_signal_id = | |
| g_signal_connect_data (manager->priv->control_proxy, | |
| "g-signal", | |
| G_CALLBACK (on_control_proxy_g_signal), | |
| weak_ref_new (G_OBJECT (manager)), | |
| (GClosureNotify) weak_ref_free, | |
| G_CONNECT_DEFAULT); | |
| manager->priv->name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy); | |
| if (manager->priv->name_owner == NULL && manager->priv->name != NULL) | |
| { | |
| /* it's perfectly fine if there's no name owner.. we're just going to | |
| * wait until one is ready | |
| */ | |
| } | |
| else | |
| { | |
| /* yay, we can get the objects */ | |
| subscribe_signals (manager, | |
| manager->priv->name_owner); | |
| value = g_dbus_proxy_call_sync (manager->priv->control_proxy, | |
| "GetManagedObjects", | |
| NULL, /* parameters */ | |
| G_DBUS_CALL_FLAGS_NONE, | |
| -1, | |
| cancellable, | |
| error); | |
| if (value == NULL) | |
| { | |
| maybe_unsubscribe_signals (manager); | |
| g_warn_if_fail (manager->priv->signal_signal_id != 0); | |
| g_signal_handler_disconnect (manager->priv->control_proxy, | |
| manager->priv->signal_signal_id); | |
| manager->priv->signal_signal_id = 0; | |
| g_warn_if_fail (manager->priv->name_owner_signal_id != 0); | |
| g_signal_handler_disconnect (manager->priv->control_proxy, | |
| manager->priv->name_owner_signal_id); | |
| manager->priv->name_owner_signal_id = 0; | |
| g_object_unref (manager->priv->control_proxy); | |
| manager->priv->control_proxy = NULL; | |
| goto out; | |
| } | |
| process_get_all_result (manager, value, manager->priv->name_owner); | |
| g_variant_unref (value); | |
| } | |
| ret = TRUE; | |
| out: | |
| return ret; | |
| } | |
| static void | |
| initable_iface_init (GInitableIface *initable_iface) | |
| { | |
| initable_iface->init = initable_init; | |
| } | |
| static void | |
| async_initable_iface_init (GAsyncInitableIface *async_initable_iface) | |
| { | |
| /* for now, just use default: run GInitable code in thread */ | |
| } | |
| /* ---------------------------------------------------------------------------------------------------- */ | |
| static void | |
| add_interfaces (GDBusObjectManagerClient *manager, | |
| const gchar *object_path, | |
| GVariant *ifaces_and_properties, | |
| const gchar *name_owner) | |
| { | |
| GDBusObjectProxy *op; | |
| gboolean added; | |
| GVariantIter iter; | |
| const gchar *interface_name; | |
| GVariant *properties; | |
| GList *interface_added_signals, *l; | |
| GDBusProxy *interface_proxy; | |
| g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner)); | |
| g_mutex_lock (&manager->priv->lock); | |
| interface_added_signals = NULL; | |
| added = FALSE; | |
| op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); | |
| if (op == NULL) | |
| { | |
| GType object_proxy_type; | |
| if (manager->priv->get_proxy_type_func != NULL) | |
| { | |
| object_proxy_type = manager->priv->get_proxy_type_func (manager, | |
| object_path, | |
| NULL, | |
| manager->priv->get_proxy_type_user_data); | |
| g_warn_if_fail (g_type_is_a (object_proxy_type, G_TYPE_DBUS_OBJECT_PROXY)); | |
| } | |
| else | |
| { | |
| object_proxy_type = G_TYPE_DBUS_OBJECT_PROXY; | |
| } | |
| op = g_object_new (object_proxy_type, | |
| "g-connection", manager->priv->connection, | |
| "g-object-path", object_path, | |
| NULL); | |
| added = TRUE; | |
| } | |
| g_object_ref (op); | |
| g_variant_iter_init (&iter, ifaces_and_properties); | |
| while (g_variant_iter_next (&iter, | |
| "{&s@a{sv}}", | |
| &interface_name, | |
| &properties)) | |
| { | |
| GError *error; | |
| GType interface_proxy_type; | |
| if (manager->priv->get_proxy_type_func != NULL) | |
| { | |
| interface_proxy_type = manager->priv->get_proxy_type_func (manager, | |
| object_path, | |
| interface_name, | |
| manager->priv->get_proxy_type_user_data); | |
| g_warn_if_fail (g_type_is_a (interface_proxy_type, G_TYPE_DBUS_PROXY)); | |
| } | |
| else | |
| { | |
| interface_proxy_type = G_TYPE_DBUS_PROXY; | |
| } | |
| /* this is fine - there is no blocking IO because we pass DO_NOT_LOAD_PROPERTIES and | |
| * DO_NOT_CONNECT_SIGNALS and use a unique name | |
| */ | |
| error = NULL; | |
| interface_proxy = g_initable_new (interface_proxy_type, | |
| NULL, /* GCancellable */ | |
| &error, | |
| "g-connection", manager->priv->connection, | |
| "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | | |
| G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, | |
| "g-name", name_owner, | |
| "g-object-path", object_path, | |
| "g-interface-name", interface_name, | |
| NULL); | |
| if (interface_proxy == NULL) | |
| { | |
| g_warning ("%s: Error constructing proxy for path %s and interface %s: %s", | |
| G_STRLOC, | |
| object_path, | |
| interface_name, | |
| error->message); | |
| g_error_free (error); | |
| } | |
| else | |
| { | |
| GVariantIter property_iter; | |
| const gchar *property_name; | |
| GVariant *property_value; | |
| /* associate the interface proxy with the object */ | |
| g_dbus_interface_set_object (G_DBUS_INTERFACE (interface_proxy), | |
| G_DBUS_OBJECT (op)); | |
| g_variant_iter_init (&property_iter, properties); | |
| while (g_variant_iter_next (&property_iter, | |
| "{&sv}", | |
| &property_name, | |
| &property_value)) | |
| { | |
| g_dbus_proxy_set_cached_property (interface_proxy, | |
| property_name, | |
| property_value); | |
| g_variant_unref (property_value); | |
| } | |
| _g_dbus_object_proxy_add_interface (op, interface_proxy); | |
| if (!added) | |
| interface_added_signals = g_list_append (interface_added_signals, g_object_ref (interface_proxy)); | |
| g_object_unref (interface_proxy); | |
| } | |
| g_variant_unref (properties); | |
| } | |
| if (added) | |
| { | |
| g_hash_table_insert (manager->priv->map_object_path_to_object_proxy, | |
| g_strdup (object_path), | |
| op); | |
| } | |
| g_mutex_unlock (&manager->priv->lock); | |
| /* now that we don't hold the lock any more, emit signals */ | |
| g_object_ref (manager); | |
| for (l = interface_added_signals; l != NULL; l = l->next) | |
| { | |
| interface_proxy = G_DBUS_PROXY (l->data); | |
| g_signal_emit_by_name (manager, "interface-added", op, interface_proxy); | |
| g_object_unref (interface_proxy); | |
| } | |
| g_list_free (interface_added_signals); | |
| if (added) | |
| g_signal_emit_by_name (manager, "object-added", op); | |
| g_object_unref (manager); | |
| g_object_unref (op); | |
| } | |
| static void | |
| remove_interfaces (GDBusObjectManagerClient *manager, | |
| const gchar *object_path, | |
| const gchar *const *interface_names) | |
| { | |
| GDBusObjectProxy *op; | |
| GList *interfaces; | |
| guint n; | |
| guint num_interfaces; | |
| guint num_interfaces_to_remove; | |
| g_mutex_lock (&manager->priv->lock); | |
| op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); | |
| if (op == NULL) | |
| { | |
| g_debug ("%s: Processing InterfaceRemoved signal for path %s but no object proxy exists", | |
| G_STRLOC, | |
| object_path); | |
| g_mutex_unlock (&manager->priv->lock); | |
| return; | |
| } | |
| interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (op)); | |
| num_interfaces = g_list_length (interfaces); | |
| g_list_free_full (interfaces, g_object_unref); | |
| num_interfaces_to_remove = g_strv_length ((gchar **) interface_names); | |
| /* see if we are going to completety remove the object */ | |
| g_object_ref (manager); | |
| if (num_interfaces_to_remove == num_interfaces) | |
| { | |
| g_object_ref (op); | |
| g_warn_if_fail (g_hash_table_remove (manager->priv->map_object_path_to_object_proxy, object_path)); | |
| g_mutex_unlock (&manager->priv->lock); | |
| g_signal_emit_by_name (manager, "object-removed", op); | |
| g_object_unref (op); | |
| } | |
| else | |
| { | |
| g_object_ref (op); | |
| g_mutex_unlock (&manager->priv->lock); | |
| for (n = 0; interface_names != NULL && interface_names[n] != NULL; n++) | |
| { | |
| GDBusInterface *interface; | |
| interface = g_dbus_object_get_interface (G_DBUS_OBJECT (op), interface_names[n]); | |
| _g_dbus_object_proxy_remove_interface (op, interface_names[n]); | |
| if (interface != NULL) | |
| { | |
| g_signal_emit_by_name (manager, "interface-removed", op, interface); | |
| g_object_unref (interface); | |
| } | |
| } | |
| g_object_unref (op); | |
| } | |
| g_object_unref (manager); | |
| } | |
| static void | |
| process_get_all_result (GDBusObjectManagerClient *manager, | |
| GVariant *value, | |
| const gchar *name_owner) | |
| { | |
| GVariant *arg0; | |
| const gchar *object_path; | |
| GVariant *ifaces_and_properties; | |
| GVariantIter iter; | |
| g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner)); | |
| arg0 = g_variant_get_child_value (value, 0); | |
| g_variant_iter_init (&iter, arg0); | |
| while (g_variant_iter_next (&iter, | |
| "{&o@a{sa{sv}}}", | |
| &object_path, | |
| &ifaces_and_properties)) | |
| { | |
| add_interfaces (manager, object_path, ifaces_and_properties, name_owner); | |
| g_variant_unref (ifaces_and_properties); | |
| } | |
| g_variant_unref (arg0); | |
| } | |
| static void | |
| on_control_proxy_g_signal (GDBusProxy *proxy, | |
| const gchar *sender_name, | |
| const gchar *signal_name, | |
| GVariant *parameters, | |
| gpointer user_data) | |
| { | |
| GWeakRef *manager_weak = user_data; | |
| GDBusObjectManagerClient *manager = NULL; | |
| const gchar *object_path; | |
| manager = G_DBUS_OBJECT_MANAGER_CLIENT (g_weak_ref_get (manager_weak)); | |
| if (manager == NULL) | |
| return; | |
| //g_debug ("yay, g_signal %s: %s\n", signal_name, g_variant_print (parameters, TRUE)); | |
| if (g_strcmp0 (signal_name, "InterfacesAdded") == 0) | |
| { | |
| GVariant *ifaces_and_properties; | |
| g_variant_get (parameters, | |
| "(&o@a{sa{sv}})", | |
| &object_path, | |
| &ifaces_and_properties); | |
| add_interfaces (manager, object_path, ifaces_and_properties, manager->priv->name_owner); | |
| g_variant_unref (ifaces_and_properties); | |
| } | |
| else if (g_strcmp0 (signal_name, "InterfacesRemoved") == 0) | |
| { | |
| const gchar **ifaces; | |
| g_variant_get (parameters, | |
| "(&o^a&s)", | |
| &object_path, | |
| &ifaces); | |
| remove_interfaces (manager, object_path, ifaces); | |
| g_free (ifaces); | |
| } | |
| g_object_unref (manager); | |
| } | |
| /* ---------------------------------------------------------------------------------------------------- */ | |
| static const gchar * | |
| g_dbus_object_manager_client_get_object_path (GDBusObjectManager *_manager) | |
| { | |
| GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager); | |
| return manager->priv->object_path; | |
| } | |
| static GDBusObject * | |
| g_dbus_object_manager_client_get_object (GDBusObjectManager *_manager, | |
| const gchar *object_path) | |
| { | |
| GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager); | |
| GDBusObject *ret; | |
| g_mutex_lock (&manager->priv->lock); | |
| ret = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); | |
| if (ret != NULL) | |
| g_object_ref (ret); | |
| g_mutex_unlock (&manager->priv->lock); | |
| return ret; | |
| } | |
| static GDBusInterface * | |
| g_dbus_object_manager_client_get_interface (GDBusObjectManager *_manager, | |
| const gchar *object_path, | |
| const gchar *interface_name) | |
| { | |
| GDBusInterface *ret; | |
| GDBusObject *object; | |
| ret = NULL; | |
| object = g_dbus_object_manager_get_object (_manager, object_path); | |
| if (object == NULL) | |
| goto out; | |
| ret = g_dbus_object_get_interface (object, interface_name); | |
| g_object_unref (object); | |
| out: | |
| return ret; | |
| } | |
| static GList * | |
| g_dbus_object_manager_client_get_objects (GDBusObjectManager *_manager) | |
| { | |
| GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager); | |
| GList *ret; | |
| g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); | |
| g_mutex_lock (&manager->priv->lock); | |
| ret = g_hash_table_get_values (manager->priv->map_object_path_to_object_proxy); | |
| g_list_foreach (ret, (GFunc) g_object_ref, NULL); | |
| g_mutex_unlock (&manager->priv->lock); | |
| return ret; | |
| } | |
| static void | |
| dbus_object_manager_interface_init (GDBusObjectManagerIface *iface) | |
| { | |
| iface->get_object_path = g_dbus_object_manager_client_get_object_path; | |
| iface->get_objects = g_dbus_object_manager_client_get_objects; | |
| iface->get_object = g_dbus_object_manager_client_get_object; | |
| iface->get_interface = g_dbus_object_manager_client_get_interface; | |
| } | |
| /* ---------------------------------------------------------------------------------------------------- */ |