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