[glib] Implement (untested) GApplication actions support



commit d8d2513710c5663dc7387d79e443edeb5c75598f
Author: Ryan Lortie <desrt desrt ca>
Date:   Mon Oct 25 14:32:07 2010 -0400

    Implement (untested) GApplication actions support

 gio/gactiongroup.h          |   29 +++-
 gio/gapplication.c          |  118 ++++++++--
 gio/gapplicationimpl-dbus.c |  517 ++++++++++++++++++++++++++++++++++++++-----
 gio/gapplicationimpl.h      |   23 ++-
 gio/giotypes.h              |    1 +
 5 files changed, 607 insertions(+), 81 deletions(-)
---
diff --git a/gio/gactiongroup.h b/gio/gactiongroup.h
index 300d415..2fa4831 100644
--- a/gio/gactiongroup.h
+++ b/gio/gactiongroup.h
@@ -38,6 +38,15 @@ G_BEGIN_DECLS
 #define G_ACTION_GROUP_GET_IFACE(inst)                      (G_TYPE_INSTANCE_GET_INTERFACE ((inst),                  \
                                                              G_TYPE_ACTION_GROUP, GActionGroupInterface))
 
+#define G_TYPE_ACTION_GROUP                                 (g_action_group_get_type ())
+#define G_ACTION_GROUP(inst)                                (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_ACTION_GROUP, GActionGroup))
+#define G_IS_ACTION_GROUP(inst)                             (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_ACTION_GROUP))
+#define G_ACTION_GROUP_GET_IFACE(inst)                      (G_TYPE_INSTANCE_GET_INTERFACE ((inst),                  \
+                                                             G_TYPE_ACTION_GROUP, GActionGroupInterface))
+
+typedef struct _GContextActionGroupInterface                GContextActionGroupInterface;
 typedef struct _GActionGroupInterface                       GActionGroupInterface;
 
 /**
@@ -101,11 +110,25 @@ struct _GActionGroupInterface
   void                  (* action_enabled_changed)     (GActionGroup  *action_group,
                                                         const gchar   *action_name,
                                                         gboolean       enabled);
-  void                  (* action_state_changed)    (GActionGroup   *action_group,
-                                                     const gchar    *action_name,
-                                                     GVariant       *value);
+  void                  (* action_state_changed)       (GActionGroup   *action_group,
+                                                        const gchar    *action_name,
+                                                        GVariant       *value);
 };
 
+struct _GContextActionGroupInterface
+{
+  void                  (* change_action_state)        (GActionGroup  *action_group,
+                                                        const gchar   *action_name,
+                                                        GVariant      *value,
+                                                        GVariant      *context);
+
+  void                  (* activate_action)            (GActionGroup  *action_group,
+                                                        const gchar   *action_name,
+                                                        GVariant      *parameter,
+                                                        GVariant      *context);
+};
+
+
 GType                   g_action_group_get_type                         (void) G_GNUC_CONST;
 
 gboolean                g_action_group_has_action                       (GActionGroup *action_group,
diff --git a/gio/gapplication.c b/gio/gapplication.c
index 279d501..d81ea80 100644
--- a/gio/gapplication.c
+++ b/gio/gapplication.c
@@ -93,6 +93,7 @@ struct _GApplicationPrivate
   guint              is_registered : 1;
   guint              is_remote : 1;
 
+  GHashTable        *remote_actions;  /* string -> RemoteActionInfo */
   GApplicationImpl  *impl;
 };
 
@@ -350,7 +351,7 @@ g_application_set_action_group (GApplication *application,
                                 GActionGroup *action_group)
 {
   g_return_if_fail (G_IS_APPLICATION (application));
-  g_return_if_fail (application->priv->is_registered);
+  g_return_if_fail (!application->priv->is_registered);
 
   if (application->priv->actions != NULL)
     g_object_unref (application->priv->actions);
@@ -894,22 +895,21 @@ g_application_register (GApplication  *application,
 
   if (!application->priv->is_registered)
     {
-      gboolean is_remote;
-
       application->priv->impl =
         g_application_impl_register (application, application->priv->id,
                                      application->priv->flags,
-                                     &is_remote, cancellable, error);
+                                     &application->priv->remote_actions,
+                                     cancellable, error);
 
       if (application->priv->impl == NULL)
         return FALSE;
 
-      application->priv->is_remote = is_remote;
+      application->priv->is_remote = application->priv->remote_actions != NULL;
       application->priv->is_registered = TRUE;
 
       g_object_notify (G_OBJECT (application), "is-registered");
 
-      if (!is_remote)
+      if (!application->priv->is_remote)
         g_signal_emit (application, g_application_signals[SIGNAL_STARTUP], 0);
     }
 
@@ -1198,6 +1198,10 @@ g_application_has_action (GActionGroup *action_group,
 
   g_return_val_if_fail (application->priv->is_registered, FALSE);
 
+  if (application->priv->remote_actions != NULL)
+    return g_hash_table_lookup (application->priv->remote_actions,
+                                action_name) != NULL;
+
   return application->priv->actions &&
          g_action_group_has_action (application->priv->actions, action_name);
 }
@@ -1209,7 +1213,26 @@ g_application_list_actions (GActionGroup *action_group)
 
   g_return_val_if_fail (application->priv->is_registered, NULL);
 
-  if (application->priv->actions != NULL)
+  if (application->priv->remote_actions != NULL)
+    {
+      GHashTableIter iter;
+      gint n, i = 0;
+      gchar **keys;
+      gpointer key;
+
+      n = g_hash_table_size (application->priv->remote_actions);
+      keys = g_new (gchar *, n + 1);
+
+      g_hash_table_iter_init (&iter, application->priv->remote_actions);
+      while (g_hash_table_iter_next (&iter, &key, NULL))
+        keys[i++] = g_strdup (key);
+      g_assert_cmpint (i, ==, n);
+      keys[n] = NULL;
+
+      return keys;
+    }
+
+  else if (application->priv->actions != NULL)
     return g_action_group_list_actions (application->priv->actions);
 
   else
@@ -1226,6 +1249,16 @@ g_application_get_action_enabled (GActionGroup *action_group,
   g_return_val_if_fail (application->priv->actions != NULL, FALSE);
   g_return_val_if_fail (application->priv->is_registered, FALSE);
 
+  if (application->priv->remote_actions)
+    {
+      RemoteActionInfo *info;
+
+      info = g_hash_table_lookup (application->priv->remote_actions,
+                                  action_name);
+
+      return info && info->enabled;
+    }
+
   return g_action_group_get_action_enabled (application->priv->actions,
                                             action_name);
 }
@@ -1239,6 +1272,19 @@ g_application_get_action_parameter_type (GActionGroup *action_group,
   g_return_val_if_fail (application->priv->actions != NULL, NULL);
   g_return_val_if_fail (application->priv->is_registered, NULL);
 
+  if (application->priv->remote_actions)
+    {
+      RemoteActionInfo *info;
+
+      info = g_hash_table_lookup (application->priv->remote_actions,
+                                  action_name);
+
+      if (info)
+        return info->parameter_type;
+      else
+        return NULL;
+    }
+
   return g_action_group_get_action_parameter_type (application->priv->actions,
                                                    action_name);
 }
@@ -1252,20 +1298,20 @@ g_application_get_action_state_type (GActionGroup *action_group,
   g_return_val_if_fail (application->priv->actions != NULL, NULL);
   g_return_val_if_fail (application->priv->is_registered, NULL);
 
-  return g_action_group_get_action_state_type (application->priv->actions,
-                                               action_name);
-}
+  if (application->priv->remote_actions)
+    {
+      RemoteActionInfo *info;
 
-static GVariant *
-g_application_get_action_state_hint (GActionGroup *action_group,
-                                     const gchar  *action_name)
-{
-  GApplication *application = G_APPLICATION (action_group);
+      info = g_hash_table_lookup (application->priv->remote_actions,
+                                  action_name);
 
-  g_return_val_if_fail (application->priv->actions != NULL, NULL);
-  g_return_val_if_fail (application->priv->is_registered, NULL);
+      if (info && info->state)
+        return g_variant_get_type (info->state);
+      else
+        return NULL;
+    }
 
-  return g_action_group_get_action_state_hint (application->priv->actions,
+  return g_action_group_get_action_state_type (application->priv->actions,
                                                action_name);
 }
 
@@ -1278,6 +1324,19 @@ g_application_get_action_state (GActionGroup *action_group,
   g_return_val_if_fail (application->priv->actions != NULL, NULL);
   g_return_val_if_fail (application->priv->is_registered, NULL);
 
+  if (application->priv->remote_actions)
+    {
+      RemoteActionInfo *info;
+
+      info = g_hash_table_lookup (application->priv->remote_actions,
+                                  action_name);
+
+      if (info && info->state)
+        return g_variant_ref (info->state);
+      else
+        return NULL;
+    }
+
   return g_action_group_get_action_state (application->priv->actions,
                                           action_name);
 }
@@ -1289,11 +1348,16 @@ g_application_change_action_state (GActionGroup *action_group,
 {
   GApplication *application = G_APPLICATION (action_group);
 
-  g_return_if_fail (application->priv->actions != NULL);
   g_return_if_fail (application->priv->is_registered);
 
-  g_action_group_change_action_state (application->priv->actions,
-                                      action_name, value);
+  if (application->priv->is_remote)
+    g_application_impl_change_action_state (application->priv->impl,
+                                            action_name, value,
+                                            get_platform_data (application));
+
+  else
+    g_action_group_change_action_state (application->priv->actions,
+                                        action_name, value);
 }
 
 static void
@@ -1303,11 +1367,16 @@ g_application_activate_action (GActionGroup *action_group,
 {
   GApplication *application = G_APPLICATION (action_group);
 
-  g_return_if_fail (application->priv->actions != NULL);
   g_return_if_fail (application->priv->is_registered);
 
-  g_action_group_activate_action (application->priv->actions,
-                                  action_name, parameter);
+  if (application->priv->is_remote)
+    g_application_impl_activate_action (application->priv->impl,
+                                        action_name, parameter,
+                                        get_platform_data (application));
+
+  else
+    g_action_group_activate_action (application->priv->actions,
+                                    action_name, parameter);
 }
 
 static void
@@ -1319,7 +1388,6 @@ g_application_action_group_iface_init (GActionGroupInterface *iface)
   iface->get_action_enabled = g_application_get_action_enabled;
   iface->get_action_parameter_type = g_application_get_action_parameter_type;
   iface->get_action_state_type = g_application_get_action_state_type;
-  iface->get_action_state_hint = g_application_get_action_state_hint;
   iface->get_action_state = g_application_get_action_state;
   iface->change_action_state = g_application_change_action_state;
   iface->activate_action = g_application_activate_action;
diff --git a/gio/gapplicationimpl-dbus.c b/gio/gapplicationimpl-dbus.c
index 893c96b..8a3837d 100644
--- a/gio/gapplicationimpl-dbus.c
+++ b/gio/gapplicationimpl-dbus.c
@@ -21,6 +21,7 @@
 
 #include "gapplicationimpl.h"
 
+#include "gactiongroup.h"
 #include "gapplication.h"
 #include "gfile.h"
 #include "gdbusconnection.h"
@@ -82,6 +83,40 @@ const GDBusInterfaceInfo org_gtk_Application = {
   (GDBusMethodInfo **) application_methods
 };
 
+static const GDBusArgInfo list_arg = { -1, (gchar *) "list", (gchar *) "a(savbav)" };
+static const GDBusArgInfo *describe_all_out[] = { &list_arg, NULL };
+
+static const GDBusArgInfo action_name_arg = { -1, (gchar *) "action_name", (gchar *) "s" };
+static const GDBusArgInfo value_arg = { -1, (gchar *) "value", (gchar *) "v" };
+static const GDBusArgInfo *set_action_state_in[] = { &action_name_arg, &value_arg, &platform_data_arg, NULL };
+
+static const GDBusArgInfo parameter_arg = { -1, (gchar *) "parameter", (gchar *) "av" };
+static const GDBusArgInfo *activate_action_in[] = { &action_name_arg, &parameter_arg, &platform_data_arg, NULL };
+
+static const GDBusMethodInfo describe_all_method = {
+  -1, (gchar *) "DescribeAll", NULL,
+  (GDBusArgInfo **) describe_all_out
+};
+
+static const GDBusMethodInfo set_action_state_method = {
+  -1, (gchar *) "SetState",
+  (GDBusArgInfo **) set_action_state_in
+};
+
+static const GDBusMethodInfo activate_action_method = {
+  -1, (gchar *) "Activate",
+  (GDBusArgInfo **) activate_action_in
+};
+
+static const GDBusMethodInfo *actions_methods[] = {
+  &describe_all_method, &set_action_state_method, &activate_action_method, NULL
+};
+
+const GDBusInterfaceInfo org_gtk_Actions = {
+  -1, (gchar *) "org.gtk.Actions",
+  (GDBusMethodInfo **) actions_methods
+};
+
 static const GDBusArgInfo message_arg = { -1, (gchar *) "message", (gchar *) "s" };
 static const GDBusArgInfo *print_in[] = { &message_arg, NULL };
 static const GDBusArgInfo *print_out[] = { NULL };
@@ -107,7 +142,6 @@ const GDBusInterfaceInfo org_gtk_private_Cmdline = {
   (GDBusMethodInfo **) cmdline_methods
 };
 
-
 /* GApplication implementation {{{1 */
 struct _GApplicationImpl
 {
@@ -115,7 +149,11 @@ struct _GApplicationImpl
   const gchar     *bus_name;
   gchar           *object_path;
   guint            object_id;
+  guint            action_id;
   gpointer         app;
+
+  GHashTable      *actions;
+  guint            signal_id;
 };
 
 
@@ -147,6 +185,8 @@ g_application_impl_method_call (GDBusConnection       *connection,
       g_signal_emit_by_name (impl->app, "activate");
       class->after_emit (impl->app, platform_data);
       g_variant_unref (platform_data);
+
+      g_dbus_method_invocation_return_value (invocation, NULL);
     }
 
   else if (strcmp (method_name, "Open") == 0)
@@ -182,6 +222,8 @@ g_application_impl_method_call (GDBusConnection       *connection,
       for (i = 0; i < n; i++)
         g_object_unref (files[i]);
       g_free (files);
+
+      g_dbus_method_invocation_return_value (invocation, NULL);
     }
 
   else if (strcmp (method_name, "CommandLine") == 0)
@@ -199,6 +241,135 @@ g_application_impl_method_call (GDBusConnection       *connection,
       g_variant_unref (platform_data);
       g_object_unref (cmdline);
     }
+  else
+    g_assert_not_reached ();
+}
+
+static void
+g_application_impl_actions_method_call (GDBusConnection       *connection,
+                                        const gchar           *sender,
+                                        const gchar           *object_path,
+                                        const gchar           *interface_name,
+                                        const gchar           *method_name,
+                                        GVariant              *parameters,
+                                        GDBusMethodInvocation *invocation,
+                                        gpointer               user_data)
+{
+  GApplicationImpl *impl = user_data;
+  GActionGroup *action_group;
+  GApplicationClass *class;
+
+  class = G_APPLICATION_GET_CLASS (impl->app);
+  action_group = G_ACTION_GROUP (impl->app);
+
+  if (strcmp (method_name, "DescribeAll") == 0)
+    {
+      GVariantBuilder builder;
+      gchar **actions;
+      gint i;
+
+      actions = g_action_group_list_actions (action_group);
+      g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a(savbav))"));
+      g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(savbav)"));
+
+      for (i = 0; actions[i]; i++)
+        {
+          /* Open */
+          g_variant_builder_open (&builder, G_VARIANT_TYPE ("(savbav)"));
+
+          /* Name */
+          g_variant_builder_add (&builder, "s", actions[i]);
+
+          /* Parameter type */
+          g_variant_builder_open (&builder, G_VARIANT_TYPE ("av"));
+            {
+              const GVariantType *type;
+
+              type = g_action_group_get_action_parameter_type (action_group,
+                                                               actions[i]);
+              if (type != NULL)
+                {
+                  GVariantType *array_type;
+
+                  array_type = g_variant_type_new_array (type);
+                  g_variant_builder_open (&builder, G_VARIANT_TYPE_VARIANT);
+                  g_variant_builder_open (&builder, array_type);
+                  g_variant_builder_close (&builder);
+                  g_variant_builder_close (&builder);
+                  g_variant_type_free (array_type);
+                }
+            }
+          g_variant_builder_close (&builder);
+
+          /* Enabled */
+          {
+            gboolean enabled = g_action_group_get_action_enabled (action_group,
+                                                                  actions[i]);
+            g_variant_builder_add (&builder, "b", enabled);
+          }
+
+          /* State */
+          g_variant_builder_open (&builder, G_VARIANT_TYPE ("av"));
+          {
+            GVariant *state = g_action_group_get_action_state (action_group,
+                                                               actions[i]);
+            if (state != NULL)
+              {
+                g_variant_builder_add (&builder, "v", state);
+                g_variant_unref (state);
+              }
+          }
+          g_variant_builder_close (&builder);
+
+          /* Close */
+          g_variant_builder_close (&builder);
+        }
+      g_variant_builder_close (&builder);
+
+      g_dbus_method_invocation_return_value (invocation,
+                                             g_variant_builder_end (&builder));
+    }
+
+  else if (strcmp (method_name, "SetState") == 0)
+    {
+      const gchar *action_name;
+      GVariant *platform_data;
+      GVariant *state;
+
+      g_variant_get (parameters, "(&sv a{sv})",
+                     &action_name, &state, &platform_data);
+
+      class->before_emit (impl->app, platform_data);
+      g_action_group_change_action_state (action_group, action_name, state);
+      class->after_emit (impl->app, platform_data);
+      g_variant_unref (platform_data);
+      g_variant_unref (state);
+
+      g_dbus_method_invocation_return_value (invocation, NULL);
+    }
+
+  else if (strcmp (method_name, "Activate") == 0)
+    {
+      const gchar *action_name;
+      GVariant *platform_data;
+      GVariantIter *param;
+      GVariant *parameter;
+
+      g_variant_get (parameters, "(&sav a{sv})",
+                     &action_name, &param, &platform_data);
+      parameter = g_variant_iter_next_value (param);
+      g_variant_iter_free (param);
+
+      class->before_emit (impl->app, platform_data);
+      g_action_group_activate_action (action_group, action_name, parameter);
+      class->after_emit (impl->app, platform_data);
+      g_variant_unref (platform_data);
+
+      if (parameter)
+        g_variant_unref (parameter);
+
+      g_dbus_method_invocation_return_value (invocation, NULL);
+    }
 
   else
     g_assert_not_reached ();
@@ -240,17 +411,132 @@ g_application_impl_destroy (GApplicationImpl *impl)
   g_slice_free (GApplicationImpl, impl);
 }
 
+RemoteActionInfo *
+remote_action_info_new_from_iter (GVariantIter *iter)
+{
+  RemoteActionInfo *info;
+  const gchar *name;
+  GVariant *param_type;
+  gboolean enabled;
+  GVariant *state;
+
+  if (!g_variant_iter_next (iter, "(s avb@av)", &name,
+                            &param_type, &enabled, &state))
+    return NULL;
+
+  info = g_slice_new (RemoteActionInfo);
+  info->parameter_type = g_variant_type_copy (
+                           g_variant_type_element (
+                             g_variant_get_type (param_type)));
+  info->enabled = enabled;
+  info->state = state;
+
+  g_variant_unref (param_type);
+
+  return info;
+}
+
+static void
+g_application_impl_action_signal (GDBusConnection *connection,
+                                  const gchar     *sender_name,
+                                  const gchar     *object_path,
+                                  const gchar     *interface_name,
+                                  const gchar     *signal_name,
+                                  GVariant        *parameters,
+                                  gpointer         user_data)
+{
+  GApplicationImpl *impl = user_data;
+  GActionGroup *action_group;
+
+  action_group = G_ACTION_GROUP (impl->app);
+
+  if (strcmp (signal_name, "Added") == 0 &&
+      g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a(savbav))")))
+    {
+      RemoteActionInfo *info;
+      GVariantIter *iter;
+
+      g_variant_get_child (parameters, 0, "a(savbav)", &iter);
+
+      while ((info = remote_action_info_new_from_iter (iter)))
+        {
+          g_hash_table_replace (impl->actions, info->name, info);
+          g_action_group_action_added (action_group, info->name);
+        }
+
+      g_variant_iter_free (iter);
+    }
+
+  else if (strcmp (signal_name, "Removed") == 0 &&
+           g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(as)")))
+    {
+      GVariantIter *iter;
+      const gchar *name;
+
+      g_variant_get_child (parameters, 0, "as", &iter);
+      while (g_variant_iter_next (iter, "&s", &name))
+        if (g_hash_table_remove (impl->actions, name))
+          g_action_group_action_removed (action_group, name);
+      g_variant_iter_free (iter);
+    }
+
+  else if (strcmp (signal_name, "EnabledChanged") == 0 &&
+           g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sb)")))
+    {
+      RemoteActionInfo *info;
+      const gchar *name;
+      gboolean enabled;
+
+      g_variant_get (parameters, "(&sb)", &name, &enabled);
+      info = g_hash_table_lookup (impl->actions, name);
+
+      if (info && enabled != info->enabled)
+        {
+          info->enabled = enabled;
+          g_action_group_action_enabled_changed (action_group,
+                                                 info->name,
+                                                 enabled);
+        }
+    }
+
+  else if (strcmp (signal_name, "StateChanged") == 0 &&
+           g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sv)")))
+    {
+      RemoteActionInfo *info;
+      const gchar *name;
+      GVariant *state;
+
+      g_variant_get (parameters, "(&sv)", &name, &state);
+      info = g_hash_table_lookup (impl->actions, name);
+      
+      if (info && info->state &&
+          g_variant_is_of_type (state, g_variant_get_type (info->state)) &&
+          !g_variant_equal (state, info->state))
+        {
+          g_variant_unref (info->state);
+          info->state = g_variant_ref (state);
+          g_action_group_action_state_changed (action_group,
+                                               info->name,
+                                               state);
+        }
+      g_variant_unref (state);
+    }
+}
+
 GApplicationImpl *
 g_application_impl_register (GApplication       *application,
                              const gchar        *appid,
                              GApplicationFlags   flags,
-                             gboolean           *is_remote,
+                             GHashTable        **remote_actions,
                              GCancellable       *cancellable,
                              GError            **error)
 {
   const static GDBusInterfaceVTable vtable = {
     g_application_impl_method_call
   };
+  const static GDBusInterfaceVTable actions_vtable = {
+    g_application_impl_actions_method_call
+  };
   GApplicationImpl *impl;
   GVariant *reply;
   guint32 rval;
@@ -271,68 +557,91 @@ g_application_impl_register (GApplication       *application,
 
   impl->object_path = application_path_from_appid (appid);
 
-  /* don't try to be the primary instance if
-   * G_APPLICATION_IS_LAUNCHER was specified.
+  /* Only try to be the primary instance if
+   * G_APPLICATION_IS_LAUNCHER was not specified.
    */
-  if (flags & G_APPLICATION_IS_LAUNCHER)
+  if (~flags & G_APPLICATION_IS_LAUNCHER)
     {
-      impl->object_id = 0;
-      *is_remote = TRUE;
+      /* Attempt to become primary instance. */
+      impl->object_id =
+        g_dbus_connection_register_object (impl->session_bus,
+                                           impl->object_path,
+                                           (GDBusInterfaceInfo *)
+                                             &org_gtk_Application,
+                                           &vtable, impl, NULL, error);
+
+      if (impl->object_id == 0)
+        {
+          g_object_unref (impl->session_bus);
+          g_free (impl->object_path);
+          impl->session_bus = NULL;
+          impl->object_path = NULL;
 
-      return impl;
-    }
+          g_slice_free (GApplicationImpl, impl);
+          return NULL;
+        }
 
-  impl->object_id = g_dbus_connection_register_object (impl->session_bus,
-                                                       impl->object_path,
-                                                       (GDBusInterfaceInfo *)
-                                                         &org_gtk_Application,
-                                                       &vtable,
-                                                       impl, NULL,
-                                                       error);
+      impl->action_id =
+        g_dbus_connection_register_object (impl->session_bus,
+                                           impl->object_path,
+                                           (GDBusInterfaceInfo *)
+                                             &org_gtk_Actions,
+                                           &actions_vtable,
+                                           impl, NULL, error);
 
-  if (impl->object_id == 0)
-    {
-      g_object_unref (impl->session_bus);
-      g_free (impl->object_path);
-      impl->session_bus = NULL;
-      impl->object_path = NULL;
+      if (impl->action_id == 0)
+        {
+          g_dbus_connection_unregister_object (impl->session_bus,
+                                               impl->object_id);
 
-      g_slice_free (GApplicationImpl, impl);
-      return NULL;
-    }
+          g_object_unref (impl->session_bus);
+          g_free (impl->object_path);
+          impl->session_bus = NULL;
+          impl->object_path = NULL;
 
-  reply = g_dbus_connection_call_sync (impl->session_bus,
-                                       "org.freedesktop.DBus",
-                                       "/org/freedesktop/DBus",
-                                       "org.freedesktop.DBus",
-                                       "RequestName",
-                                       g_variant_new ("(su)",
-                                       /* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */
-                                                      impl->bus_name, 0x4),
-                                       G_VARIANT_TYPE ("(u)"),
-                                       0, -1, cancellable, error);
+          g_slice_free (GApplicationImpl, impl);
+          return NULL;
+        }
 
-  if (reply == NULL)
-    {
-      g_dbus_connection_unregister_object (impl->session_bus,
-                                           impl->object_id);
-      impl->object_id = 0;
+      /* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */
+      reply = g_dbus_connection_call_sync (impl->session_bus,
+                                           "org.freedesktop.DBus",
+                                           "/org/freedesktop/DBus",
+                                           "org.freedesktop.DBus",
+                                           "RequestName",
+                                           g_variant_new ("(su)",
+                                                          impl->bus_name,
+                                                          0x4),
+                                           G_VARIANT_TYPE ("(u)"),
+                                           0, -1, cancellable, error);
+
+      if (reply == NULL)
+        {
+          g_dbus_connection_unregister_object (impl->session_bus,
+                                               impl->object_id);
+          impl->object_id = 0;
 
-      g_object_unref (impl->session_bus);
-      g_free (impl->object_path);
-      impl->session_bus = NULL;
-      impl->object_path = NULL;
+          g_object_unref (impl->session_bus);
+          g_free (impl->object_path);
+          impl->session_bus = NULL;
+          impl->object_path = NULL;
 
-      g_slice_free (GApplicationImpl, impl);
-      return NULL;
-    }
+          g_slice_free (GApplicationImpl, impl);
+          return NULL;
+        }
 
-  g_variant_get (reply, "(u)", &rval);
-  g_variant_unref (reply);
+      g_variant_get (reply, "(u)", &rval);
+      g_variant_unref (reply);
 
-  /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
-  if ((*is_remote = (rval == 3)))
-    {
+      /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
+      if (rval != 3)
+        {
+          /* We are the primary instance. */
+          *remote_actions = NULL;
+          return impl;
+        }
+
+      /* We didn't make it.  Drop our service-side stuff. */
       g_dbus_connection_unregister_object (impl->session_bus,
                                            impl->object_id);
       impl->object_id = 0;
@@ -349,6 +658,70 @@ g_application_impl_register (GApplication       *application,
         }
     }
 
+  /* We are non-primary.  Try to get the primary's list of actions.
+   * This also serves as a mechanism to ensure that the primary exists
+   * (ie: DBus service files installed correctly, etc).
+   */
+  impl->signal_id = 
+    g_dbus_connection_signal_subscribe (impl->session_bus, impl->bus_name,
+                                        "org.gtk.Actions", NULL,
+                                        impl->object_path, NULL,
+                                        G_DBUS_SIGNAL_FLAGS_NONE,
+                                        g_application_impl_action_signal,
+                                        impl, NULL);
+
+  reply = g_dbus_connection_call_sync (impl->session_bus, impl->bus_name,
+                                       impl->object_path, "org.gtk.Actions",
+                                       "DescribeAll", NULL,
+                                       G_VARIANT_TYPE ("(a(savbav))"),
+                                       G_DBUS_CALL_FLAGS_NONE, -1,
+                                       cancellable, error);
+
+  if (reply == NULL)
+    {
+      /* The primary appears not to exist.  Fail the registration. */
+      g_object_unref (impl->session_bus);
+      g_free (impl->object_path);
+      impl->session_bus = NULL;
+      impl->object_path = NULL;
+
+      g_slice_free (GApplicationImpl, impl);
+      return NULL;
+    }
+
+  /* Create and populate the hashtable */
+  {
+    GVariant *descriptions;
+    GVariantIter iter;
+    GVariant *param_type;
+    gboolean enabled;
+    GVariant *state;
+    gchar *name;
+
+    *remote_actions = g_hash_table_new (g_str_hash, g_str_equal);
+    descriptions = g_variant_get_child_value (reply, 0);
+    g_variant_iter_init (&iter, descriptions);
+
+    while (g_variant_iter_next (&iter, "(s avb@av)", &name,
+                                &param_type, &enabled, &state))
+      {
+        RemoteActionInfo *action;
+
+        action = g_slice_new (RemoteActionInfo);
+        action->parameter_type = g_variant_type_copy (
+                                   g_variant_type_element (
+                                     g_variant_get_type (param_type)));
+        action->enabled = enabled;
+        action->state = state;
+
+        g_hash_table_insert (*remote_actions, name, action);
+        g_variant_unref (param_type);
+      }
+
+    g_variant_unref (descriptions);
+  }
+
+
   return impl;
 }
 
@@ -499,6 +872,46 @@ g_application_impl_command_line (GApplicationImpl  *impl,
 }
 
 void
+g_application_impl_change_action_state (GApplicationImpl *impl,
+                                        const gchar      *action_name,
+                                        GVariant         *value,
+                                        GVariant         *platform_data)
+{
+  g_dbus_connection_call (impl->session_bus,
+                          impl->bus_name,
+                          impl->object_path,
+                          "org.gtk.Actions",
+                          "SetState",
+                          g_variant_new ("(sv a{sv})", action_name,
+                                         value, platform_data),
+                          NULL, 0, -1, NULL, NULL, NULL);
+}
+
+void
+g_application_impl_activate_action (GApplicationImpl *impl,
+                                    const gchar      *action_name,
+                                    GVariant         *parameter,
+                                    GVariant         *platform_data)
+{
+  GVariant *param;
+
+  if (parameter)
+    parameter = g_variant_new_variant (parameter);
+
+  param = g_variant_new_array (G_VARIANT_TYPE_VARIANT,
+                               &parameter, parameter != NULL);
+
+  g_dbus_connection_call (impl->session_bus,
+                          impl->bus_name,
+                          impl->object_path,
+                          "org.gtk.Actions",
+                          "Activate",
+                          g_variant_new ("(s av@a{sv})", action_name,
+                                         param, platform_data),
+                          NULL, 0, -1, NULL, NULL, NULL);
+}
+
+void
 g_application_impl_flush (GApplicationImpl *impl)
 {
   g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
diff --git a/gio/gapplicationimpl.h b/gio/gapplicationimpl.h
index 35a34b9..95f0b76 100644
--- a/gio/gapplicationimpl.h
+++ b/gio/gapplicationimpl.h
@@ -2,6 +2,15 @@
 
 typedef struct _GApplicationImpl GApplicationImpl;
 
+typedef struct
+{
+  gchar *name;
+
+  GVariantType *parameter_type;
+  gboolean      enabled;
+  GVariant     *state;
+} RemoteActionInfo;
+
 G_GNUC_INTERNAL
 void                    g_application_impl_destroy                      (GApplicationImpl   *impl);
 
@@ -9,7 +18,7 @@ G_GNUC_INTERNAL
 GApplicationImpl *      g_application_impl_register                     (GApplication       *application,
                                                                          const gchar        *appid,
                                                                          GApplicationFlags   flags,
-                                                                         gboolean           *is_remote,
+                                                                         GHashTable        **remote_actions,
                                                                          GCancellable       *cancellable,
                                                                          GError            **error);
 
@@ -30,4 +39,16 @@ int                     g_application_impl_command_line                 (GApplic
                                                                          GVariant           *platform_data);
 
 G_GNUC_INTERNAL
+void                    g_application_impl_change_action_state          (GApplicationImpl   *impl,
+                                                                         const gchar        *action_name,
+                                                                         GVariant           *value,
+                                                                         GVariant           *platform_data);
+
+G_GNUC_INTERNAL
+void                    g_application_impl_activate_action              (GApplicationImpl   *impl,
+                                                                         const gchar        *action_name,
+                                                                         GVariant           *parameter,
+                                                                         GVariant           *platform_data);
+
+G_GNUC_INTERNAL
 void                    g_application_impl_flush                        (GApplicationImpl   *impl);
diff --git a/gio/giotypes.h b/gio/giotypes.h
index ffd38ef..b9eb908 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -47,6 +47,7 @@ typedef struct _GSimplePermission             GSimplePermission;
 typedef struct _GZlibCompressor               GZlibCompressor;
 typedef struct _GZlibDecompressor             GZlibDecompressor;
 
+typedef struct _GDBusActionGroup              GDBusActionGroup;
 typedef struct _GSimpleActionGroup            GSimpleActionGroup;
 typedef struct _GActionGroup                  GActionGroup;
 typedef struct _GSimpleAction                 GSimpleAction;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]