2 * Copyright © 2010 Codethink Limited
3 * Copyright © 2011 Canonical Limited
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; either version 2 of the licence or (at
8 * your option) any later version.
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.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Authors: Ryan Lortie <desrt@desrt.ca>
25 #include "gsimpleasyncresult.h"
26 #include "gdbusactiongroup.h"
27 #include "gdbusconnection.h"
28 #include "gasyncinitable.h"
29 #include "gactiongroup.h"
33 * SECTION:gdbusactiongroup
34 * @title: GDBusActionGroup
35 * @short_description: A D-Bus GActionGroup implementation
36 * @see_also: <link linkend="gio-GActionGroup-exporter">GActionGroup exporter</link>
38 * #GDBusActionGroup is an implementation of the #GActionGroup
39 * interface that can be used as a proxy for an action group
40 * that is exported over D-Bus with g_action_group_dbus_export_start().
43 struct _GDBusActionGroup
45 GObject parent_instance
;
47 GDBusConnection
*connection
;
50 guint subscription_id
;
53 /* The 'strict' flag indicates that the non-existence of at least one
54 * action has potentially been observed through the API. This means
55 * that we should always emit 'action-added' signals for all new
58 * The user can observe the non-existence of an action by listing the
59 * actions or by performing a query (such as parameter type) on a
60 * non-existent action.
62 * If the user has no way of knowing that a given action didn't
63 * already exist then we can skip emitting 'action-added' signals
64 * since they have no way of knowing that it wasn't there from the
70 typedef GObjectClass GDBusActionGroupClass
;
75 GVariantType
*parameter_type
;
81 action_info_free (gpointer user_data
)
83 ActionInfo
*info
= user_data
;
88 g_variant_unref (info
->state
);
90 if (info
->parameter_type
)
91 g_variant_type_free (info
->parameter_type
);
93 g_slice_free (ActionInfo
, info
);
97 action_info_new_from_iter (GVariantIter
*iter
)
99 const gchar
*param_str
;
105 if (!g_variant_iter_next (iter
, "{s(bg@av)}", &name
,
106 &enabled
, ¶m_str
, &state
))
109 info
= g_slice_new (ActionInfo
);
111 info
->enabled
= enabled
;
113 if (g_variant_n_children (state
))
114 g_variant_get_child (state
, 0, "v", &info
->state
);
117 g_variant_unref (state
);
120 info
->parameter_type
= g_variant_type_copy ((GVariantType
*) param_str
);
122 info
->parameter_type
= NULL
;
127 static void g_dbus_action_group_iface_init (GActionGroupInterface
*);
128 G_DEFINE_TYPE_WITH_CODE (GDBusActionGroup
, g_dbus_action_group
, G_TYPE_OBJECT
,
129 G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP
, g_dbus_action_group_iface_init
))
132 g_dbus_action_group_list_actions (GActionGroup
*g_group
)
134 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (g_group
);
140 n
= g_hash_table_size (group
->actions
);
141 keys
= g_new (gchar
*, n
+ 1);
143 g_hash_table_iter_init (&iter
, group
->actions
);
144 while (g_hash_table_iter_next (&iter
, &key
, NULL
))
145 keys
[i
++] = g_strdup (key
);
146 g_assert_cmpint (i
, ==, n
);
149 group
->strict
= TRUE
;
155 g_dbus_action_group_query_action (GActionGroup
*g_group
,
156 const gchar
*action_name
,
158 const GVariantType
**parameter_type
,
159 const GVariantType
**state_type
,
160 GVariant
**state_hint
,
163 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (g_group
);
166 info
= g_hash_table_lookup (group
->actions
, action_name
);
170 group
->strict
= TRUE
;
175 *enabled
= info
->enabled
;
178 *parameter_type
= info
->parameter_type
;
181 *state_type
= info
->state
? g_variant_get_type (info
->state
) : NULL
;
187 *state
= info
->state
? g_variant_ref (info
->state
) : NULL
;
193 g_dbus_action_group_change_state (GActionGroup
*g_group
,
194 const gchar
*action_name
,
197 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (g_group
);
199 /* Don't bother with the checks. The other side will do it again. */
200 g_dbus_connection_call (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions", "SetState",
201 g_variant_new ("(sva{sv})", action_name
, value
, NULL
),
202 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
206 g_dbus_action_group_activate (GActionGroup
*g_group
,
207 const gchar
*action_name
,
210 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (g_group
);
211 GVariantBuilder builder
;
213 g_variant_builder_init (&builder
, G_VARIANT_TYPE ("av"));
216 g_variant_builder_add (&builder
, "v", parameter
);
218 g_dbus_connection_call (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions", "Activate",
219 g_variant_new ("(sava{sv})", action_name
, &builder
, NULL
),
220 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
224 g_dbus_action_group_finalize (GObject
*object
)
226 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (object
);
228 g_dbus_connection_signal_unsubscribe (group
->connection
, group
->subscription_id
);
229 g_hash_table_unref (group
->actions
);
230 g_object_unref (group
->connection
);
231 g_free (group
->object_path
);
232 g_free (group
->bus_name
);
234 G_OBJECT_CLASS (g_dbus_action_group_parent_class
)
239 g_dbus_action_group_init (GDBusActionGroup
*group
)
241 group
->actions
= g_hash_table_new_full (g_str_hash
, g_str_equal
, NULL
, action_info_free
);
245 g_dbus_action_group_class_init (GDBusActionGroupClass
*class)
247 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
249 object_class
->finalize
= g_dbus_action_group_finalize
;
253 g_dbus_action_group_iface_init (GActionGroupInterface
*iface
)
255 iface
->list_actions
= g_dbus_action_group_list_actions
;
256 iface
->query_action
= g_dbus_action_group_query_action
;
257 iface
->change_action_state
= g_dbus_action_group_change_state
;
258 iface
->activate_action
= g_dbus_action_group_activate
;
262 g_dbus_action_group_changed (GDBusConnection
*connection
,
264 const gchar
*object_path
,
265 const gchar
*interface_name
,
266 const gchar
*signal_name
,
267 GVariant
*parameters
,
270 GDBusActionGroup
*group
= user_data
;
271 GActionGroup
*g_group
= user_data
;
273 if (g_str_equal (signal_name
, "Changed") &&
274 g_variant_is_of_type (parameters
, G_VARIANT_TYPE ("(asa{sb}a{sv}a{s(bgav)})")))
281 g_variant_get_child (parameters
, 0, "as", &iter
);
282 while (g_variant_iter_next (iter
, "&s", &name
))
284 if (g_hash_table_lookup (group
->actions
, name
))
286 g_hash_table_remove (group
->actions
, name
);
287 g_action_group_action_removed (g_group
, name
);
290 g_variant_iter_free (iter
);
299 g_variant_get_child (parameters
, 1, "a{sb}", &iter
);
300 while (g_variant_iter_next (iter
, "{&sb}", &name
, &enabled
))
304 info
= g_hash_table_lookup (group
->actions
, name
);
306 if (info
&& info
->enabled
!= enabled
)
308 info
->enabled
= enabled
;
309 g_action_group_action_enabled_changed (g_group
, name
, enabled
);
312 g_variant_iter_free (iter
);
321 g_variant_get_child (parameters
, 2, "a{sv}", &iter
);
322 while (g_variant_iter_next (iter
, "{&sv}", &name
, &state
))
326 info
= g_hash_table_lookup (group
->actions
, name
);
328 if (info
&& info
->state
&& !g_variant_equal (state
, info
->state
) &&
329 g_variant_is_of_type (state
, g_variant_get_type (info
->state
)))
331 g_variant_unref (info
->state
);
332 info
->state
= g_variant_ref (state
);
334 g_action_group_action_state_changed (g_group
, name
, state
);
337 g_variant_unref (state
);
339 g_variant_iter_free (iter
);
347 g_variant_get_child (parameters
, 3, "a{s(bgav)}", &iter
);
348 while ((info
= action_info_new_from_iter (iter
)))
350 if (!g_hash_table_lookup (group
->actions
, info
->name
))
352 g_hash_table_insert (group
->actions
, info
->name
, info
);
355 g_action_group_action_added (g_group
, info
->name
);
358 action_info_free (info
);
365 g_dbus_action_group_describe_all_done (GObject
*source
,
366 GAsyncResult
*result
,
369 GSimpleAsyncResult
*my_result
= user_data
;
370 GDBusActionGroup
*group
;
371 GError
*error
= NULL
;
374 group
= g_simple_async_result_get_op_res_gpointer (my_result
);
375 g_assert (G_IS_DBUS_ACTION_GROUP (group
));
376 g_assert (group
->connection
== (gpointer
) source
);
377 reply
= g_dbus_connection_call_finish (group
->connection
, result
, &error
);
384 g_variant_get (reply
, "(a{s(bgav)})", &iter
);
385 while ((action
= action_info_new_from_iter (iter
)))
386 g_hash_table_insert (group
->actions
, action
->name
, action
);
387 g_variant_iter_free (iter
);
388 g_variant_unref (reply
);
392 g_simple_async_result_set_from_error (my_result
, error
);
393 g_error_free (error
);
396 g_simple_async_result_complete (my_result
);
397 g_object_unref (my_result
);
401 * g_dbus_action_group_new:
402 * @connection: A #GDBusConnection
403 * @bus_name: the bus name which exports the action group
404 * @object_path: the object path at which the action group is exported
405 * @flags: Flags used when constructing the object
406 * @cancellable: A #GCancellable or %NULL
407 * @callback: Callback function to invoke when the object is ready
408 * @user_data: User data to pass to @callback
410 * Creates a new, empty, #GDBusActionGroup.
412 * This is a failable asynchronous constructor - when the object
413 * is ready, @callback will be invoked and you can use
414 * g_dbus_action_group_new_finish() to get the result.
416 * See g_dbus_action_group_new_sync() and for a synchronous version
417 * of this constructor.
420 g_dbus_action_group_new (GDBusConnection
*connection
,
421 const gchar
*bus_name
,
422 const gchar
*object_path
,
423 GDBusActionGroupFlags flags
,
424 GCancellable
*cancellable
,
425 GAsyncReadyCallback callback
,
428 GSimpleAsyncResult
*result
;
429 GDBusActionGroup
*group
;
431 group
= g_object_new (G_TYPE_DBUS_ACTION_GROUP
, NULL
);
432 group
->connection
= g_object_ref (connection
);
433 group
->bus_name
= g_strdup (bus_name
);
434 group
->object_path
= g_strdup (object_path
);
436 /* It's probably excessive to worry about watching the name ownership.
437 * The person using this class will know for themselves when the name
441 result
= g_simple_async_result_new (G_OBJECT (group
), callback
, user_data
, g_dbus_action_group_new
);
442 g_simple_async_result_set_op_res_gpointer (result
, group
, g_object_unref
);
444 if (~flags
& G_DBUS_ACTION_GROUP_FLAGS_DO_NOT_WATCH
)
445 group
->subscription_id
=
446 g_dbus_connection_signal_subscribe (connection
, bus_name
, "org.gtk.Actions", "Changed", object_path
, NULL
,
447 G_DBUS_SIGNAL_FLAGS_NONE
, g_dbus_action_group_changed
, group
, NULL
);
449 if (~flags
& G_DBUS_ACTION_GROUP_FLAGS_DO_NOT_POPULATE
)
450 g_dbus_connection_call (connection
, bus_name
, object_path
, "org.gtk.Actions", "DescribeAll", NULL
,
451 G_VARIANT_TYPE ("(a{s(bgav)})"), G_DBUS_CALL_FLAGS_NONE
, -1, cancellable
,
452 g_dbus_action_group_describe_all_done
, result
);
455 g_simple_async_result_complete_in_idle (result
);
459 * g_dbus_action_group_new_finish:
460 * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback
461 * function passed to g_dbus_action_group_new()
462 * @error: Return location for error or %NULL
464 * Finishes creating a #GDBusActionGroup.
466 * Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref().
469 g_dbus_action_group_new_finish (GAsyncResult
*result
,
472 GSimpleAsyncResult
*simple
= G_SIMPLE_ASYNC_RESULT (result
);
474 g_return_val_if_fail (g_simple_async_result_is_valid (result
,
475 g_simple_async_result_get_op_res_gpointer (simple
),
476 g_dbus_action_group_new
), NULL
);
478 if (g_simple_async_result_propagate_error (simple
, error
))
481 return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple
));
485 * g_dbus_action_group_new_sync:
486 * @connection: A #GDBusConnection
487 * @bus_name: the bus name which exports the action group
488 * @object_path: the object path at which the action group is exported
489 * @flags: Flags used when constructing the object
490 * @cancellable: A #GCancellable or %NULL
491 * @error: Return location for error or %NULL
493 * This is a synchronous failable constructor. See g_dbus_action_group_new()
494 * and g_dbus_action_group_new_finish() for the asynchronous version.
496 * Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref().
499 g_dbus_action_group_new_sync (GDBusConnection
*connection
,
500 const gchar
*bus_name
,
501 const gchar
*object_path
,
502 GDBusActionGroupFlags flags
,
503 GCancellable
*cancellable
,
506 GDBusActionGroup
*group
;
508 group
= g_object_new (G_TYPE_DBUS_ACTION_GROUP
, NULL
);
509 group
->connection
= g_object_ref (connection
);
510 group
->bus_name
= g_strdup (bus_name
);
511 group
->object_path
= g_strdup (object_path
);
513 if (~flags
& G_DBUS_ACTION_GROUP_FLAGS_DO_NOT_WATCH
)
514 group
->subscription_id
=
515 g_dbus_connection_signal_subscribe (connection
, bus_name
, "org.gtk.Actions", "Changed", object_path
, NULL
,
516 G_DBUS_SIGNAL_FLAGS_NONE
, g_dbus_action_group_changed
, group
, NULL
);
518 if (~flags
& G_DBUS_ACTION_GROUP_FLAGS_DO_NOT_POPULATE
)
522 reply
= g_dbus_connection_call_sync (connection
, bus_name
, object_path
, "org.gtk.Actions",
523 "DescribeAll", NULL
, G_VARIANT_TYPE ("(a{s(bgav)})"),
524 G_DBUS_CALL_FLAGS_NONE
, -1, cancellable
, error
);
531 g_variant_get (reply
, "(a{s(bgav)})", &iter
);
532 while ((action
= action_info_new_from_iter (iter
)))
533 g_hash_table_insert (group
->actions
, action
->name
, action
);
534 g_variant_iter_free (iter
);
535 g_variant_unref (reply
);
539 g_object_unref (group
);
548 g_dbus_action_group_inject (GDBusActionGroup
*group
,
549 const gchar
*action_name
,
550 const GVariantType
*parameter_type
,
556 g_return_val_if_fail (G_IS_DBUS_ACTION_GROUP (group
), FALSE
);
557 g_return_val_if_fail (action_name
!= NULL
, FALSE
);
559 if (g_hash_table_lookup (group
->actions
, action_name
))
562 action
= g_slice_new (ActionInfo
);
563 action
->name
= g_strdup (action_name
);
564 action
->parameter_type
= parameter_type
? g_variant_type_copy (parameter_type
) : NULL
;
565 action
->enabled
= !!enabled
;
566 action
->state
= state
? g_variant_ref_sink (state
) : NULL
;
568 g_hash_table_insert (group
->actions
, action
->name
, action
);
571 g_action_group_action_added (G_ACTION_GROUP (group
), action_name
);