mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-26 14:02:17 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1856 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1856 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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
 | ||
|    * (see [method@GLib.MainContext.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
 | ||
|    * (see [method@GLib.MainContext.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
 | ||
|  * (see [method@GLib.MainContext.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 context
 | ||
|  * (see [method@GLib.MainContext.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,
 | ||
|                                           g_steal_handle_id (&manager->priv->signal_subscription_id));
 | ||
| 
 | ||
|   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 completely 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;
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 |