2 * Copyright © 2010 Codethink Limited
3 * Copyright © 2011 Canonical Limited
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.
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, see <http://www.gnu.org/licenses/>.
18 * Authors: Ryan Lortie <desrt@desrt.ca>
23 #include "gdbusactiongroup-private.h"
25 #include "gremoteactiongroup.h"
26 #include "gdbusconnection.h"
27 #include "gactiongroup.h"
30 * SECTION:gdbusactiongroup
31 * @title: GDBusActionGroup
32 * @short_description: A D-Bus GActionGroup implementation
34 * @see_also: [GActionGroup exporter][gio-GActionGroup-exporter]
36 * #GDBusActionGroup is an implementation of the #GActionGroup
37 * interface that can be used as a proxy for an action group
38 * that is exported over D-Bus with g_dbus_connection_export_action_group().
44 * #GDBusActionGroup is an opaque data structure and can only be accessed
45 * using the following functions.
48 struct _GDBusActionGroup
50 GObject parent_instance
;
52 GDBusConnection
*connection
;
55 guint subscription_id
;
58 /* The 'strict' flag indicates that the non-existence of at least one
59 * action has potentially been observed through the API. This means
60 * that we should always emit 'action-added' signals for all new
63 * The user can observe the non-existence of an action by listing the
64 * actions or by performing a query (such as parameter type) on a
65 * non-existent action.
67 * If the user has no way of knowing that a given action didn't
68 * already exist then we can skip emitting 'action-added' signals
69 * since they have no way of knowing that it wasn't there from the
75 typedef GObjectClass GDBusActionGroupClass
;
80 GVariantType
*parameter_type
;
86 action_info_free (gpointer user_data
)
88 ActionInfo
*info
= user_data
;
93 g_variant_unref (info
->state
);
95 if (info
->parameter_type
)
96 g_variant_type_free (info
->parameter_type
);
98 g_slice_free (ActionInfo
, info
);
102 action_info_new_from_iter (GVariantIter
*iter
)
104 const gchar
*param_str
;
110 if (!g_variant_iter_next (iter
, "{s(b&g@av)}", &name
,
111 &enabled
, ¶m_str
, &state
))
114 info
= g_slice_new (ActionInfo
);
116 info
->enabled
= enabled
;
118 if (g_variant_n_children (state
))
119 g_variant_get_child (state
, 0, "v", &info
->state
);
122 g_variant_unref (state
);
125 info
->parameter_type
= g_variant_type_copy ((GVariantType
*) param_str
);
127 info
->parameter_type
= NULL
;
132 static void g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface
*iface
);
133 static void g_dbus_action_group_iface_init (GActionGroupInterface
*iface
);
134 G_DEFINE_TYPE_WITH_CODE (GDBusActionGroup
, g_dbus_action_group
, G_TYPE_OBJECT
,
135 G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP
, g_dbus_action_group_iface_init
)
136 G_IMPLEMENT_INTERFACE (G_TYPE_REMOTE_ACTION_GROUP
, g_dbus_action_group_remote_iface_init
))
139 g_dbus_action_group_changed (GDBusConnection
*connection
,
141 const gchar
*object_path
,
142 const gchar
*interface_name
,
143 const gchar
*signal_name
,
144 GVariant
*parameters
,
147 GDBusActionGroup
*group
= user_data
;
148 GActionGroup
*g_group
= user_data
;
150 /* make sure that we've been fully initialised */
151 if (group
->actions
== NULL
)
154 if (g_str_equal (signal_name
, "Changed") &&
155 g_variant_is_of_type (parameters
, G_VARIANT_TYPE ("(asa{sb}a{sv}a{s(bgav)})")))
162 g_variant_get_child (parameters
, 0, "as", &iter
);
163 while (g_variant_iter_next (iter
, "&s", &name
))
165 if (g_hash_table_lookup (group
->actions
, name
))
167 g_hash_table_remove (group
->actions
, name
);
168 g_action_group_action_removed (g_group
, name
);
171 g_variant_iter_free (iter
);
180 g_variant_get_child (parameters
, 1, "a{sb}", &iter
);
181 while (g_variant_iter_next (iter
, "{&sb}", &name
, &enabled
))
185 info
= g_hash_table_lookup (group
->actions
, name
);
187 if (info
&& info
->enabled
!= enabled
)
189 info
->enabled
= enabled
;
190 g_action_group_action_enabled_changed (g_group
, name
, enabled
);
193 g_variant_iter_free (iter
);
202 g_variant_get_child (parameters
, 2, "a{sv}", &iter
);
203 while (g_variant_iter_next (iter
, "{&sv}", &name
, &state
))
207 info
= g_hash_table_lookup (group
->actions
, name
);
209 if (info
&& info
->state
&& !g_variant_equal (state
, info
->state
) &&
210 g_variant_is_of_type (state
, g_variant_get_type (info
->state
)))
212 g_variant_unref (info
->state
);
213 info
->state
= g_variant_ref (state
);
215 g_action_group_action_state_changed (g_group
, name
, state
);
218 g_variant_unref (state
);
220 g_variant_iter_free (iter
);
228 g_variant_get_child (parameters
, 3, "a{s(bgav)}", &iter
);
229 while ((info
= action_info_new_from_iter (iter
)))
231 if (!g_hash_table_lookup (group
->actions
, info
->name
))
233 g_hash_table_insert (group
->actions
, info
->name
, info
);
236 g_action_group_action_added (g_group
, info
->name
);
239 action_info_free (info
);
241 g_variant_iter_free (iter
);
248 g_dbus_action_group_describe_all_done (GObject
*source
,
249 GAsyncResult
*result
,
252 GDBusActionGroup
*group
= user_data
;
255 g_assert (group
->actions
== NULL
);
256 group
->actions
= g_hash_table_new_full (g_str_hash
, g_str_equal
, NULL
, action_info_free
);
258 g_assert (group
->connection
== (gpointer
) source
);
259 reply
= g_dbus_connection_call_finish (group
->connection
, result
, NULL
);
266 g_variant_get (reply
, "(a{s(bgav)})", &iter
);
267 while ((action
= action_info_new_from_iter (iter
)))
269 g_hash_table_insert (group
->actions
, action
->name
, action
);
272 g_action_group_action_added (G_ACTION_GROUP (group
), action
->name
);
274 g_variant_iter_free (iter
);
275 g_variant_unref (reply
);
278 g_object_unref (group
);
283 g_dbus_action_group_async_init (GDBusActionGroup
*group
)
285 if (group
->subscription_id
!= 0)
288 group
->subscription_id
=
289 g_dbus_connection_signal_subscribe (group
->connection
, group
->bus_name
, "org.gtk.Actions", "Changed", group
->object_path
,
290 NULL
, G_DBUS_SIGNAL_FLAGS_NONE
, g_dbus_action_group_changed
, group
, NULL
);
292 g_dbus_connection_call (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions", "DescribeAll", NULL
,
293 G_VARIANT_TYPE ("(a{s(bgav)})"), G_DBUS_CALL_FLAGS_NONE
, -1, NULL
,
294 g_dbus_action_group_describe_all_done
, g_object_ref (group
));
298 g_dbus_action_group_list_actions (GActionGroup
*g_group
)
300 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (g_group
);
303 if (group
->actions
!= NULL
)
309 n
= g_hash_table_size (group
->actions
);
310 keys
= g_new (gchar
*, n
+ 1);
312 g_hash_table_iter_init (&iter
, group
->actions
);
313 while (g_hash_table_iter_next (&iter
, &key
, NULL
))
314 keys
[i
++] = g_strdup (key
);
315 g_assert_cmpint (i
, ==, n
);
320 g_dbus_action_group_async_init (group
);
321 keys
= g_new0 (gchar
*, 1);
324 group
->strict
= TRUE
;
330 g_dbus_action_group_query_action (GActionGroup
*g_group
,
331 const gchar
*action_name
,
333 const GVariantType
**parameter_type
,
334 const GVariantType
**state_type
,
335 GVariant
**state_hint
,
338 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (g_group
);
341 if (group
->actions
!= NULL
)
343 info
= g_hash_table_lookup (group
->actions
, action_name
);
347 group
->strict
= TRUE
;
352 *enabled
= info
->enabled
;
355 *parameter_type
= info
->parameter_type
;
358 *state_type
= info
->state
? g_variant_get_type (info
->state
) : NULL
;
364 *state
= info
->state
? g_variant_ref (info
->state
) : NULL
;
370 g_dbus_action_group_async_init (group
);
371 group
->strict
= TRUE
;
378 g_dbus_action_group_activate_action_full (GRemoteActionGroup
*remote
,
379 const gchar
*action_name
,
381 GVariant
*platform_data
)
383 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (remote
);
384 GVariantBuilder builder
;
386 g_variant_builder_init (&builder
, G_VARIANT_TYPE ("av"));
389 g_variant_builder_add (&builder
, "v", parameter
);
391 g_dbus_connection_call (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions", "Activate",
392 g_variant_new ("(sav@a{sv})", action_name
, &builder
, platform_data
),
393 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
397 g_dbus_action_group_change_action_state_full (GRemoteActionGroup
*remote
,
398 const gchar
*action_name
,
400 GVariant
*platform_data
)
402 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (remote
);
404 g_dbus_connection_call (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions", "SetState",
405 g_variant_new ("(sv@a{sv})", action_name
, value
, platform_data
),
406 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
410 g_dbus_action_group_change_state (GActionGroup
*group
,
411 const gchar
*action_name
,
414 g_dbus_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (group
),
415 action_name
, value
, g_variant_new ("a{sv}", NULL
));
419 g_dbus_action_group_activate (GActionGroup
*group
,
420 const gchar
*action_name
,
423 g_dbus_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (group
),
424 action_name
, parameter
, g_variant_new ("a{sv}", NULL
));
428 g_dbus_action_group_finalize (GObject
*object
)
430 GDBusActionGroup
*group
= G_DBUS_ACTION_GROUP (object
);
432 if (group
->subscription_id
)
433 g_dbus_connection_signal_unsubscribe (group
->connection
, group
->subscription_id
);
436 g_hash_table_unref (group
->actions
);
438 g_object_unref (group
->connection
);
439 g_free (group
->object_path
);
440 g_free (group
->bus_name
);
442 G_OBJECT_CLASS (g_dbus_action_group_parent_class
)
447 g_dbus_action_group_init (GDBusActionGroup
*group
)
452 g_dbus_action_group_class_init (GDBusActionGroupClass
*class)
454 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
456 object_class
->finalize
= g_dbus_action_group_finalize
;
460 g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface
*iface
)
462 iface
->activate_action_full
= g_dbus_action_group_activate_action_full
;
463 iface
->change_action_state_full
= g_dbus_action_group_change_action_state_full
;
467 g_dbus_action_group_iface_init (GActionGroupInterface
*iface
)
469 iface
->list_actions
= g_dbus_action_group_list_actions
;
470 iface
->query_action
= g_dbus_action_group_query_action
;
471 iface
->change_action_state
= g_dbus_action_group_change_state
;
472 iface
->activate_action
= g_dbus_action_group_activate
;
476 * g_dbus_action_group_get:
477 * @connection: A #GDBusConnection
478 * @bus_name: the bus name which exports the action group
479 * @object_path: the object path at which the action group is exported
481 * Obtains a #GDBusActionGroup for the action group which is exported at
482 * the given @bus_name and @object_path.
484 * The thread default main context is taken at the time of this call.
485 * All signals on the menu model (and any linked models) are reported
486 * with respect to this context. All calls on the returned menu model
487 * (and linked models) must also originate from this same context, with
488 * the thread default main context unchanged.
490 * This call is non-blocking. The returned action group may or may not
491 * already be filled in. The correct thing to do is connect the signals
492 * for the action group to monitor for changes and then to call
493 * g_action_group_list_actions() to get the initial list.
495 * Returns: (transfer full): a #GDBusActionGroup
500 g_dbus_action_group_get (GDBusConnection
*connection
,
501 const gchar
*bus_name
,
502 const gchar
*object_path
)
504 GDBusActionGroup
*group
;
506 group
= g_object_new (G_TYPE_DBUS_ACTION_GROUP
, NULL
);
507 group
->connection
= g_object_ref (connection
);
508 group
->bus_name
= g_strdup (bus_name
);
509 group
->object_path
= g_strdup (object_path
);
515 g_dbus_action_group_sync (GDBusActionGroup
*group
,
516 GCancellable
*cancellable
,
521 g_assert (group
->subscription_id
== 0);
523 group
->subscription_id
=
524 g_dbus_connection_signal_subscribe (group
->connection
, group
->bus_name
, "org.gtk.Actions", "Changed", group
->object_path
,
525 NULL
, G_DBUS_SIGNAL_FLAGS_NONE
, g_dbus_action_group_changed
, group
, NULL
);
527 reply
= g_dbus_connection_call_sync (group
->connection
, group
->bus_name
, group
->object_path
, "org.gtk.Actions",
528 "DescribeAll", NULL
, G_VARIANT_TYPE ("(a{s(bgav)})"),
529 G_DBUS_CALL_FLAGS_NONE
, -1, cancellable
, error
);
536 g_assert (group
->actions
== NULL
);
537 group
->actions
= g_hash_table_new_full (g_str_hash
, g_str_equal
, NULL
, action_info_free
);
539 g_variant_get (reply
, "(a{s(bgav)})", &iter
);
540 while ((action
= action_info_new_from_iter (iter
)))
541 g_hash_table_insert (group
->actions
, action
->name
, action
);
542 g_variant_iter_free (iter
);
543 g_variant_unref (reply
);
546 return reply
!= NULL
;