/src/glib/gio/gdbusobjectmanagerclient.c
Line | Count | Source |
1 | | /* GDBus - GLib D-Bus Library |
2 | | * |
3 | | * Copyright (C) 2008-2010 Red Hat, Inc. |
4 | | * |
5 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * This library is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General |
18 | | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
19 | | * |
20 | | * Author: David Zeuthen <davidz@redhat.com> |
21 | | */ |
22 | | |
23 | | #include "config.h" |
24 | | |
25 | | #include "gcancellable.h" |
26 | | #include "gdbusobjectmanager.h" |
27 | | #include "gdbusobjectmanagerclient.h" |
28 | | #include "gdbusobject.h" |
29 | | #include "gdbusprivate.h" |
30 | | #include "gioenumtypes.h" |
31 | | #include "gioerror.h" |
32 | | #include "ginitable.h" |
33 | | #include "gasyncresult.h" |
34 | | #include "gasyncinitable.h" |
35 | | #include "gdbusconnection.h" |
36 | | #include "gdbusutils.h" |
37 | | #include "gdbusobject.h" |
38 | | #include "gdbusobjectproxy.h" |
39 | | #include "gdbusproxy.h" |
40 | | #include "gdbusinterface.h" |
41 | | |
42 | | #include "glibintl.h" |
43 | | #include "gmarshal-internal.h" |
44 | | |
45 | | /** |
46 | | * GDBusObjectManagerClient: |
47 | | * |
48 | | * `GDBusObjectManagerClient` is used to create, monitor and delete object |
49 | | * proxies for remote objects exported by a [class@Gio.DBusObjectManagerServer] |
50 | | * (or any code implementing the |
51 | | * [org.freedesktop.DBus.ObjectManager](http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) |
52 | | * interface). |
53 | | * |
54 | | * Once an instance of this type has been created, you can connect to |
55 | | * the [signal@Gio.DBusObjectManager::object-added] and |
56 | | * [signal@Gio.DBusObjectManager::object-removed signals] and inspect the |
57 | | * [class@Gio.DBusObjectProxy] objects returned by |
58 | | * [method@Gio.DBusObjectManager.get_objects]. |
59 | | * |
60 | | * If the name for a `GDBusObjectManagerClient` is not owned by anyone at |
61 | | * object construction time, the default behavior is to request the |
62 | | * message bus to launch an owner for the name. This behavior can be |
63 | | * disabled using the `G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START` |
64 | | * flag. It’s also worth noting that this only works if the name of |
65 | | * interest is activatable in the first place. E.g. in some cases it |
66 | | * is not possible to launch an owner for the requested name. In this |
67 | | * case, `GDBusObjectManagerClient` object construction still succeeds but |
68 | | * there will be no object proxies |
69 | | * (e.g. [method@Gio.DBusObjectManager.get_objects] returns the empty list) and |
70 | | * the [property@Gio.DBusObjectManagerClient:name-owner] property is `NULL`. |
71 | | * |
72 | | * The owner of the requested name can come and go (for example |
73 | | * consider a system service being restarted) – `GDBusObjectManagerClient` |
74 | | * handles this case too; simply connect to the [signal@GObject.Object::notify] |
75 | | * signal to watch for changes on the |
76 | | * [property@Gio.DBusObjectManagerClient:name-owner] property. When the name |
77 | | * owner vanishes, the behavior is that |
78 | | * [property@Gio.DBusObjectManagerClient:name-owner] is set to `NULL` (this |
79 | | * includes emission of the [signal@GObject.Object::notify] signal) and then |
80 | | * [signal@Gio.DBusObjectManager::object-removed] signals are synthesized |
81 | | * for all currently existing object proxies. Since |
82 | | * [property@Gio.DBusObjectManagerClient:name-owner] is `NULL` when this |
83 | | * happens, you can use this information to disambiguate a synthesized signal |
84 | | * from a genuine signal caused by object removal on the remote |
85 | | * [iface@Gio.DBusObjectManager]. Similarly, when a new name owner appears, |
86 | | * [signal@Gio.DBusObjectManager::object-added] signals are synthesized |
87 | | * while [property@Gio.DBusObjectManagerClient:name-owner] is still `NULL`. Only |
88 | | * when all object proxies have been added, the |
89 | | * [property@Gio.DBusObjectManagerClient:name-owner] is set to the new name |
90 | | * owner (this includes emission of the [signal@GObject.Object::notify] signal). |
91 | | * Furthermore, you are guaranteed that |
92 | | * [property@Gio.DBusObjectManagerClient:name-owner] will alternate between a |
93 | | * name owner (e.g. `:1.42`) and `NULL` even in the case where |
94 | | * the name of interest is atomically replaced |
95 | | * |
96 | | * Ultimately, `GDBusObjectManagerClient` is used to obtain |
97 | | * [class@Gio.DBusProxy] instances. All signals (including the |
98 | | * `org.freedesktop.DBus.Properties::PropertiesChanged` signal) |
99 | | * delivered to [class@Gio.DBusProxy] instances are guaranteed to originate |
100 | | * from the name owner. This guarantee along with the behavior |
101 | | * described above, means that certain race conditions including the |
102 | | * “half the proxy is from the old owner and the other half is from |
103 | | * the new owner” problem cannot happen. |
104 | | * |
105 | | * To avoid having the application connect to signals on the returned |
106 | | * [class@Gio.DBusObjectProxy] and [class@Gio.DBusProxy] objects, the |
107 | | * [signal@Gio.DBusObject::interface-added], |
108 | | * [signal@Gio.DBusObject::interface-removed], |
109 | | * [signal@Gio.DBusProxy::g-properties-changed] and |
110 | | * [signal@Gio.DBusProxy::g-signal] signals |
111 | | * are also emitted on the `GDBusObjectManagerClient` instance managing these |
112 | | * objects. The signals emitted are |
113 | | * [signal@Gio.DBusObjectManager::interface-added], |
114 | | * [signal@Gio.DBusObjectManager::interface-removed], |
115 | | * [signal@Gio.DBusObjectManagerClient::interface-proxy-properties-changed] and |
116 | | * [signal@Gio.DBusObjectManagerClient::interface-proxy-signal]. |
117 | | * |
118 | | * Note that all callbacks and signals are emitted in the |
119 | | * thread-default main context (see |
120 | | * [method@GLib.MainContext.push_thread_default]) that the |
121 | | * `GDBusObjectManagerClient` object was constructed in. Additionally, the |
122 | | * [class@Gio.DBusObjectProxy] and [class@Gio.DBusProxy] objects |
123 | | * originating from the `GDBusObjectManagerClient` object will be created in |
124 | | * the same context and, consequently, will deliver signals in the |
125 | | * same main loop. |
126 | | * |
127 | | * Since: 2.30 |
128 | | */ |
129 | | |
130 | | struct _GDBusObjectManagerClientPrivate |
131 | | { |
132 | | GMutex lock; |
133 | | |
134 | | GBusType bus_type; |
135 | | GDBusConnection *connection; |
136 | | gchar *object_path; |
137 | | gchar *name; |
138 | | gchar *name_owner; |
139 | | GDBusObjectManagerClientFlags flags; |
140 | | |
141 | | GDBusProxy *control_proxy; |
142 | | |
143 | | GHashTable *map_object_path_to_object_proxy; |
144 | | |
145 | | guint signal_subscription_id; |
146 | | gchar *match_rule; |
147 | | |
148 | | GDBusProxyTypeFunc get_proxy_type_func; |
149 | | gpointer get_proxy_type_user_data; |
150 | | GDestroyNotify get_proxy_type_destroy_notify; |
151 | | |
152 | | gulong name_owner_signal_id; |
153 | | gulong signal_signal_id; |
154 | | GCancellable *cancel; |
155 | | }; |
156 | | |
157 | | enum |
158 | | { |
159 | | PROP_0, |
160 | | PROP_BUS_TYPE, |
161 | | PROP_CONNECTION, |
162 | | PROP_FLAGS, |
163 | | PROP_OBJECT_PATH, |
164 | | PROP_NAME, |
165 | | PROP_NAME_OWNER, |
166 | | PROP_GET_PROXY_TYPE_FUNC, |
167 | | PROP_GET_PROXY_TYPE_USER_DATA, |
168 | | PROP_GET_PROXY_TYPE_DESTROY_NOTIFY |
169 | | }; |
170 | | |
171 | | enum |
172 | | { |
173 | | INTERFACE_PROXY_SIGNAL_SIGNAL, |
174 | | INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL, |
175 | | LAST_SIGNAL |
176 | | }; |
177 | | |
178 | | static guint signals[LAST_SIGNAL] = { 0 }; |
179 | | |
180 | | static void initable_iface_init (GInitableIface *initable_iface); |
181 | | static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); |
182 | | static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface); |
183 | | |
184 | 0 | G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerClient, g_dbus_object_manager_client, G_TYPE_OBJECT, |
185 | 0 | G_ADD_PRIVATE (GDBusObjectManagerClient) |
186 | 0 | G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) |
187 | 0 | G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) |
188 | 0 | G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init)) |
189 | 0 |
|
190 | 0 | static void maybe_unsubscribe_signals (GDBusObjectManagerClient *manager); |
191 | 0 |
|
192 | 0 | static void on_control_proxy_g_signal (GDBusProxy *proxy, |
193 | 0 | const gchar *sender_name, |
194 | 0 | const gchar *signal_name, |
195 | 0 | GVariant *parameters, |
196 | 0 | gpointer user_data); |
197 | 0 |
|
198 | 0 | static void process_get_all_result (GDBusObjectManagerClient *manager, |
199 | 0 | GVariant *value, |
200 | 0 | const gchar *name_owner); |
201 | 0 |
|
202 | 0 | static void |
203 | 0 | g_dbus_object_manager_client_dispose (GObject *object) |
204 | 0 | { |
205 | 0 | GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (object); |
206 | |
|
207 | 0 | if (manager->priv->cancel != NULL) |
208 | 0 | { |
209 | 0 | g_cancellable_cancel (manager->priv->cancel); |
210 | 0 | g_clear_object (&manager->priv->cancel); |
211 | 0 | } |
212 | |
|
213 | 0 | G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->dispose (object); |
214 | 0 | } |
215 | | |
216 | | static void |
217 | | g_dbus_object_manager_client_finalize (GObject *object) |
218 | 0 | { |
219 | 0 | GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (object); |
220 | |
|
221 | 0 | maybe_unsubscribe_signals (manager); |
222 | |
|
223 | 0 | g_hash_table_unref (manager->priv->map_object_path_to_object_proxy); |
224 | |
|
225 | 0 | if (manager->priv->control_proxy != NULL && manager->priv->signal_signal_id != 0) |
226 | 0 | g_signal_handler_disconnect (manager->priv->control_proxy, |
227 | 0 | manager->priv->signal_signal_id); |
228 | 0 | manager->priv->signal_signal_id = 0; |
229 | |
|
230 | 0 | if (manager->priv->control_proxy != NULL && manager->priv->name_owner_signal_id != 0) |
231 | 0 | g_signal_handler_disconnect (manager->priv->control_proxy, |
232 | 0 | manager->priv->name_owner_signal_id); |
233 | 0 | manager->priv->name_owner_signal_id = 0; |
234 | |
|
235 | 0 | g_clear_object (&manager->priv->control_proxy); |
236 | |
|
237 | 0 | if (manager->priv->connection != NULL) |
238 | 0 | g_object_unref (manager->priv->connection); |
239 | 0 | g_free (manager->priv->object_path); |
240 | 0 | g_free (manager->priv->name); |
241 | 0 | g_free (manager->priv->name_owner); |
242 | |
|
243 | 0 | if (manager->priv->get_proxy_type_destroy_notify != NULL) |
244 | 0 | manager->priv->get_proxy_type_destroy_notify (manager->priv->get_proxy_type_user_data); |
245 | |
|
246 | 0 | g_mutex_clear (&manager->priv->lock); |
247 | |
|
248 | 0 | if (G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize != NULL) |
249 | 0 | G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize (object); |
250 | 0 | } |
251 | | |
252 | | static void |
253 | | g_dbus_object_manager_client_get_property (GObject *_object, |
254 | | guint prop_id, |
255 | | GValue *value, |
256 | | GParamSpec *pspec) |
257 | 0 | { |
258 | 0 | GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object); |
259 | |
|
260 | 0 | switch (prop_id) |
261 | 0 | { |
262 | 0 | case PROP_CONNECTION: |
263 | 0 | g_value_set_object (value, g_dbus_object_manager_client_get_connection (manager)); |
264 | 0 | break; |
265 | | |
266 | 0 | case PROP_OBJECT_PATH: |
267 | 0 | g_value_set_string (value, g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager))); |
268 | 0 | break; |
269 | | |
270 | 0 | case PROP_NAME: |
271 | 0 | g_value_set_string (value, g_dbus_object_manager_client_get_name (manager)); |
272 | 0 | break; |
273 | | |
274 | 0 | case PROP_FLAGS: |
275 | 0 | g_value_set_flags (value, g_dbus_object_manager_client_get_flags (manager)); |
276 | 0 | break; |
277 | | |
278 | 0 | case PROP_NAME_OWNER: |
279 | 0 | g_value_take_string (value, g_dbus_object_manager_client_get_name_owner (manager)); |
280 | 0 | break; |
281 | | |
282 | 0 | default: |
283 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (manager, prop_id, pspec); |
284 | 0 | break; |
285 | 0 | } |
286 | 0 | } |
287 | | |
288 | | static void |
289 | | g_dbus_object_manager_client_set_property (GObject *_object, |
290 | | guint prop_id, |
291 | | const GValue *value, |
292 | | GParamSpec *pspec) |
293 | 0 | { |
294 | 0 | GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object); |
295 | 0 | const gchar *name; |
296 | |
|
297 | 0 | switch (prop_id) |
298 | 0 | { |
299 | 0 | case PROP_BUS_TYPE: |
300 | 0 | manager->priv->bus_type = g_value_get_enum (value); |
301 | 0 | break; |
302 | | |
303 | 0 | case PROP_CONNECTION: |
304 | 0 | if (g_value_get_object (value) != NULL) |
305 | 0 | { |
306 | 0 | g_assert (manager->priv->connection == NULL); |
307 | 0 | g_assert (G_IS_DBUS_CONNECTION (g_value_get_object (value))); |
308 | 0 | manager->priv->connection = g_value_dup_object (value); |
309 | 0 | } |
310 | 0 | break; |
311 | | |
312 | 0 | case PROP_OBJECT_PATH: |
313 | 0 | g_assert (manager->priv->object_path == NULL); |
314 | 0 | g_assert (g_variant_is_object_path (g_value_get_string (value))); |
315 | 0 | manager->priv->object_path = g_value_dup_string (value); |
316 | 0 | break; |
317 | | |
318 | 0 | case PROP_NAME: |
319 | 0 | g_assert (manager->priv->name == NULL); |
320 | 0 | name = g_value_get_string (value); |
321 | 0 | g_assert (name == NULL || g_dbus_is_name (name)); |
322 | 0 | manager->priv->name = g_strdup (name); |
323 | 0 | break; |
324 | | |
325 | 0 | case PROP_FLAGS: |
326 | 0 | manager->priv->flags = g_value_get_flags (value); |
327 | 0 | break; |
328 | | |
329 | 0 | case PROP_GET_PROXY_TYPE_FUNC: |
330 | 0 | manager->priv->get_proxy_type_func = g_value_get_pointer (value); |
331 | 0 | break; |
332 | | |
333 | 0 | case PROP_GET_PROXY_TYPE_USER_DATA: |
334 | 0 | manager->priv->get_proxy_type_user_data = g_value_get_pointer (value); |
335 | 0 | break; |
336 | | |
337 | 0 | case PROP_GET_PROXY_TYPE_DESTROY_NOTIFY: |
338 | 0 | manager->priv->get_proxy_type_destroy_notify = g_value_get_pointer (value); |
339 | 0 | break; |
340 | | |
341 | 0 | default: |
342 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (manager, prop_id, pspec); |
343 | 0 | break; |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | | static void |
348 | | g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass) |
349 | 0 | { |
350 | 0 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
351 | |
|
352 | 0 | gobject_class->dispose = g_dbus_object_manager_client_dispose; |
353 | 0 | gobject_class->finalize = g_dbus_object_manager_client_finalize; |
354 | 0 | gobject_class->set_property = g_dbus_object_manager_client_set_property; |
355 | 0 | gobject_class->get_property = g_dbus_object_manager_client_get_property; |
356 | | |
357 | | /** |
358 | | * GDBusObjectManagerClient:connection: |
359 | | * |
360 | | * The #GDBusConnection to use. |
361 | | * |
362 | | * Since: 2.30 |
363 | | */ |
364 | 0 | g_object_class_install_property (gobject_class, |
365 | 0 | PROP_CONNECTION, |
366 | 0 | g_param_spec_object ("connection", NULL, NULL, |
367 | 0 | G_TYPE_DBUS_CONNECTION, |
368 | 0 | G_PARAM_READABLE | |
369 | 0 | G_PARAM_WRITABLE | |
370 | 0 | G_PARAM_CONSTRUCT_ONLY | |
371 | 0 | G_PARAM_STATIC_STRINGS)); |
372 | | |
373 | | /** |
374 | | * GDBusObjectManagerClient:bus-type: |
375 | | * |
376 | | * If this property is not %G_BUS_TYPE_NONE, then |
377 | | * #GDBusObjectManagerClient:connection must be %NULL and will be set to the |
378 | | * #GDBusConnection obtained by calling g_bus_get() with the value |
379 | | * of this property. |
380 | | * |
381 | | * Since: 2.30 |
382 | | */ |
383 | 0 | g_object_class_install_property (gobject_class, |
384 | 0 | PROP_BUS_TYPE, |
385 | 0 | g_param_spec_enum ("bus-type", NULL, NULL, |
386 | 0 | G_TYPE_BUS_TYPE, |
387 | 0 | G_BUS_TYPE_NONE, |
388 | 0 | G_PARAM_WRITABLE | |
389 | 0 | G_PARAM_CONSTRUCT_ONLY | |
390 | 0 | G_PARAM_STATIC_NAME | |
391 | 0 | G_PARAM_STATIC_BLURB | |
392 | 0 | G_PARAM_STATIC_NICK)); |
393 | | |
394 | | /** |
395 | | * GDBusObjectManagerClient:flags: |
396 | | * |
397 | | * Flags from the #GDBusObjectManagerClientFlags enumeration. |
398 | | * |
399 | | * Since: 2.30 |
400 | | */ |
401 | 0 | g_object_class_install_property (gobject_class, |
402 | 0 | PROP_FLAGS, |
403 | 0 | g_param_spec_flags ("flags", NULL, NULL, |
404 | 0 | G_TYPE_DBUS_OBJECT_MANAGER_CLIENT_FLAGS, |
405 | 0 | G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, |
406 | 0 | G_PARAM_READABLE | |
407 | 0 | G_PARAM_WRITABLE | |
408 | 0 | G_PARAM_CONSTRUCT_ONLY | |
409 | 0 | G_PARAM_STATIC_NAME | |
410 | 0 | G_PARAM_STATIC_BLURB | |
411 | 0 | G_PARAM_STATIC_NICK)); |
412 | | |
413 | | /** |
414 | | * GDBusObjectManagerClient:object-path: |
415 | | * |
416 | | * The object path the manager is for. |
417 | | * |
418 | | * Since: 2.30 |
419 | | */ |
420 | 0 | g_object_class_install_property (gobject_class, |
421 | 0 | PROP_OBJECT_PATH, |
422 | 0 | g_param_spec_string ("object-path", NULL, NULL, |
423 | 0 | NULL, |
424 | 0 | G_PARAM_READABLE | |
425 | 0 | G_PARAM_WRITABLE | |
426 | 0 | G_PARAM_CONSTRUCT_ONLY | |
427 | 0 | G_PARAM_STATIC_STRINGS)); |
428 | | |
429 | | /** |
430 | | * GDBusObjectManagerClient:name: |
431 | | * |
432 | | * The well-known name or unique name that the manager is for. |
433 | | * |
434 | | * Since: 2.30 |
435 | | */ |
436 | 0 | g_object_class_install_property (gobject_class, |
437 | 0 | PROP_NAME, |
438 | 0 | g_param_spec_string ("name", NULL, NULL, |
439 | 0 | NULL, |
440 | 0 | G_PARAM_READABLE | |
441 | 0 | G_PARAM_WRITABLE | |
442 | 0 | G_PARAM_CONSTRUCT_ONLY | |
443 | 0 | G_PARAM_STATIC_STRINGS)); |
444 | | |
445 | | /** |
446 | | * GDBusObjectManagerClient:name-owner: |
447 | | * |
448 | | * The unique name that owns #GDBusObjectManagerClient:name or %NULL if |
449 | | * no-one is currently owning the name. Connect to the |
450 | | * #GObject::notify signal to track changes to this property. |
451 | | * |
452 | | * Since: 2.30 |
453 | | */ |
454 | 0 | g_object_class_install_property (gobject_class, |
455 | 0 | PROP_NAME_OWNER, |
456 | 0 | g_param_spec_string ("name-owner", NULL, NULL, |
457 | 0 | NULL, |
458 | 0 | G_PARAM_READABLE | |
459 | 0 | G_PARAM_STATIC_STRINGS)); |
460 | | |
461 | | /** |
462 | | * GDBusObjectManagerClient:get-proxy-type-func: |
463 | | * |
464 | | * The #GDBusProxyTypeFunc to use when determining what #GType to |
465 | | * use for interface proxies or %NULL. |
466 | | * |
467 | | * Since: 2.30 |
468 | | */ |
469 | 0 | g_object_class_install_property (gobject_class, |
470 | 0 | PROP_GET_PROXY_TYPE_FUNC, |
471 | 0 | g_param_spec_pointer ("get-proxy-type-func", NULL, NULL, |
472 | 0 | G_PARAM_READABLE | |
473 | 0 | G_PARAM_WRITABLE | |
474 | 0 | G_PARAM_CONSTRUCT_ONLY | |
475 | 0 | G_PARAM_STATIC_STRINGS)); |
476 | | |
477 | | /** |
478 | | * GDBusObjectManagerClient:get-proxy-type-user-data: |
479 | | * |
480 | | * The #gpointer user_data to pass to #GDBusObjectManagerClient:get-proxy-type-func. |
481 | | * |
482 | | * Since: 2.30 |
483 | | */ |
484 | 0 | g_object_class_install_property (gobject_class, |
485 | 0 | PROP_GET_PROXY_TYPE_USER_DATA, |
486 | 0 | g_param_spec_pointer ("get-proxy-type-user-data", NULL, NULL, |
487 | 0 | G_PARAM_READABLE | |
488 | 0 | G_PARAM_WRITABLE | |
489 | 0 | G_PARAM_CONSTRUCT_ONLY | |
490 | 0 | G_PARAM_STATIC_STRINGS)); |
491 | | |
492 | | /** |
493 | | * GDBusObjectManagerClient:get-proxy-type-destroy-notify: |
494 | | * |
495 | | * A #GDestroyNotify for the #gpointer user_data in #GDBusObjectManagerClient:get-proxy-type-user-data. |
496 | | * |
497 | | * Since: 2.30 |
498 | | */ |
499 | 0 | g_object_class_install_property (gobject_class, |
500 | 0 | PROP_GET_PROXY_TYPE_DESTROY_NOTIFY, |
501 | 0 | g_param_spec_pointer ("get-proxy-type-destroy-notify", NULL, NULL, |
502 | 0 | G_PARAM_READABLE | |
503 | 0 | G_PARAM_WRITABLE | |
504 | 0 | G_PARAM_CONSTRUCT_ONLY | |
505 | 0 | G_PARAM_STATIC_STRINGS)); |
506 | | |
507 | | /** |
508 | | * GDBusObjectManagerClient::interface-proxy-signal: |
509 | | * @manager: The #GDBusObjectManagerClient emitting the signal. |
510 | | * @object_proxy: The #GDBusObjectProxy on which an interface is emitting a D-Bus signal. |
511 | | * @interface_proxy: The #GDBusProxy that is emitting a D-Bus signal. |
512 | | * @sender_name: The sender of the signal or NULL if the connection is not a bus connection. |
513 | | * @signal_name: The signal name. |
514 | | * @parameters: A #GVariant tuple with parameters for the signal. |
515 | | * |
516 | | * Emitted when a D-Bus signal is received on @interface_proxy. |
517 | | * |
518 | | * This signal exists purely as a convenience to avoid having to |
519 | | * connect signals to all interface proxies managed by @manager. |
520 | | * |
521 | | * This signal is emitted in the |
522 | | * [thread-default main context][g-main-context-push-thread-default] |
523 | | * that @manager was constructed in. |
524 | | * |
525 | | * Since: 2.30 |
526 | | */ |
527 | 0 | signals[INTERFACE_PROXY_SIGNAL_SIGNAL] = |
528 | 0 | g_signal_new (I_("interface-proxy-signal"), |
529 | 0 | G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, |
530 | 0 | G_SIGNAL_RUN_LAST, |
531 | 0 | G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_signal), |
532 | 0 | NULL, |
533 | 0 | NULL, |
534 | 0 | _g_cclosure_marshal_VOID__OBJECT_OBJECT_STRING_STRING_VARIANT, |
535 | 0 | G_TYPE_NONE, |
536 | 0 | 5, |
537 | 0 | G_TYPE_DBUS_OBJECT_PROXY, |
538 | 0 | G_TYPE_DBUS_PROXY, |
539 | 0 | G_TYPE_STRING, |
540 | 0 | G_TYPE_STRING, |
541 | 0 | G_TYPE_VARIANT); |
542 | 0 | g_signal_set_va_marshaller (signals[INTERFACE_PROXY_SIGNAL_SIGNAL], |
543 | 0 | G_TYPE_FROM_CLASS (klass), |
544 | 0 | _g_cclosure_marshal_VOID__OBJECT_OBJECT_STRING_STRING_VARIANTv); |
545 | | |
546 | | /** |
547 | | * GDBusObjectManagerClient::interface-proxy-properties-changed: |
548 | | * @manager: The #GDBusObjectManagerClient emitting the signal. |
549 | | * @object_proxy: The #GDBusObjectProxy on which an interface has properties that are changing. |
550 | | * @interface_proxy: The #GDBusProxy that has properties that are changing. |
551 | | * @changed_properties: A #GVariant containing the properties that changed (type: `a{sv}`). |
552 | | * @invalidated_properties: (array zero-terminated=1) (element-type utf8): A %NULL terminated |
553 | | * array of properties that were invalidated. |
554 | | * |
555 | | * Emitted when one or more D-Bus properties on proxy changes. The |
556 | | * local cache has already been updated when this signal fires. Note |
557 | | * that both @changed_properties and @invalidated_properties are |
558 | | * guaranteed to never be %NULL (either may be empty though). |
559 | | * |
560 | | * This signal exists purely as a convenience to avoid having to |
561 | | * connect signals to all interface proxies managed by @manager. |
562 | | * |
563 | | * This signal is emitted in the |
564 | | * [thread-default main context][g-main-context-push-thread-default] |
565 | | * that @manager was constructed in. |
566 | | * |
567 | | * Since: 2.30 |
568 | | */ |
569 | 0 | signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL] = |
570 | 0 | g_signal_new (I_("interface-proxy-properties-changed"), |
571 | 0 | G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, |
572 | 0 | G_SIGNAL_RUN_LAST, |
573 | 0 | G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_properties_changed), |
574 | 0 | NULL, |
575 | 0 | NULL, |
576 | 0 | _g_cclosure_marshal_VOID__OBJECT_OBJECT_VARIANT_BOXED, |
577 | 0 | G_TYPE_NONE, |
578 | 0 | 4, |
579 | 0 | G_TYPE_DBUS_OBJECT_PROXY, |
580 | 0 | G_TYPE_DBUS_PROXY, |
581 | 0 | G_TYPE_VARIANT, |
582 | 0 | G_TYPE_STRV); |
583 | 0 | g_signal_set_va_marshaller (signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL], |
584 | 0 | G_TYPE_FROM_CLASS (klass), |
585 | 0 | _g_cclosure_marshal_VOID__OBJECT_OBJECT_VARIANT_BOXEDv); |
586 | 0 | } |
587 | | |
588 | | static void |
589 | | g_dbus_object_manager_client_init (GDBusObjectManagerClient *manager) |
590 | 0 | { |
591 | 0 | manager->priv = g_dbus_object_manager_client_get_instance_private (manager); |
592 | 0 | g_mutex_init (&manager->priv->lock); |
593 | 0 | manager->priv->map_object_path_to_object_proxy = g_hash_table_new_full (g_str_hash, |
594 | 0 | g_str_equal, |
595 | 0 | g_free, |
596 | 0 | (GDestroyNotify) g_object_unref); |
597 | 0 | manager->priv->cancel = g_cancellable_new (); |
598 | 0 | } |
599 | | |
600 | | /* ---------------------------------------------------------------------------------------------------- */ |
601 | | |
602 | | /** |
603 | | * g_dbus_object_manager_client_new_sync: |
604 | | * @connection: A #GDBusConnection. |
605 | | * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. |
606 | | * @name: (nullable): The owner of the control object (unique or well-known name), or %NULL when not using a message bus connection. |
607 | | * @object_path: The object path of the control object. |
608 | | * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. |
609 | | * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. |
610 | | * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL. |
611 | | * @cancellable: (nullable): A #GCancellable or %NULL |
612 | | * @error: Return location for error or %NULL. |
613 | | * |
614 | | * Creates a new #GDBusObjectManagerClient object. |
615 | | * |
616 | | * This is a synchronous failable constructor - the calling thread is |
617 | | * blocked until a reply is received. See g_dbus_object_manager_client_new() |
618 | | * for the asynchronous version. |
619 | | * |
620 | | * Returns: (transfer full) (type GDBusObjectManagerClient): A |
621 | | * #GDBusObjectManagerClient object or %NULL if @error is set. Free |
622 | | * with g_object_unref(). |
623 | | * |
624 | | * Since: 2.30 |
625 | | */ |
626 | | GDBusObjectManager * |
627 | | g_dbus_object_manager_client_new_sync (GDBusConnection *connection, |
628 | | GDBusObjectManagerClientFlags flags, |
629 | | const gchar *name, |
630 | | const gchar *object_path, |
631 | | GDBusProxyTypeFunc get_proxy_type_func, |
632 | | gpointer get_proxy_type_user_data, |
633 | | GDestroyNotify get_proxy_type_destroy_notify, |
634 | | GCancellable *cancellable, |
635 | | GError **error) |
636 | 0 | { |
637 | 0 | GInitable *initable; |
638 | |
|
639 | 0 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); |
640 | 0 | g_return_val_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || |
641 | 0 | g_dbus_is_name (name), NULL); |
642 | 0 | g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); |
643 | 0 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
644 | | |
645 | 0 | initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, |
646 | 0 | cancellable, |
647 | 0 | error, |
648 | 0 | "connection", connection, |
649 | 0 | "flags", flags, |
650 | 0 | "name", name, |
651 | 0 | "object-path", object_path, |
652 | 0 | "get-proxy-type-func", get_proxy_type_func, |
653 | 0 | "get-proxy-type-user-data", get_proxy_type_user_data, |
654 | 0 | "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify, |
655 | 0 | NULL); |
656 | 0 | if (initable != NULL) |
657 | 0 | return G_DBUS_OBJECT_MANAGER (initable); |
658 | 0 | else |
659 | 0 | return NULL; |
660 | 0 | } |
661 | | |
662 | | /** |
663 | | * g_dbus_object_manager_client_new: |
664 | | * @connection: A #GDBusConnection. |
665 | | * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. |
666 | | * @name: The owner of the control object (unique or well-known name). |
667 | | * @object_path: The object path of the control object. |
668 | | * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. |
669 | | * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. |
670 | | * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL. |
671 | | * @cancellable: (nullable): A #GCancellable or %NULL |
672 | | * @callback: A #GAsyncReadyCallback to call when the request is satisfied. |
673 | | * @user_data: The data to pass to @callback. |
674 | | * |
675 | | * Asynchronously creates a new #GDBusObjectManagerClient object. |
676 | | * |
677 | | * This is an asynchronous failable constructor. When the result is |
678 | | * ready, @callback will be invoked in the |
679 | | * [thread-default main context][g-main-context-push-thread-default] |
680 | | * of the thread you are calling this method from. You can |
681 | | * then call g_dbus_object_manager_client_new_finish() to get the result. See |
682 | | * g_dbus_object_manager_client_new_sync() for the synchronous version. |
683 | | * |
684 | | * Since: 2.30 |
685 | | */ |
686 | | void |
687 | | g_dbus_object_manager_client_new (GDBusConnection *connection, |
688 | | GDBusObjectManagerClientFlags flags, |
689 | | const gchar *name, |
690 | | const gchar *object_path, |
691 | | GDBusProxyTypeFunc get_proxy_type_func, |
692 | | gpointer get_proxy_type_user_data, |
693 | | GDestroyNotify get_proxy_type_destroy_notify, |
694 | | GCancellable *cancellable, |
695 | | GAsyncReadyCallback callback, |
696 | | gpointer user_data) |
697 | 0 | { |
698 | 0 | g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); |
699 | 0 | g_return_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || |
700 | 0 | g_dbus_is_name (name)); |
701 | 0 | g_return_if_fail (g_variant_is_object_path (object_path)); |
702 | | |
703 | 0 | g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, |
704 | 0 | G_PRIORITY_DEFAULT, |
705 | 0 | cancellable, |
706 | 0 | callback, |
707 | 0 | user_data, |
708 | 0 | "connection", connection, |
709 | 0 | "flags", flags, |
710 | 0 | "name", name, |
711 | 0 | "object-path", object_path, |
712 | 0 | "get-proxy-type-func", get_proxy_type_func, |
713 | 0 | "get-proxy-type-user-data", get_proxy_type_user_data, |
714 | 0 | "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify, |
715 | 0 | NULL); |
716 | 0 | } |
717 | | |
718 | | /** |
719 | | * g_dbus_object_manager_client_new_finish: |
720 | | * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new(). |
721 | | * @error: Return location for error or %NULL. |
722 | | * |
723 | | * Finishes an operation started with g_dbus_object_manager_client_new(). |
724 | | * |
725 | | * Returns: (transfer full) (type GDBusObjectManagerClient): A |
726 | | * #GDBusObjectManagerClient object or %NULL if @error is set. Free |
727 | | * with g_object_unref(). |
728 | | * |
729 | | * Since: 2.30 |
730 | | */ |
731 | | GDBusObjectManager * |
732 | | g_dbus_object_manager_client_new_finish (GAsyncResult *res, |
733 | | GError **error) |
734 | 0 | { |
735 | 0 | GObject *object; |
736 | 0 | GObject *source_object; |
737 | |
|
738 | 0 | source_object = g_async_result_get_source_object (res); |
739 | 0 | g_assert (source_object != NULL); |
740 | | |
741 | 0 | object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), |
742 | 0 | res, |
743 | 0 | error); |
744 | 0 | g_object_unref (source_object); |
745 | |
|
746 | 0 | if (object != NULL) |
747 | 0 | return G_DBUS_OBJECT_MANAGER (object); |
748 | 0 | else |
749 | 0 | return NULL; |
750 | 0 | } |
751 | | |
752 | | /* ---------------------------------------------------------------------------------------------------- */ |
753 | | |
754 | | /** |
755 | | * g_dbus_object_manager_client_new_for_bus_sync: |
756 | | * @bus_type: A #GBusType. |
757 | | * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. |
758 | | * @name: The owner of the control object (unique or well-known name). |
759 | | * @object_path: The object path of the control object. |
760 | | * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. |
761 | | * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. |
762 | | * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL. |
763 | | * @cancellable: (nullable): A #GCancellable or %NULL |
764 | | * @error: Return location for error or %NULL. |
765 | | * |
766 | | * Like g_dbus_object_manager_client_new_sync() but takes a #GBusType instead |
767 | | * of a #GDBusConnection. |
768 | | * |
769 | | * This is a synchronous failable constructor - the calling thread is |
770 | | * blocked until a reply is received. See g_dbus_object_manager_client_new_for_bus() |
771 | | * for the asynchronous version. |
772 | | * |
773 | | * Returns: (transfer full) (type GDBusObjectManagerClient): A |
774 | | * #GDBusObjectManagerClient object or %NULL if @error is set. Free |
775 | | * with g_object_unref(). |
776 | | * |
777 | | * Since: 2.30 |
778 | | */ |
779 | | GDBusObjectManager * |
780 | | g_dbus_object_manager_client_new_for_bus_sync (GBusType bus_type, |
781 | | GDBusObjectManagerClientFlags flags, |
782 | | const gchar *name, |
783 | | const gchar *object_path, |
784 | | GDBusProxyTypeFunc get_proxy_type_func, |
785 | | gpointer get_proxy_type_user_data, |
786 | | GDestroyNotify get_proxy_type_destroy_notify, |
787 | | GCancellable *cancellable, |
788 | | GError **error) |
789 | 0 | { |
790 | 0 | GInitable *initable; |
791 | |
|
792 | 0 | g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, NULL); |
793 | 0 | g_return_val_if_fail (g_dbus_is_name (name), NULL); |
794 | 0 | g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); |
795 | 0 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
796 | | |
797 | 0 | initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, |
798 | 0 | cancellable, |
799 | 0 | error, |
800 | 0 | "bus-type", bus_type, |
801 | 0 | "flags", flags, |
802 | 0 | "name", name, |
803 | 0 | "object-path", object_path, |
804 | 0 | "get-proxy-type-func", get_proxy_type_func, |
805 | 0 | "get-proxy-type-user-data", get_proxy_type_user_data, |
806 | 0 | "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify, |
807 | 0 | NULL); |
808 | 0 | if (initable != NULL) |
809 | 0 | return G_DBUS_OBJECT_MANAGER (initable); |
810 | 0 | else |
811 | 0 | return NULL; |
812 | 0 | } |
813 | | |
814 | | /** |
815 | | * g_dbus_object_manager_client_new_for_bus: |
816 | | * @bus_type: A #GBusType. |
817 | | * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration. |
818 | | * @name: The owner of the control object (unique or well-known name). |
819 | | * @object_path: The object path of the control object. |
820 | | * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies. |
821 | | * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func. |
822 | | * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL. |
823 | | * @cancellable: (nullable): A #GCancellable or %NULL |
824 | | * @callback: A #GAsyncReadyCallback to call when the request is satisfied. |
825 | | * @user_data: The data to pass to @callback. |
826 | | * |
827 | | * Like g_dbus_object_manager_client_new() but takes a #GBusType instead of a |
828 | | * #GDBusConnection. |
829 | | * |
830 | | * This is an asynchronous failable constructor. When the result is |
831 | | * ready, @callback will be invoked in the |
832 | | * [thread-default main loop][g-main-context-push-thread-default] |
833 | | * of the thread you are calling this method from. You can |
834 | | * then call g_dbus_object_manager_client_new_for_bus_finish() to get the result. See |
835 | | * g_dbus_object_manager_client_new_for_bus_sync() for the synchronous version. |
836 | | * |
837 | | * Since: 2.30 |
838 | | */ |
839 | | void |
840 | | g_dbus_object_manager_client_new_for_bus (GBusType bus_type, |
841 | | GDBusObjectManagerClientFlags flags, |
842 | | const gchar *name, |
843 | | const gchar *object_path, |
844 | | GDBusProxyTypeFunc get_proxy_type_func, |
845 | | gpointer get_proxy_type_user_data, |
846 | | GDestroyNotify get_proxy_type_destroy_notify, |
847 | | GCancellable *cancellable, |
848 | | GAsyncReadyCallback callback, |
849 | | gpointer user_data) |
850 | 0 | { |
851 | 0 | g_return_if_fail (bus_type != G_BUS_TYPE_NONE); |
852 | 0 | g_return_if_fail (g_dbus_is_name (name)); |
853 | 0 | g_return_if_fail (g_variant_is_object_path (object_path)); |
854 | | |
855 | 0 | g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, |
856 | 0 | G_PRIORITY_DEFAULT, |
857 | 0 | cancellable, |
858 | 0 | callback, |
859 | 0 | user_data, |
860 | 0 | "bus-type", bus_type, |
861 | 0 | "flags", flags, |
862 | 0 | "name", name, |
863 | 0 | "object-path", object_path, |
864 | 0 | "get-proxy-type-func", get_proxy_type_func, |
865 | 0 | "get-proxy-type-user-data", get_proxy_type_user_data, |
866 | 0 | "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify, |
867 | 0 | NULL); |
868 | 0 | } |
869 | | |
870 | | /** |
871 | | * g_dbus_object_manager_client_new_for_bus_finish: |
872 | | * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new_for_bus(). |
873 | | * @error: Return location for error or %NULL. |
874 | | * |
875 | | * Finishes an operation started with g_dbus_object_manager_client_new_for_bus(). |
876 | | * |
877 | | * Returns: (transfer full) (type GDBusObjectManagerClient): A |
878 | | * #GDBusObjectManagerClient object or %NULL if @error is set. Free |
879 | | * with g_object_unref(). |
880 | | * |
881 | | * Since: 2.30 |
882 | | */ |
883 | | GDBusObjectManager * |
884 | | g_dbus_object_manager_client_new_for_bus_finish (GAsyncResult *res, |
885 | | GError **error) |
886 | 0 | { |
887 | 0 | GObject *object; |
888 | 0 | GObject *source_object; |
889 | |
|
890 | 0 | source_object = g_async_result_get_source_object (res); |
891 | 0 | g_assert (source_object != NULL); |
892 | | |
893 | 0 | object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), |
894 | 0 | res, |
895 | 0 | error); |
896 | 0 | g_object_unref (source_object); |
897 | |
|
898 | 0 | if (object != NULL) |
899 | 0 | return G_DBUS_OBJECT_MANAGER (object); |
900 | 0 | else |
901 | 0 | return NULL; |
902 | 0 | } |
903 | | |
904 | | /* ---------------------------------------------------------------------------------------------------- */ |
905 | | |
906 | | /** |
907 | | * g_dbus_object_manager_client_get_connection: |
908 | | * @manager: A #GDBusObjectManagerClient |
909 | | * |
910 | | * Gets the #GDBusConnection used by @manager. |
911 | | * |
912 | | * Returns: (transfer none): A #GDBusConnection object. Do not free, |
913 | | * the object belongs to @manager. |
914 | | * |
915 | | * Since: 2.30 |
916 | | */ |
917 | | GDBusConnection * |
918 | | g_dbus_object_manager_client_get_connection (GDBusObjectManagerClient *manager) |
919 | 0 | { |
920 | 0 | GDBusConnection *ret; |
921 | 0 | g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); |
922 | 0 | g_mutex_lock (&manager->priv->lock); |
923 | 0 | ret = manager->priv->connection; |
924 | 0 | g_mutex_unlock (&manager->priv->lock); |
925 | 0 | return ret; |
926 | 0 | } |
927 | | |
928 | | /** |
929 | | * g_dbus_object_manager_client_get_name: |
930 | | * @manager: A #GDBusObjectManagerClient |
931 | | * |
932 | | * Gets the name that @manager is for, or %NULL if not a message bus |
933 | | * connection. |
934 | | * |
935 | | * Returns: A unique or well-known name. Do not free, the string |
936 | | * belongs to @manager. |
937 | | * |
938 | | * Since: 2.30 |
939 | | */ |
940 | | const gchar * |
941 | | g_dbus_object_manager_client_get_name (GDBusObjectManagerClient *manager) |
942 | 0 | { |
943 | 0 | const gchar *ret; |
944 | 0 | g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); |
945 | 0 | g_mutex_lock (&manager->priv->lock); |
946 | 0 | ret = manager->priv->name; |
947 | 0 | g_mutex_unlock (&manager->priv->lock); |
948 | 0 | return ret; |
949 | 0 | } |
950 | | |
951 | | /** |
952 | | * g_dbus_object_manager_client_get_flags: |
953 | | * @manager: A #GDBusObjectManagerClient |
954 | | * |
955 | | * Gets the flags that @manager was constructed with. |
956 | | * |
957 | | * Returns: Zero of more flags from the #GDBusObjectManagerClientFlags |
958 | | * enumeration. |
959 | | * |
960 | | * Since: 2.30 |
961 | | */ |
962 | | GDBusObjectManagerClientFlags |
963 | | g_dbus_object_manager_client_get_flags (GDBusObjectManagerClient *manager) |
964 | 0 | { |
965 | 0 | GDBusObjectManagerClientFlags ret; |
966 | 0 | g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE); |
967 | 0 | g_mutex_lock (&manager->priv->lock); |
968 | 0 | ret = manager->priv->flags; |
969 | 0 | g_mutex_unlock (&manager->priv->lock); |
970 | 0 | return ret; |
971 | 0 | } |
972 | | |
973 | | /** |
974 | | * g_dbus_object_manager_client_get_name_owner: |
975 | | * @manager: A #GDBusObjectManagerClient. |
976 | | * |
977 | | * The unique name that owns the name that @manager is for or %NULL if |
978 | | * no-one currently owns that name. You can connect to the |
979 | | * #GObject::notify signal to track changes to the |
980 | | * #GDBusObjectManagerClient:name-owner property. |
981 | | * |
982 | | * Returns: (nullable): The name owner or %NULL if no name owner |
983 | | * exists. Free with g_free(). |
984 | | * |
985 | | * Since: 2.30 |
986 | | */ |
987 | | gchar * |
988 | | g_dbus_object_manager_client_get_name_owner (GDBusObjectManagerClient *manager) |
989 | 0 | { |
990 | 0 | gchar *ret; |
991 | 0 | g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); |
992 | 0 | g_mutex_lock (&manager->priv->lock); |
993 | 0 | ret = g_strdup (manager->priv->name_owner); |
994 | 0 | g_mutex_unlock (&manager->priv->lock); |
995 | 0 | return ret; |
996 | 0 | } |
997 | | |
998 | | /* ---------------------------------------------------------------------------------------------------- */ |
999 | | |
1000 | | /* signal handler for all objects we manage - we dispatch signals |
1001 | | * from here to the objects |
1002 | | */ |
1003 | | static void |
1004 | | signal_cb (GDBusConnection *connection, |
1005 | | const gchar *sender_name, |
1006 | | const gchar *object_path, |
1007 | | const gchar *interface_name, |
1008 | | const gchar *signal_name, |
1009 | | GVariant *parameters, |
1010 | | gpointer user_data) |
1011 | 0 | { |
1012 | 0 | GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (user_data); |
1013 | 0 | GDBusObjectProxy *object_proxy; |
1014 | 0 | GDBusInterface *interface; |
1015 | |
|
1016 | 0 | g_mutex_lock (&manager->priv->lock); |
1017 | 0 | object_proxy = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); |
1018 | 0 | if (object_proxy == NULL) |
1019 | 0 | { |
1020 | 0 | g_mutex_unlock (&manager->priv->lock); |
1021 | 0 | goto out; |
1022 | 0 | } |
1023 | 0 | g_object_ref (object_proxy); |
1024 | 0 | g_mutex_unlock (&manager->priv->lock); |
1025 | | |
1026 | | //g_debug ("yay, signal_cb %s %s: %s\n", signal_name, object_path, g_variant_print (parameters, TRUE)); |
1027 | |
|
1028 | 0 | g_object_ref (manager); |
1029 | 0 | if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0) |
1030 | 0 | { |
1031 | 0 | if (g_strcmp0 (signal_name, "PropertiesChanged") == 0) |
1032 | 0 | { |
1033 | 0 | const gchar *properties_interface_name; |
1034 | 0 | GVariant *changed_properties; |
1035 | 0 | const gchar **invalidated_properties; |
1036 | |
|
1037 | 0 | g_variant_get (parameters, |
1038 | 0 | "(&s@a{sv}^a&s)", |
1039 | 0 | &properties_interface_name, |
1040 | 0 | &changed_properties, |
1041 | 0 | &invalidated_properties); |
1042 | |
|
1043 | 0 | interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), properties_interface_name); |
1044 | 0 | if (interface != NULL) |
1045 | 0 | { |
1046 | 0 | GVariantIter property_iter; |
1047 | 0 | const gchar *property_name; |
1048 | 0 | GVariant *property_value; |
1049 | 0 | guint n; |
1050 | | |
1051 | | /* update caches... */ |
1052 | 0 | g_variant_iter_init (&property_iter, changed_properties); |
1053 | 0 | while (g_variant_iter_next (&property_iter, |
1054 | 0 | "{&sv}", |
1055 | 0 | &property_name, |
1056 | 0 | &property_value)) |
1057 | 0 | { |
1058 | 0 | g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface), |
1059 | 0 | property_name, |
1060 | 0 | property_value); |
1061 | 0 | g_variant_unref (property_value); |
1062 | 0 | } |
1063 | |
|
1064 | 0 | for (n = 0; invalidated_properties[n] != NULL; n++) |
1065 | 0 | { |
1066 | 0 | g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface), |
1067 | 0 | invalidated_properties[n], |
1068 | 0 | NULL); |
1069 | 0 | } |
1070 | | /* ... and then synthesize the signal */ |
1071 | 0 | g_signal_emit_by_name (interface, |
1072 | 0 | "g-properties-changed", |
1073 | 0 | changed_properties, |
1074 | 0 | invalidated_properties); |
1075 | 0 | g_signal_emit (manager, |
1076 | 0 | signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL], |
1077 | 0 | 0, |
1078 | 0 | object_proxy, |
1079 | 0 | interface, |
1080 | 0 | changed_properties, |
1081 | 0 | invalidated_properties); |
1082 | 0 | g_object_unref (interface); |
1083 | 0 | } |
1084 | 0 | g_variant_unref (changed_properties); |
1085 | 0 | g_free (invalidated_properties); |
1086 | 0 | } |
1087 | 0 | } |
1088 | 0 | else |
1089 | 0 | { |
1090 | | /* regular signal - just dispatch it */ |
1091 | 0 | interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), interface_name); |
1092 | 0 | if (interface != NULL) |
1093 | 0 | { |
1094 | 0 | g_signal_emit_by_name (interface, |
1095 | 0 | "g-signal", |
1096 | 0 | sender_name, |
1097 | 0 | signal_name, |
1098 | 0 | parameters); |
1099 | 0 | g_signal_emit (manager, |
1100 | 0 | signals[INTERFACE_PROXY_SIGNAL_SIGNAL], |
1101 | 0 | 0, |
1102 | 0 | object_proxy, |
1103 | 0 | interface, |
1104 | 0 | sender_name, |
1105 | 0 | signal_name, |
1106 | 0 | parameters); |
1107 | 0 | g_object_unref (interface); |
1108 | 0 | } |
1109 | 0 | } |
1110 | 0 | g_object_unref (manager); |
1111 | |
|
1112 | 0 | out: |
1113 | 0 | g_clear_object (&object_proxy); |
1114 | 0 | } |
1115 | | |
1116 | | static void |
1117 | | subscribe_signals (GDBusObjectManagerClient *manager, |
1118 | | const gchar *name_owner) |
1119 | 0 | { |
1120 | 0 | GError *error = NULL; |
1121 | 0 | GVariant *ret; |
1122 | |
|
1123 | 0 | g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager)); |
1124 | 0 | g_return_if_fail (manager->priv->signal_subscription_id == 0); |
1125 | 0 | g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner)); |
1126 | | |
1127 | 0 | if (name_owner != NULL) |
1128 | 0 | { |
1129 | | /* Only add path_namespace if it's non-'/'. This removes a no-op key from |
1130 | | * the match rule, and also works around a D-Bus bug where |
1131 | | * path_namespace='/' matches nothing in D-Bus versions < 1.6.18. |
1132 | | * |
1133 | | * See: https://bugs.freedesktop.org/show_bug.cgi?id=70799 */ |
1134 | 0 | if (g_str_equal (manager->priv->object_path, "/")) |
1135 | 0 | { |
1136 | 0 | manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s'", |
1137 | 0 | name_owner); |
1138 | 0 | } |
1139 | 0 | else |
1140 | 0 | { |
1141 | 0 | manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s',path_namespace='%s'", |
1142 | 0 | name_owner, manager->priv->object_path); |
1143 | 0 | } |
1144 | | |
1145 | | /* The bus daemon may not implement path_namespace so gracefully |
1146 | | * handle this by using a fallback triggered if @error is set. */ |
1147 | 0 | ret = g_dbus_connection_call_sync (manager->priv->connection, |
1148 | 0 | "org.freedesktop.DBus", |
1149 | 0 | "/org/freedesktop/DBus", |
1150 | 0 | "org.freedesktop.DBus", |
1151 | 0 | "AddMatch", |
1152 | 0 | g_variant_new ("(s)", |
1153 | 0 | manager->priv->match_rule), |
1154 | 0 | NULL, /* reply_type */ |
1155 | 0 | G_DBUS_CALL_FLAGS_NONE, |
1156 | 0 | -1, /* default timeout */ |
1157 | 0 | NULL, /* TODO: Cancellable */ |
1158 | 0 | &error); |
1159 | | |
1160 | | /* yay, bus daemon supports path_namespace */ |
1161 | 0 | if (ret != NULL) |
1162 | 0 | g_variant_unref (ret); |
1163 | 0 | } |
1164 | |
|
1165 | 0 | if (error == NULL) |
1166 | 0 | { |
1167 | | /* still need to ask GDBusConnection for the callbacks */ |
1168 | 0 | manager->priv->signal_subscription_id = |
1169 | 0 | g_dbus_connection_signal_subscribe (manager->priv->connection, |
1170 | 0 | name_owner, |
1171 | 0 | NULL, /* interface */ |
1172 | 0 | NULL, /* member */ |
1173 | 0 | NULL, /* path - TODO: really want wildcard support here */ |
1174 | 0 | NULL, /* arg0 */ |
1175 | 0 | G_DBUS_SIGNAL_FLAGS_NONE | |
1176 | 0 | G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, |
1177 | 0 | signal_cb, |
1178 | 0 | manager, |
1179 | 0 | NULL); /* user_data_free_func */ |
1180 | |
|
1181 | 0 | } |
1182 | 0 | else |
1183 | 0 | { |
1184 | | /* TODO: we could report this to the user |
1185 | | g_warning ("Message bus daemon does not support path_namespace: %s (%s %d)", |
1186 | | error->message, |
1187 | | g_quark_to_string (error->domain), |
1188 | | error->code); |
1189 | | */ |
1190 | |
|
1191 | 0 | g_error_free (error); |
1192 | | |
1193 | | /* no need to call RemoveMatch when done since it didn't work */ |
1194 | 0 | g_free (manager->priv->match_rule); |
1195 | 0 | manager->priv->match_rule = NULL; |
1196 | | |
1197 | | /* Fallback is to subscribe to *all* signals from the name owner which |
1198 | | * is rather wasteful. It's probably not a big practical problem because |
1199 | | * users typically want all objects that the name owner supplies. |
1200 | | */ |
1201 | 0 | manager->priv->signal_subscription_id = |
1202 | 0 | g_dbus_connection_signal_subscribe (manager->priv->connection, |
1203 | 0 | name_owner, |
1204 | 0 | NULL, /* interface */ |
1205 | 0 | NULL, /* member */ |
1206 | 0 | NULL, /* path - TODO: really want wildcard support here */ |
1207 | 0 | NULL, /* arg0 */ |
1208 | 0 | G_DBUS_SIGNAL_FLAGS_NONE, |
1209 | 0 | signal_cb, |
1210 | 0 | manager, |
1211 | 0 | NULL); /* user_data_free_func */ |
1212 | 0 | } |
1213 | 0 | } |
1214 | | |
1215 | | static void |
1216 | | maybe_unsubscribe_signals (GDBusObjectManagerClient *manager) |
1217 | 0 | { |
1218 | 0 | g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager)); |
1219 | | |
1220 | 0 | if (manager->priv->signal_subscription_id > 0) |
1221 | 0 | { |
1222 | 0 | g_dbus_connection_signal_unsubscribe (manager->priv->connection, |
1223 | 0 | manager->priv->signal_subscription_id); |
1224 | 0 | manager->priv->signal_subscription_id = 0; |
1225 | 0 | } |
1226 | |
|
1227 | 0 | if (manager->priv->match_rule != NULL) |
1228 | 0 | { |
1229 | | /* Since the AddMatch call succeeded this is guaranteed to not |
1230 | | * fail - therefore, don't bother checking the return value |
1231 | | */ |
1232 | 0 | g_dbus_connection_call (manager->priv->connection, |
1233 | 0 | "org.freedesktop.DBus", |
1234 | 0 | "/org/freedesktop/DBus", |
1235 | 0 | "org.freedesktop.DBus", |
1236 | 0 | "RemoveMatch", |
1237 | 0 | g_variant_new ("(s)", |
1238 | 0 | manager->priv->match_rule), |
1239 | 0 | NULL, /* reply_type */ |
1240 | 0 | G_DBUS_CALL_FLAGS_NONE, |
1241 | 0 | -1, /* default timeout */ |
1242 | 0 | NULL, /* GCancellable */ |
1243 | 0 | NULL, /* GAsyncReadyCallback */ |
1244 | 0 | NULL); /* user data */ |
1245 | 0 | g_free (manager->priv->match_rule); |
1246 | 0 | manager->priv->match_rule = NULL; |
1247 | 0 | } |
1248 | |
|
1249 | 0 | } |
1250 | | |
1251 | | /* ---------------------------------------------------------------------------------------------------- */ |
1252 | | |
1253 | | static GWeakRef * |
1254 | | weak_ref_new (GObject *object) |
1255 | 0 | { |
1256 | 0 | GWeakRef *weak_ref = g_new0 (GWeakRef, 1); |
1257 | 0 | g_weak_ref_init (weak_ref, object); |
1258 | 0 | return g_steal_pointer (&weak_ref); |
1259 | 0 | } |
1260 | | |
1261 | | static void |
1262 | | weak_ref_free (GWeakRef *weak_ref) |
1263 | 0 | { |
1264 | 0 | g_weak_ref_clear (weak_ref); |
1265 | 0 | g_free (weak_ref); |
1266 | 0 | } |
1267 | | |
1268 | | static void |
1269 | | on_get_managed_objects_finish (GObject *source, |
1270 | | GAsyncResult *result, |
1271 | | gpointer user_data) |
1272 | 0 | { |
1273 | |
|
1274 | 0 | GDBusProxy *proxy = G_DBUS_PROXY (source); |
1275 | 0 | GWeakRef *manager_weak = user_data; |
1276 | 0 | GDBusObjectManagerClient *manager; |
1277 | 0 | GError *error = NULL; |
1278 | 0 | GVariant *value = NULL; |
1279 | 0 | gchar *new_name_owner = NULL; |
1280 | |
|
1281 | 0 | value = g_dbus_proxy_call_finish (proxy, result, &error); |
1282 | |
|
1283 | 0 | manager = G_DBUS_OBJECT_MANAGER_CLIENT (g_weak_ref_get (manager_weak)); |
1284 | | /* Manager got disposed, nothing to do */ |
1285 | 0 | if (manager == NULL) |
1286 | 0 | { |
1287 | 0 | goto out; |
1288 | 0 | } |
1289 | | |
1290 | 0 | new_name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy); |
1291 | 0 | if (value == NULL) |
1292 | 0 | { |
1293 | 0 | maybe_unsubscribe_signals (manager); |
1294 | 0 | if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
1295 | 0 | { |
1296 | 0 | g_warning ("Error calling GetManagedObjects() when name owner %s for name %s came back: %s", |
1297 | 0 | new_name_owner, |
1298 | 0 | manager->priv->name, |
1299 | 0 | error->message); |
1300 | 0 | } |
1301 | 0 | } |
1302 | 0 | else |
1303 | 0 | { |
1304 | 0 | process_get_all_result (manager, value, new_name_owner); |
1305 | 0 | } |
1306 | | |
1307 | | /* do the :name-owner notify *AFTER* emitting ::object-proxy-added signals - this |
1308 | | * way the user knows that the signals were emitted because the name owner came back |
1309 | | */ |
1310 | 0 | g_mutex_lock (&manager->priv->lock); |
1311 | 0 | manager->priv->name_owner = g_steal_pointer (&new_name_owner); |
1312 | 0 | g_mutex_unlock (&manager->priv->lock); |
1313 | 0 | g_object_notify (G_OBJECT (manager), "name-owner"); |
1314 | |
|
1315 | 0 | g_object_unref (manager); |
1316 | 0 | out: |
1317 | 0 | g_clear_error (&error); |
1318 | 0 | g_clear_pointer (&value, g_variant_unref); |
1319 | 0 | weak_ref_free (manager_weak); |
1320 | 0 | } |
1321 | | |
1322 | | static void |
1323 | | on_notify_g_name_owner (GObject *object, |
1324 | | GParamSpec *pspec, |
1325 | | gpointer user_data) |
1326 | 0 | { |
1327 | 0 | GWeakRef *manager_weak = user_data; |
1328 | 0 | GDBusObjectManagerClient *manager = NULL; |
1329 | 0 | gchar *old_name_owner; |
1330 | 0 | gchar *new_name_owner; |
1331 | |
|
1332 | 0 | manager = G_DBUS_OBJECT_MANAGER_CLIENT (g_weak_ref_get (manager_weak)); |
1333 | 0 | if (manager == NULL) |
1334 | 0 | return; |
1335 | | |
1336 | 0 | g_mutex_lock (&manager->priv->lock); |
1337 | 0 | old_name_owner = manager->priv->name_owner; |
1338 | 0 | new_name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy); |
1339 | 0 | manager->priv->name_owner = NULL; |
1340 | |
|
1341 | 0 | if (g_strcmp0 (old_name_owner, new_name_owner) != 0) |
1342 | 0 | { |
1343 | 0 | GPtrArray *proxies; |
1344 | | |
1345 | | /* remote manager changed; nuke all local proxies */ |
1346 | 0 | proxies = g_hash_table_steal_all_values ( |
1347 | 0 | manager->priv->map_object_path_to_object_proxy); |
1348 | |
|
1349 | 0 | g_mutex_unlock (&manager->priv->lock); |
1350 | | |
1351 | | /* do the :name-owner notify with a NULL name - this way the user knows |
1352 | | * the ::object-proxy-removed following is because the name owner went |
1353 | | * away |
1354 | | */ |
1355 | 0 | g_object_notify (G_OBJECT (manager), "name-owner"); |
1356 | |
|
1357 | 0 | for (guint i = 0; i < proxies->len; ++i) |
1358 | 0 | { |
1359 | 0 | GDBusObjectProxy *object_proxy = |
1360 | 0 | G_DBUS_OBJECT_PROXY (g_ptr_array_index (proxies, i)); |
1361 | 0 | g_signal_emit_by_name (manager, "object-removed", object_proxy); |
1362 | 0 | } |
1363 | 0 | g_clear_pointer (&proxies, g_ptr_array_unref); |
1364 | | |
1365 | | /* nuke local filter */ |
1366 | 0 | maybe_unsubscribe_signals (manager); |
1367 | 0 | } |
1368 | 0 | else |
1369 | 0 | { |
1370 | 0 | g_mutex_unlock (&manager->priv->lock); |
1371 | 0 | } |
1372 | |
|
1373 | 0 | if (new_name_owner != NULL) |
1374 | 0 | { |
1375 | | //g_debug ("repopulating for %s", new_name_owner); |
1376 | |
|
1377 | 0 | subscribe_signals (manager, |
1378 | 0 | new_name_owner); |
1379 | 0 | g_dbus_proxy_call (manager->priv->control_proxy, |
1380 | 0 | "GetManagedObjects", |
1381 | 0 | NULL, /* parameters */ |
1382 | 0 | G_DBUS_CALL_FLAGS_NONE, |
1383 | 0 | -1, |
1384 | 0 | manager->priv->cancel, |
1385 | 0 | on_get_managed_objects_finish, |
1386 | 0 | weak_ref_new (G_OBJECT (manager))); |
1387 | 0 | } |
1388 | 0 | g_free (new_name_owner); |
1389 | 0 | g_free (old_name_owner); |
1390 | 0 | g_object_unref (manager); |
1391 | 0 | } |
1392 | | |
1393 | | static gboolean |
1394 | | initable_init (GInitable *initable, |
1395 | | GCancellable *cancellable, |
1396 | | GError **error) |
1397 | 0 | { |
1398 | 0 | GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (initable); |
1399 | 0 | gboolean ret; |
1400 | 0 | GVariant *value; |
1401 | 0 | GDBusProxyFlags proxy_flags; |
1402 | |
|
1403 | 0 | ret = FALSE; |
1404 | |
|
1405 | 0 | if (manager->priv->bus_type != G_BUS_TYPE_NONE) |
1406 | 0 | { |
1407 | 0 | g_assert (manager->priv->connection == NULL); |
1408 | 0 | manager->priv->connection = g_bus_get_sync (manager->priv->bus_type, cancellable, error); |
1409 | 0 | if (manager->priv->connection == NULL) |
1410 | 0 | goto out; |
1411 | 0 | } |
1412 | | |
1413 | 0 | proxy_flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; |
1414 | 0 | if (manager->priv->flags & G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START) |
1415 | 0 | proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START; |
1416 | |
|
1417 | 0 | manager->priv->control_proxy = g_dbus_proxy_new_sync (manager->priv->connection, |
1418 | 0 | proxy_flags, |
1419 | 0 | NULL, /* GDBusInterfaceInfo* */ |
1420 | 0 | manager->priv->name, |
1421 | 0 | manager->priv->object_path, |
1422 | 0 | "org.freedesktop.DBus.ObjectManager", |
1423 | 0 | cancellable, |
1424 | 0 | error); |
1425 | 0 | if (manager->priv->control_proxy == NULL) |
1426 | 0 | goto out; |
1427 | | |
1428 | | /* Use weak refs here. The @control_proxy will emit its signals in the current |
1429 | | * #GMainContext (since we constructed it just above). However, the user may |
1430 | | * drop the last external reference to this #GDBusObjectManagerClient in |
1431 | | * another thread between a signal being emitted and scheduled in an idle |
1432 | | * callback in this #GMainContext, and that idle callback being invoked. We |
1433 | | * can’t use a strong reference here, as there’s no |
1434 | | * g_dbus_object_manager_client_disconnect() (or similar) method to tell us |
1435 | | * when the last external reference to this object has been dropped, so we |
1436 | | * can’t break a strong reference count cycle. So use weak refs. */ |
1437 | 0 | manager->priv->name_owner_signal_id = |
1438 | 0 | g_signal_connect_data (G_OBJECT (manager->priv->control_proxy), |
1439 | 0 | "notify::g-name-owner", |
1440 | 0 | G_CALLBACK (on_notify_g_name_owner), |
1441 | 0 | weak_ref_new (G_OBJECT (manager)), |
1442 | 0 | (GClosureNotify) weak_ref_free, |
1443 | 0 | G_CONNECT_DEFAULT); |
1444 | |
|
1445 | 0 | manager->priv->signal_signal_id = |
1446 | 0 | g_signal_connect_data (manager->priv->control_proxy, |
1447 | 0 | "g-signal", |
1448 | 0 | G_CALLBACK (on_control_proxy_g_signal), |
1449 | 0 | weak_ref_new (G_OBJECT (manager)), |
1450 | 0 | (GClosureNotify) weak_ref_free, |
1451 | 0 | G_CONNECT_DEFAULT); |
1452 | |
|
1453 | 0 | manager->priv->name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy); |
1454 | 0 | if (manager->priv->name_owner == NULL && manager->priv->name != NULL) |
1455 | 0 | { |
1456 | | /* it's perfectly fine if there's no name owner.. we're just going to |
1457 | | * wait until one is ready |
1458 | | */ |
1459 | 0 | } |
1460 | 0 | else |
1461 | 0 | { |
1462 | | /* yay, we can get the objects */ |
1463 | 0 | subscribe_signals (manager, |
1464 | 0 | manager->priv->name_owner); |
1465 | 0 | value = g_dbus_proxy_call_sync (manager->priv->control_proxy, |
1466 | 0 | "GetManagedObjects", |
1467 | 0 | NULL, /* parameters */ |
1468 | 0 | G_DBUS_CALL_FLAGS_NONE, |
1469 | 0 | -1, |
1470 | 0 | cancellable, |
1471 | 0 | error); |
1472 | 0 | if (value == NULL) |
1473 | 0 | { |
1474 | 0 | maybe_unsubscribe_signals (manager); |
1475 | |
|
1476 | 0 | g_warn_if_fail (manager->priv->signal_signal_id != 0); |
1477 | 0 | g_signal_handler_disconnect (manager->priv->control_proxy, |
1478 | 0 | manager->priv->signal_signal_id); |
1479 | 0 | manager->priv->signal_signal_id = 0; |
1480 | |
|
1481 | 0 | g_warn_if_fail (manager->priv->name_owner_signal_id != 0); |
1482 | 0 | g_signal_handler_disconnect (manager->priv->control_proxy, |
1483 | 0 | manager->priv->name_owner_signal_id); |
1484 | 0 | manager->priv->name_owner_signal_id = 0; |
1485 | |
|
1486 | 0 | g_object_unref (manager->priv->control_proxy); |
1487 | 0 | manager->priv->control_proxy = NULL; |
1488 | |
|
1489 | 0 | goto out; |
1490 | 0 | } |
1491 | | |
1492 | 0 | process_get_all_result (manager, value, manager->priv->name_owner); |
1493 | 0 | g_variant_unref (value); |
1494 | 0 | } |
1495 | | |
1496 | 0 | ret = TRUE; |
1497 | |
|
1498 | 0 | out: |
1499 | 0 | return ret; |
1500 | 0 | } |
1501 | | |
1502 | | static void |
1503 | | initable_iface_init (GInitableIface *initable_iface) |
1504 | 0 | { |
1505 | 0 | initable_iface->init = initable_init; |
1506 | 0 | } |
1507 | | |
1508 | | static void |
1509 | | async_initable_iface_init (GAsyncInitableIface *async_initable_iface) |
1510 | 0 | { |
1511 | | /* for now, just use default: run GInitable code in thread */ |
1512 | 0 | } |
1513 | | |
1514 | | /* ---------------------------------------------------------------------------------------------------- */ |
1515 | | |
1516 | | static void |
1517 | | add_interfaces (GDBusObjectManagerClient *manager, |
1518 | | const gchar *object_path, |
1519 | | GVariant *ifaces_and_properties, |
1520 | | const gchar *name_owner) |
1521 | 0 | { |
1522 | 0 | GDBusObjectProxy *op; |
1523 | 0 | gboolean added; |
1524 | 0 | GVariantIter iter; |
1525 | 0 | const gchar *interface_name; |
1526 | 0 | GVariant *properties; |
1527 | 0 | GList *interface_added_signals, *l; |
1528 | 0 | GDBusProxy *interface_proxy; |
1529 | |
|
1530 | 0 | g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner)); |
1531 | | |
1532 | 0 | g_mutex_lock (&manager->priv->lock); |
1533 | |
|
1534 | 0 | interface_added_signals = NULL; |
1535 | 0 | added = FALSE; |
1536 | |
|
1537 | 0 | op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); |
1538 | 0 | if (op == NULL) |
1539 | 0 | { |
1540 | 0 | GType object_proxy_type; |
1541 | 0 | if (manager->priv->get_proxy_type_func != NULL) |
1542 | 0 | { |
1543 | 0 | object_proxy_type = manager->priv->get_proxy_type_func (manager, |
1544 | 0 | object_path, |
1545 | 0 | NULL, |
1546 | 0 | manager->priv->get_proxy_type_user_data); |
1547 | 0 | g_warn_if_fail (g_type_is_a (object_proxy_type, G_TYPE_DBUS_OBJECT_PROXY)); |
1548 | 0 | } |
1549 | 0 | else |
1550 | 0 | { |
1551 | 0 | object_proxy_type = G_TYPE_DBUS_OBJECT_PROXY; |
1552 | 0 | } |
1553 | 0 | op = g_object_new (object_proxy_type, |
1554 | 0 | "g-connection", manager->priv->connection, |
1555 | 0 | "g-object-path", object_path, |
1556 | 0 | NULL); |
1557 | 0 | added = TRUE; |
1558 | 0 | } |
1559 | 0 | g_object_ref (op); |
1560 | |
|
1561 | 0 | g_variant_iter_init (&iter, ifaces_and_properties); |
1562 | 0 | while (g_variant_iter_next (&iter, |
1563 | 0 | "{&s@a{sv}}", |
1564 | 0 | &interface_name, |
1565 | 0 | &properties)) |
1566 | 0 | { |
1567 | 0 | GError *error; |
1568 | 0 | GType interface_proxy_type; |
1569 | |
|
1570 | 0 | if (manager->priv->get_proxy_type_func != NULL) |
1571 | 0 | { |
1572 | 0 | interface_proxy_type = manager->priv->get_proxy_type_func (manager, |
1573 | 0 | object_path, |
1574 | 0 | interface_name, |
1575 | 0 | manager->priv->get_proxy_type_user_data); |
1576 | 0 | g_warn_if_fail (g_type_is_a (interface_proxy_type, G_TYPE_DBUS_PROXY)); |
1577 | 0 | } |
1578 | 0 | else |
1579 | 0 | { |
1580 | 0 | interface_proxy_type = G_TYPE_DBUS_PROXY; |
1581 | 0 | } |
1582 | | |
1583 | | /* this is fine - there is no blocking IO because we pass DO_NOT_LOAD_PROPERTIES and |
1584 | | * DO_NOT_CONNECT_SIGNALS and use a unique name |
1585 | | */ |
1586 | 0 | error = NULL; |
1587 | 0 | interface_proxy = g_initable_new (interface_proxy_type, |
1588 | 0 | NULL, /* GCancellable */ |
1589 | 0 | &error, |
1590 | 0 | "g-connection", manager->priv->connection, |
1591 | 0 | "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | |
1592 | 0 | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, |
1593 | 0 | "g-name", name_owner, |
1594 | 0 | "g-object-path", object_path, |
1595 | 0 | "g-interface-name", interface_name, |
1596 | 0 | NULL); |
1597 | 0 | if (interface_proxy == NULL) |
1598 | 0 | { |
1599 | 0 | g_warning ("%s: Error constructing proxy for path %s and interface %s: %s", |
1600 | 0 | G_STRLOC, |
1601 | 0 | object_path, |
1602 | 0 | interface_name, |
1603 | 0 | error->message); |
1604 | 0 | g_error_free (error); |
1605 | 0 | } |
1606 | 0 | else |
1607 | 0 | { |
1608 | 0 | GVariantIter property_iter; |
1609 | 0 | const gchar *property_name; |
1610 | 0 | GVariant *property_value; |
1611 | | |
1612 | | /* associate the interface proxy with the object */ |
1613 | 0 | g_dbus_interface_set_object (G_DBUS_INTERFACE (interface_proxy), |
1614 | 0 | G_DBUS_OBJECT (op)); |
1615 | |
|
1616 | 0 | g_variant_iter_init (&property_iter, properties); |
1617 | 0 | while (g_variant_iter_next (&property_iter, |
1618 | 0 | "{&sv}", |
1619 | 0 | &property_name, |
1620 | 0 | &property_value)) |
1621 | 0 | { |
1622 | 0 | g_dbus_proxy_set_cached_property (interface_proxy, |
1623 | 0 | property_name, |
1624 | 0 | property_value); |
1625 | 0 | g_variant_unref (property_value); |
1626 | 0 | } |
1627 | |
|
1628 | 0 | _g_dbus_object_proxy_add_interface (op, interface_proxy); |
1629 | 0 | if (!added) |
1630 | 0 | interface_added_signals = g_list_append (interface_added_signals, g_object_ref (interface_proxy)); |
1631 | 0 | g_object_unref (interface_proxy); |
1632 | 0 | } |
1633 | 0 | g_variant_unref (properties); |
1634 | 0 | } |
1635 | |
|
1636 | 0 | if (added) |
1637 | 0 | { |
1638 | 0 | g_hash_table_insert (manager->priv->map_object_path_to_object_proxy, |
1639 | 0 | g_strdup (object_path), |
1640 | 0 | op); |
1641 | 0 | } |
1642 | |
|
1643 | 0 | g_mutex_unlock (&manager->priv->lock); |
1644 | | |
1645 | | /* now that we don't hold the lock any more, emit signals */ |
1646 | 0 | g_object_ref (manager); |
1647 | 0 | for (l = interface_added_signals; l != NULL; l = l->next) |
1648 | 0 | { |
1649 | 0 | interface_proxy = G_DBUS_PROXY (l->data); |
1650 | 0 | g_signal_emit_by_name (manager, "interface-added", op, interface_proxy); |
1651 | 0 | g_object_unref (interface_proxy); |
1652 | 0 | } |
1653 | 0 | g_list_free (interface_added_signals); |
1654 | |
|
1655 | 0 | if (added) |
1656 | 0 | g_signal_emit_by_name (manager, "object-added", op); |
1657 | |
|
1658 | 0 | g_object_unref (manager); |
1659 | 0 | g_object_unref (op); |
1660 | 0 | } |
1661 | | |
1662 | | static void |
1663 | | remove_interfaces (GDBusObjectManagerClient *manager, |
1664 | | const gchar *object_path, |
1665 | | const gchar *const *interface_names) |
1666 | 0 | { |
1667 | 0 | GDBusObjectProxy *op; |
1668 | 0 | GList *interfaces; |
1669 | 0 | guint n; |
1670 | 0 | guint num_interfaces; |
1671 | 0 | guint num_interfaces_to_remove; |
1672 | |
|
1673 | 0 | g_mutex_lock (&manager->priv->lock); |
1674 | |
|
1675 | 0 | op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); |
1676 | 0 | if (op == NULL) |
1677 | 0 | { |
1678 | 0 | g_debug ("%s: Processing InterfaceRemoved signal for path %s but no object proxy exists", |
1679 | 0 | G_STRLOC, |
1680 | 0 | object_path); |
1681 | 0 | g_mutex_unlock (&manager->priv->lock); |
1682 | 0 | return; |
1683 | 0 | } |
1684 | | |
1685 | 0 | interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (op)); |
1686 | 0 | num_interfaces = g_list_length (interfaces); |
1687 | 0 | g_list_free_full (interfaces, g_object_unref); |
1688 | |
|
1689 | 0 | num_interfaces_to_remove = g_strv_length ((gchar **) interface_names); |
1690 | | |
1691 | | /* see if we are going to completety remove the object */ |
1692 | 0 | g_object_ref (manager); |
1693 | 0 | if (num_interfaces_to_remove == num_interfaces) |
1694 | 0 | { |
1695 | 0 | g_object_ref (op); |
1696 | 0 | g_warn_if_fail (g_hash_table_remove (manager->priv->map_object_path_to_object_proxy, object_path)); |
1697 | 0 | g_mutex_unlock (&manager->priv->lock); |
1698 | 0 | g_signal_emit_by_name (manager, "object-removed", op); |
1699 | 0 | g_object_unref (op); |
1700 | 0 | } |
1701 | 0 | else |
1702 | 0 | { |
1703 | 0 | g_object_ref (op); |
1704 | 0 | g_mutex_unlock (&manager->priv->lock); |
1705 | 0 | for (n = 0; interface_names != NULL && interface_names[n] != NULL; n++) |
1706 | 0 | { |
1707 | 0 | GDBusInterface *interface; |
1708 | 0 | interface = g_dbus_object_get_interface (G_DBUS_OBJECT (op), interface_names[n]); |
1709 | 0 | _g_dbus_object_proxy_remove_interface (op, interface_names[n]); |
1710 | 0 | if (interface != NULL) |
1711 | 0 | { |
1712 | 0 | g_signal_emit_by_name (manager, "interface-removed", op, interface); |
1713 | 0 | g_object_unref (interface); |
1714 | 0 | } |
1715 | 0 | } |
1716 | 0 | g_object_unref (op); |
1717 | 0 | } |
1718 | 0 | g_object_unref (manager); |
1719 | 0 | } |
1720 | | |
1721 | | static void |
1722 | | process_get_all_result (GDBusObjectManagerClient *manager, |
1723 | | GVariant *value, |
1724 | | const gchar *name_owner) |
1725 | 0 | { |
1726 | 0 | GVariant *arg0; |
1727 | 0 | const gchar *object_path; |
1728 | 0 | GVariant *ifaces_and_properties; |
1729 | 0 | GVariantIter iter; |
1730 | |
|
1731 | 0 | g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner)); |
1732 | | |
1733 | 0 | arg0 = g_variant_get_child_value (value, 0); |
1734 | 0 | g_variant_iter_init (&iter, arg0); |
1735 | 0 | while (g_variant_iter_next (&iter, |
1736 | 0 | "{&o@a{sa{sv}}}", |
1737 | 0 | &object_path, |
1738 | 0 | &ifaces_and_properties)) |
1739 | 0 | { |
1740 | 0 | add_interfaces (manager, object_path, ifaces_and_properties, name_owner); |
1741 | 0 | g_variant_unref (ifaces_and_properties); |
1742 | 0 | } |
1743 | 0 | g_variant_unref (arg0); |
1744 | 0 | } |
1745 | | |
1746 | | static void |
1747 | | on_control_proxy_g_signal (GDBusProxy *proxy, |
1748 | | const gchar *sender_name, |
1749 | | const gchar *signal_name, |
1750 | | GVariant *parameters, |
1751 | | gpointer user_data) |
1752 | 0 | { |
1753 | 0 | GWeakRef *manager_weak = user_data; |
1754 | 0 | GDBusObjectManagerClient *manager = NULL; |
1755 | 0 | const gchar *object_path; |
1756 | |
|
1757 | 0 | manager = G_DBUS_OBJECT_MANAGER_CLIENT (g_weak_ref_get (manager_weak)); |
1758 | 0 | if (manager == NULL) |
1759 | 0 | return; |
1760 | | |
1761 | | //g_debug ("yay, g_signal %s: %s\n", signal_name, g_variant_print (parameters, TRUE)); |
1762 | | |
1763 | 0 | if (g_strcmp0 (signal_name, "InterfacesAdded") == 0) |
1764 | 0 | { |
1765 | 0 | GVariant *ifaces_and_properties; |
1766 | 0 | g_variant_get (parameters, |
1767 | 0 | "(&o@a{sa{sv}})", |
1768 | 0 | &object_path, |
1769 | 0 | &ifaces_and_properties); |
1770 | 0 | add_interfaces (manager, object_path, ifaces_and_properties, manager->priv->name_owner); |
1771 | 0 | g_variant_unref (ifaces_and_properties); |
1772 | 0 | } |
1773 | 0 | else if (g_strcmp0 (signal_name, "InterfacesRemoved") == 0) |
1774 | 0 | { |
1775 | 0 | const gchar **ifaces; |
1776 | 0 | g_variant_get (parameters, |
1777 | 0 | "(&o^a&s)", |
1778 | 0 | &object_path, |
1779 | 0 | &ifaces); |
1780 | 0 | remove_interfaces (manager, object_path, ifaces); |
1781 | 0 | g_free (ifaces); |
1782 | 0 | } |
1783 | |
|
1784 | 0 | g_object_unref (manager); |
1785 | 0 | } |
1786 | | |
1787 | | /* ---------------------------------------------------------------------------------------------------- */ |
1788 | | |
1789 | | static const gchar * |
1790 | | g_dbus_object_manager_client_get_object_path (GDBusObjectManager *_manager) |
1791 | 0 | { |
1792 | 0 | GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager); |
1793 | 0 | return manager->priv->object_path; |
1794 | 0 | } |
1795 | | |
1796 | | static GDBusObject * |
1797 | | g_dbus_object_manager_client_get_object (GDBusObjectManager *_manager, |
1798 | | const gchar *object_path) |
1799 | 0 | { |
1800 | 0 | GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager); |
1801 | 0 | GDBusObject *ret; |
1802 | |
|
1803 | 0 | g_mutex_lock (&manager->priv->lock); |
1804 | 0 | ret = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path); |
1805 | 0 | if (ret != NULL) |
1806 | 0 | g_object_ref (ret); |
1807 | 0 | g_mutex_unlock (&manager->priv->lock); |
1808 | 0 | return ret; |
1809 | 0 | } |
1810 | | |
1811 | | static GDBusInterface * |
1812 | | g_dbus_object_manager_client_get_interface (GDBusObjectManager *_manager, |
1813 | | const gchar *object_path, |
1814 | | const gchar *interface_name) |
1815 | 0 | { |
1816 | 0 | GDBusInterface *ret; |
1817 | 0 | GDBusObject *object; |
1818 | |
|
1819 | 0 | ret = NULL; |
1820 | |
|
1821 | 0 | object = g_dbus_object_manager_get_object (_manager, object_path); |
1822 | 0 | if (object == NULL) |
1823 | 0 | goto out; |
1824 | | |
1825 | 0 | ret = g_dbus_object_get_interface (object, interface_name); |
1826 | 0 | g_object_unref (object); |
1827 | |
|
1828 | 0 | out: |
1829 | 0 | return ret; |
1830 | 0 | } |
1831 | | |
1832 | | static GList * |
1833 | | g_dbus_object_manager_client_get_objects (GDBusObjectManager *_manager) |
1834 | 0 | { |
1835 | 0 | GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager); |
1836 | 0 | GList *ret; |
1837 | |
|
1838 | 0 | g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL); |
1839 | | |
1840 | 0 | g_mutex_lock (&manager->priv->lock); |
1841 | 0 | ret = g_hash_table_get_values (manager->priv->map_object_path_to_object_proxy); |
1842 | 0 | g_list_foreach (ret, (GFunc) g_object_ref, NULL); |
1843 | 0 | g_mutex_unlock (&manager->priv->lock); |
1844 | |
|
1845 | 0 | return ret; |
1846 | 0 | } |
1847 | | |
1848 | | |
1849 | | static void |
1850 | | dbus_object_manager_interface_init (GDBusObjectManagerIface *iface) |
1851 | 0 | { |
1852 | 0 | iface->get_object_path = g_dbus_object_manager_client_get_object_path; |
1853 | 0 | iface->get_objects = g_dbus_object_manager_client_get_objects; |
1854 | 0 | iface->get_object = g_dbus_object_manager_client_get_object; |
1855 | 0 | iface->get_interface = g_dbus_object_manager_client_get_interface; |
1856 | 0 | } |
1857 | | |
1858 | | /* ---------------------------------------------------------------------------------------------------- */ |