GDBusActionGroup: make API just like GDBusMenuModel

Have one simple _get() API that returns the group immediately, in an
empty state.  The group is initialised on the first attempt to interact
with it.

Leave a secret 'back door' for GApplication to do a blocking
initialisation.
This commit is contained in:
Ryan Lortie 2011-12-08 17:49:01 -05:00
parent 0fdd9985bb
commit a6366dc289
5 changed files with 225 additions and 274 deletions

View File

@ -2918,10 +2918,7 @@ g_dbus_connection_unexport_action_group
<SECTION>
<FILE>gdbusactiongroup</FILE>
GDBusActionGroup
g_dbus_action_group_new
g_dbus_action_group_new_finish
g_dbus_action_group_new_sync
g_dbus_action_group_inject
g_dbus_action_group_get
<SUBSECTION Standard>
G_TYPE_DBUS_ACTION_GROUP

View File

@ -37,6 +37,12 @@
#include "gapplicationcommandline.h"
#include "gdbusmethodinvocation.h"
G_GNUC_INTERNAL gboolean
g_dbus_action_group_sync (GDBusActionGroup *group,
GCancellable *cancellable,
GError **error);
/* DBus Interface definition {{{1 */
static const gchar org_gtk_Application_xml[] =
"<node>"
@ -526,12 +532,12 @@ g_application_impl_register (GApplication *application,
* This also serves as a mechanism to ensure that the primary exists
* (ie: DBus service files installed correctly, etc).
*/
actions = g_dbus_action_group_new_sync (impl->session_bus, impl->bus_name, impl->object_path, cancellable, error);
if (actions == NULL)
actions = g_dbus_action_group_get (impl->session_bus, impl->bus_name, impl->object_path);
if (!g_dbus_action_group_sync (actions, cancellable, error))
{
/* The primary appears not to exist. Fail the registration. */
g_application_impl_destroy (impl);
g_object_unref (actions);
return NULL;
}

View File

@ -22,12 +22,10 @@
#include "config.h"
#include "gsimpleasyncresult.h"
#include "gdbusactiongroup.h"
#include "gdbusconnection.h"
#include "gasyncinitable.h"
#include "gactiongroup.h"
#include "gdbusconnection.h"
#include "gactiongroup.h"
/**
* SECTION:gdbusactiongroup
@ -128,136 +126,6 @@ static void g_dbus_action_group_iface_init (GActionGroupInterface *);
G_DEFINE_TYPE_WITH_CODE (GDBusActionGroup, g_dbus_action_group, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_dbus_action_group_iface_init))
static gchar **
g_dbus_action_group_list_actions (GActionGroup *g_group)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
GHashTableIter iter;
gint n, i = 0;
gchar **keys;
gpointer key;
n = g_hash_table_size (group->actions);
keys = g_new (gchar *, n + 1);
g_hash_table_iter_init (&iter, group->actions);
while (g_hash_table_iter_next (&iter, &key, NULL))
keys[i++] = g_strdup (key);
g_assert_cmpint (i, ==, n);
keys[n] = NULL;
group->strict = TRUE;
return keys;
}
static gboolean
g_dbus_action_group_query_action (GActionGroup *g_group,
const gchar *action_name,
gboolean *enabled,
const GVariantType **parameter_type,
const GVariantType **state_type,
GVariant **state_hint,
GVariant **state)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
ActionInfo *info;
info = g_hash_table_lookup (group->actions, action_name);
if (info == NULL)
{
group->strict = TRUE;
return FALSE;
}
if (enabled)
*enabled = info->enabled;
if (parameter_type)
*parameter_type = info->parameter_type;
if (state_type)
*state_type = info->state ? g_variant_get_type (info->state) : NULL;
if (state_hint)
*state_hint = NULL;
if (state)
*state = info->state ? g_variant_ref (info->state) : NULL;
return TRUE;
}
static void
g_dbus_action_group_change_state (GActionGroup *g_group,
const gchar *action_name,
GVariant *value)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
/* Don't bother with the checks. The other side will do it again. */
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "SetState",
g_variant_new ("(sva{sv})", action_name, value, NULL),
NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static void
g_dbus_action_group_activate (GActionGroup *g_group,
const gchar *action_name,
GVariant *parameter)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
if (parameter)
g_variant_builder_add (&builder, "v", parameter);
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "Activate",
g_variant_new ("(sava{sv})", action_name, &builder, NULL),
NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static void
g_dbus_action_group_finalize (GObject *object)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (object);
g_dbus_connection_signal_unsubscribe (group->connection, group->subscription_id);
g_hash_table_unref (group->actions);
g_object_unref (group->connection);
g_free (group->object_path);
g_free (group->bus_name);
G_OBJECT_CLASS (g_dbus_action_group_parent_class)
->finalize (object);
}
static void
g_dbus_action_group_init (GDBusActionGroup *group)
{
group->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, action_info_free);
}
static void
g_dbus_action_group_class_init (GDBusActionGroupClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = g_dbus_action_group_finalize;
}
static void
g_dbus_action_group_iface_init (GActionGroupInterface *iface)
{
iface->list_actions = g_dbus_action_group_list_actions;
iface->query_action = g_dbus_action_group_query_action;
iface->change_action_state = g_dbus_action_group_change_state;
iface->activate_action = g_dbus_action_group_activate;
}
static void
g_dbus_action_group_changed (GDBusConnection *connection,
const gchar *sender,
@ -270,6 +138,10 @@ g_dbus_action_group_changed (GDBusConnection *connection,
GDBusActionGroup *group = user_data;
GActionGroup *g_group = user_data;
/* make sure that we've been fully initialised */
if (group->actions == NULL)
return;
if (g_str_equal (signal_name, "Changed") &&
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(asa{sb}a{sv}a{s(bgav)})")))
{
@ -361,20 +233,20 @@ g_dbus_action_group_changed (GDBusConnection *connection,
}
}
static void
g_dbus_action_group_describe_all_done (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GSimpleAsyncResult *my_result = user_data;
GDBusActionGroup *group;
GError *error = NULL;
GDBusActionGroup *group= user_data;
GVariant *reply;
group = g_simple_async_result_get_op_res_gpointer (my_result);
g_assert (G_IS_DBUS_ACTION_GROUP (group));
g_assert (group->actions == NULL);
group->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, action_info_free);
g_assert (group->connection == (gpointer) source);
reply = g_dbus_connection_call_finish (group->connection, result, &error);
reply = g_dbus_connection_call_finish (group->connection, result, NULL);
if (reply != NULL)
{
@ -383,47 +255,211 @@ g_dbus_action_group_describe_all_done (GObject *source,
g_variant_get (reply, "(a{s(bgav)})", &iter);
while ((action = action_info_new_from_iter (iter)))
g_hash_table_insert (group->actions, action->name, action);
{
g_hash_table_insert (group->actions, action->name, action);
if (group->strict)
g_action_group_action_added (G_ACTION_GROUP (group), action->name);
}
g_variant_iter_free (iter);
g_variant_unref (reply);
}
}
static void
g_dbus_action_group_async_init (GDBusActionGroup *group)
{
if (group->subscription_id != 0)
return;
group->subscription_id =
g_dbus_connection_signal_subscribe (group->connection, group->bus_name, "org.gtk.Actions", "Changed", group->object_path,
NULL, G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "DescribeAll", NULL,
G_VARIANT_TYPE ("(a{s(bgav)})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL,
g_dbus_action_group_describe_all_done, group);
}
static gchar **
g_dbus_action_group_list_actions (GActionGroup *g_group)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
gchar **keys;
if (group->actions != NULL)
{
GHashTableIter iter;
gint n, i = 0;
gpointer key;
n = g_hash_table_size (group->actions);
keys = g_new (gchar *, n + 1);
g_hash_table_iter_init (&iter, group->actions);
while (g_hash_table_iter_next (&iter, &key, NULL))
keys[i++] = g_strdup (key);
g_assert_cmpint (i, ==, n);
keys[n] = NULL;
}
else
{
g_simple_async_result_set_from_error (my_result, error);
g_error_free (error);
g_dbus_action_group_async_init (group);
keys = g_new0 (gchar *, 1);
}
g_simple_async_result_complete (my_result);
g_object_unref (my_result);
group->strict = TRUE;
return keys;
}
static gboolean
g_dbus_action_group_query_action (GActionGroup *g_group,
const gchar *action_name,
gboolean *enabled,
const GVariantType **parameter_type,
const GVariantType **state_type,
GVariant **state_hint,
GVariant **state)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
ActionInfo *info;
if (group->actions != NULL)
{
info = g_hash_table_lookup (group->actions, action_name);
if (info == NULL)
{
group->strict = TRUE;
return FALSE;
}
if (enabled)
*enabled = info->enabled;
if (parameter_type)
*parameter_type = info->parameter_type;
if (state_type)
*state_type = info->state ? g_variant_get_type (info->state) : NULL;
if (state_hint)
*state_hint = NULL;
if (state)
*state = info->state ? g_variant_ref (info->state) : NULL;
return TRUE;
}
else
{
g_dbus_action_group_async_init (group);
return FALSE;
}
}
static void
g_dbus_action_group_change_state (GActionGroup *g_group,
const gchar *action_name,
GVariant *value)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
/* Don't bother with the checks. The other side will do it again. */
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "SetState",
g_variant_new ("(sva{sv})", action_name, value, NULL),
NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static void
g_dbus_action_group_activate (GActionGroup *g_group,
const gchar *action_name,
GVariant *parameter)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
if (parameter)
g_variant_builder_add (&builder, "v", parameter);
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "Activate",
g_variant_new ("(sava{sv})", action_name, &builder, NULL),
NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static void
g_dbus_action_group_finalize (GObject *object)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (object);
if (group->subscription_id)
g_dbus_connection_signal_unsubscribe (group->connection, group->subscription_id);
if (group->actions)
g_hash_table_unref (group->actions);
g_object_unref (group->connection);
g_free (group->object_path);
g_free (group->bus_name);
G_OBJECT_CLASS (g_dbus_action_group_parent_class)
->finalize (object);
}
static void
g_dbus_action_group_init (GDBusActionGroup *group)
{
}
static void
g_dbus_action_group_class_init (GDBusActionGroupClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = g_dbus_action_group_finalize;
}
static void
g_dbus_action_group_iface_init (GActionGroupInterface *iface)
{
iface->list_actions = g_dbus_action_group_list_actions;
iface->query_action = g_dbus_action_group_query_action;
iface->change_action_state = g_dbus_action_group_change_state;
iface->activate_action = g_dbus_action_group_activate;
}
/**
* g_dbus_action_group_new:
* g_dbus_action_group_get:
* @connection: A #GDBusConnection
* @bus_name: the bus name which exports the action group
* @object_path: the object path at which the action group is exported
* @cancellable: A #GCancellable or %NULL
* @callback: Callback function to invoke when the object is ready
* @user_data: User data to pass to @callback
*
* Creates a new, empty, #GDBusActionGroup.
* Obtains a #GDBusAcitonGroup for the action group which is exported at
* the given @bus_name and @object_path.
*
* This is a failable asynchronous constructor - when the object
* is ready, @callback will be invoked and you can use
* g_dbus_action_group_new_finish() to get the result.
* The thread default main context is taken at the time of this call.
* All signals on the menu model (and any linked models) are reported
* with respect to this context. All calls on the returned menu model
* (and linked models) must also originate from this same context, with
* the thread default main context unchanged.
*
* See g_dbus_action_group_new_sync() and for a synchronous version
* of this constructor.
*/
void
g_dbus_action_group_new (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
* This call is non-blocking. The returned action group may or may not
* already be filled in. The correct thing to do is connect the signals
* for the action group to monitor for changes and then to call
* g_action_group_list_actions() to get the initial list.
*
* Returns: (transfer full): a #GDBusActionGroup
**/
GDBusActionGroup *
g_dbus_action_group_get (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path)
{
GSimpleAsyncResult *result;
GDBusActionGroup *group;
group = g_object_new (G_TYPE_DBUS_ACTION_GROUP, NULL);
@ -431,82 +467,23 @@ g_dbus_action_group_new (GDBusConnection *connection,
group->bus_name = g_strdup (bus_name);
group->object_path = g_strdup (object_path);
/* It's probably excessive to worry about watching the name ownership.
* The person using this class will know for themselves when the name
* disappears.
*/
result = g_simple_async_result_new (G_OBJECT (group), callback, user_data, g_dbus_action_group_new);
g_simple_async_result_set_op_res_gpointer (result, group, g_object_unref);
group->subscription_id =
g_dbus_connection_signal_subscribe (connection, bus_name, "org.gtk.Actions", "Changed", object_path, NULL,
G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
g_dbus_connection_call (connection, bus_name, object_path, "org.gtk.Actions", "DescribeAll", NULL,
G_VARIANT_TYPE ("(a{s(bgav)})"), G_DBUS_CALL_FLAGS_NONE, -1, cancellable,
g_dbus_action_group_describe_all_done, result);
return group;
}
/**
* g_dbus_action_group_new_finish:
* @res: A #GAsyncResult obtained from the #GAsyncReadyCallback
* function passed to g_dbus_action_group_new()
* @error: Return location for error or %NULL
*
* Finishes creating a #GDBusActionGroup.
*
* Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref().
*/
GDBusActionGroup *
g_dbus_action_group_new_finish (GAsyncResult *result,
GError **error)
G_GNUC_INTERNAL gboolean
g_dbus_action_group_sync (GDBusActionGroup *group,
GCancellable *cancellable,
GError **error)
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
g_return_val_if_fail (g_simple_async_result_is_valid (result,
g_simple_async_result_get_op_res_gpointer (simple),
g_dbus_action_group_new), NULL);
if (g_simple_async_result_propagate_error (simple, error))
return NULL;
return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
}
/**
* g_dbus_action_group_new_sync:
* @connection: A #GDBusConnection
* @bus_name: the bus name which exports the action group
* @object_path: the object path at which the action group is exported
* @cancellable: A #GCancellable or %NULL
* @error: Return location for error or %NULL
*
* This is a synchronous failable constructor. See g_dbus_action_group_new()
* and g_dbus_action_group_new_finish() for the asynchronous version.
*
* Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref().
*/
GDBusActionGroup *
g_dbus_action_group_new_sync (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
GCancellable *cancellable,
GError **error)
{
GDBusActionGroup *group;
GVariant *reply;
group = g_object_new (G_TYPE_DBUS_ACTION_GROUP, NULL);
group->connection = g_object_ref (connection);
group->bus_name = g_strdup (bus_name);
group->object_path = g_strdup (object_path);
g_assert (group->subscription_id == 0);
group->subscription_id =
g_dbus_connection_signal_subscribe (connection, bus_name, "org.gtk.Actions", "Changed", object_path, NULL,
G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
g_dbus_connection_signal_subscribe (group->connection, group->bus_name, "org.gtk.Actions", "Changed", group->object_path,
NULL, G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
reply = g_dbus_connection_call_sync (connection, bus_name, object_path, "org.gtk.Actions",
reply = g_dbus_connection_call_sync (group->connection, group->bus_name, group->object_path, "org.gtk.Actions",
"DescribeAll", NULL, G_VARIANT_TYPE ("(a{s(bgav)})"),
G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error);
@ -521,11 +498,6 @@ g_dbus_action_group_new_sync (GDBusConnection *connection,
g_variant_iter_free (iter);
g_variant_unref (reply);
}
else
{
g_object_unref (group);
return NULL;
}
return group;
return reply != NULL;
}

View File

@ -45,21 +45,9 @@ G_BEGIN_DECLS
GType g_dbus_action_group_get_type (void) G_GNUC_CONST;
void g_dbus_action_group_new (GDBusConnection *connection,
GDBusActionGroup * g_dbus_action_group_get (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDBusActionGroup * g_dbus_action_group_new_finish (GAsyncResult *result,
GError **error);
GDBusActionGroup * g_dbus_action_group_new_sync (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
GCancellable *cancellable,
GError **error);
const gchar *object_path);
G_END_DECLS

View File

@ -496,24 +496,12 @@ stop_loop (gpointer data)
return G_SOURCE_REMOVE;
}
GDBusActionGroup *proxy;
static void
got_proxy (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
GError *error = NULL;
proxy = g_dbus_action_group_new_finish (res, &error);
g_assert_no_error (error);
}
static void
test_dbus_export (void)
{
GDBusConnection *bus;
GSimpleActionGroup *group;
GDBusActionGroup *proxy;
GSimpleAction *action;
GMainLoop *loop;
static GActionEntry entries[] = {
@ -539,8 +527,8 @@ test_dbus_export (void)
id = g_dbus_connection_export_action_group (bus, "/", G_ACTION_GROUP (group), &error);
g_assert_no_error (error);
g_dbus_action_group_new (bus, g_dbus_connection_get_unique_name (bus), "/", NULL, got_proxy, NULL);
g_assert_no_error (error);
proxy = g_dbus_action_group_get (bus, g_dbus_connection_get_unique_name (bus), "/");
g_strfreev (g_action_group_list_actions (G_ACTION_GROUP (proxy)));
g_timeout_add (100, stop_loop, loop);
g_main_loop_run (loop);