cleanup
[platform/upstream/glib.git] / gio / gactiongroupexporter.c
index 9c78487..17a05fa 100644 (file)
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Authors: Ryan Lortie <desrt@desrt.ca>
  */
 #include "gactiongroupexporter.h"
 
 #include "gdbusmethodinvocation.h"
+#include "gremoteactiongroup.h"
 #include "gdbusintrospection.h"
 #include "gdbusconnection.h"
 #include "gactiongroup.h"
-#include "gapplication.h"
 #include "gdbuserror.h"
 
 /**
  * SECTION:gactiongroupexporter
  * @title: GActionGroup exporter
+ * @include: gio/gio.h
  * @short_description: Export GActionGroups on D-Bus
  * @see_also: #GActionGroup, #GDBusActionGroup
  *
  * detail.
  *
  * To access an exported #GActionGroup remotely, use
- * g_dbus_action_group_new() to obtain a #GDBusActionGroup.
+ * g_dbus_action_group_get() to obtain a #GDBusActionGroup.
  */
 
-G_GNUC_INTERNAL GVariant *
+static GVariant *
 g_action_group_describe_action (GActionGroup *action_group,
                                 const gchar  *name)
 {
@@ -79,104 +78,14 @@ g_action_group_describe_action (GActionGroup *action_group,
   return g_variant_builder_end (&builder);
 }
 
-/* The org.gtk.Actions interface
- * =============================
- *
- * This interface describes a group of actions.
- *
- * Each action:
- * - has a unique string name
- * - can be activated
- * - optionally has a parameter type that must be given to the activation
- * - has an enabled state that may be true or false
- * - optionally has a state which can change value, but not type
- *
- * Methods
- * -------
- *
- * List :: () → (as)
- *
- *   Lists the names of the actions exported at this object path.
- *
- * Describe :: (s) → (bgav)
- *
- *   Describes a single action, or a given name.
- *
- *   The return value has the following components:
- *   b: specifies if the action is currently enabled. This is
- *      a hint that attempting to interact with the action will
- *      produce no effect.
- *   g: specifies the optional parameter type. If not "",
- *      the string specifies the type of argument that must
- *      be passed to the activation.
- *   av: specifies the optional state. If not empty, the array
- *       contains the current value of the state as a variant
- *
- * DescribeAll :: () → (a{s(bgav)})
- *
- *   Describes all actions in a single round-trip.
- *
- *   The dictionary maps action name strings to their descriptions
- *   (in the format discussed above).
- *
- * Activate :: (sava{sv}) → ()
- *
- *   Requests activation of the named action.
- *
- *   The action is named by the first parameter (s).
- *
- *   If the action activation requires a parameter then this parameter
- *   must be given in the second parameter (av). If there is no parameter
- *   to be specified, the array must be empty.
- *
- *   The final parameter (a{sv}) is a list of "platform data".
- *
- *   This method is not guaranteed to have any particular effect. The
- *   implementation may take some action (including changing the state
- *   of the action, if it is stateful) or it may take none at all. In
- *   particular, callers should expect their request to be completely
- *   ignored when the enabled flag is false (but even this is not
- *   guaranteed).
- *
- * SetState :: (sva{sv}) → ()
- *
- *   Requests the state of an action to be changed to the given value.
- *
- *   The action is named by the first parameter (s).
- *
- *   The requested new state is given in the second parameter (v).
- *   It must be equal in type to the existing state.
- *
- *   The final parameter (a{sv}) is a list of "platform data".
- *
- *   This method is not guaranteed to have any particular effect.
- *   The implementation of an action can choose to ignore the requested
- *   state change, or choose to change its state to something else or
- *   to trigger other side effects. In particular, callers should expect
- *   their request to be completely ignored when the enabled flag is
- *   false (but even this is not guaranteed).
- *
- * Signals
- * -------
- *
- * Changed :: (asa{sb}a{sv}a{s(bgav)})
- *
- *   Signals that some change has occured to the action group.
- *
- *   Four separate types of changes are possible, and the 4 parameters
- *   of the change signal reflect these possibilities:
- *   as: a list of removed actions
- *   a{sb}: a list of actions that had their enabled flag changed
- *   a{sv}: a list of actions that had their state changed
- *   a{s(bgav)}: a list of new actions added in the same format as
- *               the return value of the DescribeAll method
- */
-
 /* Using XML saves us dozens of relocations vs. using the introspection
  * structure types.  We only need to burn cycles and memory if we
  * actually use the exporter -- not in every single app using GIO.
  *
  * It's also a lot easier to read. :)
+ *
+ * For documentation of this interface, see
+ * https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
  */
 const char org_gtk_Actions_xml[] =
   "<node>"
@@ -216,10 +125,10 @@ typedef struct
 {
   GActionGroup    *action_group;
   GDBusConnection *connection;
+  GMainContext    *context;
   gchar           *object_path;
   GHashTable      *pending_changes;
-  guint            pending_id;
-  gulong           signal_ids[4];
+  GSource         *pending_source;
 } GActionGroupExporter;
 
 #define ACTION_ADDED_EVENT             (1u<<0)
@@ -294,7 +203,7 @@ g_action_group_exporter_dispatch_events (gpointer user_data)
                                                 &state_changes, &adds),
                                  NULL);
 
-  exporter->pending_id = 0;
+  exporter->pending_source = NULL;
 
   return FALSE;
 }
@@ -320,15 +229,24 @@ g_action_group_exporter_set_events (GActionGroupExporter *exporter,
     g_hash_table_remove (exporter->pending_changes, name);
 
   have_events = g_hash_table_size (exporter->pending_changes) > 0;
-  is_queued = exporter->pending_id > 0;
+  is_queued = exporter->pending_source != NULL;
 
   if (have_events && !is_queued)
-    exporter->pending_id = g_idle_add (g_action_group_exporter_dispatch_events, exporter);
+    {
+      GSource *source;
+
+      source = g_idle_source_new ();
+      exporter->pending_source = source;
+      g_source_set_callback (source, g_action_group_exporter_dispatch_events, exporter, NULL);
+      g_source_set_name (source, "[gio] g_action_group_exporter_dispatch_events");
+      g_source_attach (source, exporter->context);
+      g_source_unref (source);
+    }
 
   if (!have_events && is_queued)
     {
-      g_source_remove (exporter->pending_id);
-      exporter->pending_id = 0;
+      g_source_destroy (exporter->pending_source);
+      exporter->pending_source = NULL;
     }
 }
 
@@ -435,24 +353,6 @@ g_action_group_exporter_action_enabled_changed (GActionGroup *action_group,
 }
 
 static void
-g_action_group_exporter_pre_emit (GActionGroupExporter *exporter,
-                                  GVariant             *platform_data)
-{
-  if (G_IS_APPLICATION (exporter->action_group))
-    G_APPLICATION_GET_CLASS (exporter->action_group)
-      ->before_emit (G_APPLICATION (exporter->action_group), platform_data);
-}
-
-static void
-g_action_group_exporter_post_emit (GActionGroupExporter *exporter,
-                                   GVariant             *platform_data)
-{
-  if (G_IS_APPLICATION (exporter->action_group))
-    G_APPLICATION_GET_CLASS (exporter->action_group)
-      ->after_emit (G_APPLICATION (exporter->action_group), platform_data);
-}
-
-static void
 org_gtk_Actions_method_call (GDBusConnection       *connection,
                              const gchar           *sender,
                              const gchar           *object_path,
@@ -480,6 +380,14 @@ org_gtk_Actions_method_call (GDBusConnection       *connection,
       GVariant *desc;
 
       g_variant_get (parameters, "(&s)", &name);
+
+      if (!g_action_group_has_action (exporter->action_group, name))
+        {
+          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                                 "The named action ('%s') does not exist.", name);
+          return;
+        }
+
       desc = g_action_group_describe_action (exporter->action_group, name);
       result = g_variant_new ("(@(bgav))", desc);
     }
@@ -515,9 +423,11 @@ org_gtk_Actions_method_call (GDBusConnection       *connection,
       g_variant_iter_next (iter, "v", &parameter);
       g_variant_iter_free (iter);
 
-      g_action_group_exporter_pre_emit (exporter, platform_data);
-      g_action_group_activate_action (exporter->action_group, name, parameter);
-      g_action_group_exporter_post_emit (exporter, platform_data);
+      if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
+        g_remote_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
+                                                    name, parameter, platform_data);
+      else
+        g_action_group_activate_action (exporter->action_group, name, parameter);
 
       if (parameter)
         g_variant_unref (parameter);
@@ -532,9 +442,13 @@ org_gtk_Actions_method_call (GDBusConnection       *connection,
       GVariant *state;
 
       g_variant_get (parameters, "(&sv@a{sv})", &name, &state, &platform_data);
-      g_action_group_exporter_pre_emit (exporter, platform_data);
-      g_action_group_change_action_state (exporter->action_group, name, state);
-      g_action_group_exporter_post_emit (exporter, platform_data);
+
+      if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
+        g_remote_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
+                                                        name, state, platform_data);
+      else
+        g_action_group_change_action_state (exporter->action_group, name, state);
+
       g_variant_unref (platform_data);
       g_variant_unref (state);
     }
@@ -549,10 +463,21 @@ static void
 g_action_group_exporter_free (gpointer user_data)
 {
   GActionGroupExporter *exporter = user_data;
-  gint i;
 
-  for (i = 0; i < G_N_ELEMENTS (exporter->signal_ids); i++)
-    g_signal_handler_disconnect (exporter->action_group, exporter->signal_ids[i]);
+  g_signal_handlers_disconnect_by_func (exporter->action_group,
+                                        g_action_group_exporter_action_added, exporter);
+  g_signal_handlers_disconnect_by_func (exporter->action_group,
+                                        g_action_group_exporter_action_enabled_changed, exporter);
+  g_signal_handlers_disconnect_by_func (exporter->action_group,
+                                        g_action_group_exporter_action_state_changed, exporter);
+  g_signal_handlers_disconnect_by_func (exporter->action_group,
+                                        g_action_group_exporter_action_removed, exporter);
+
+  g_hash_table_unref (exporter->pending_changes);
+  if (exporter->pending_source)
+    g_source_destroy (exporter->pending_source);
+
+  g_main_context_unref (exporter->context);
   g_object_unref (exporter->connection);
   g_object_unref (exporter->action_group);
   g_free (exporter->object_path);
@@ -572,7 +497,7 @@ g_action_group_exporter_free (gpointer user_data)
  * The implemented D-Bus API should be considered private.  It is
  * subject to change in the future.
  *
- * A given object path can only have one action group exported on it.
+ * A given object path can only have one action group exported on it.
  * If this constraint is violated, the export will fail and 0 will be
  * returned (with @error set accordingly).
  *
@@ -580,6 +505,15 @@ g_action_group_exporter_free (gpointer user_data)
  * g_dbus_connection_unexport_action_group() with the return value of
  * this function.
  *
+ * The thread default main context is taken at the time of this call.
+ * All incoming action activations and state change requests are
+ * reported from this context.  Any changes on the action group that
+ * cause it to emit signals must also come from this same context.
+ * Since incoming action activations and state change requests are
+ * rather likely to cause changes on the action group, this effectively
+ * limits a given action group to being exported from only one main
+ * context.
+ *
  * Returns: the ID of the export (never zero), or 0 in case of failure
  *
  * Since: 2.32
@@ -620,24 +554,21 @@ g_dbus_connection_export_action_group (GDBusConnection  *connection,
       return 0;
     }
 
+  exporter->context = g_main_context_ref_thread_default ();
   exporter->pending_changes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-  exporter->pending_id = 0;
+  exporter->pending_source = NULL;
   exporter->action_group = g_object_ref (action_group);
   exporter->connection = g_object_ref (connection);
   exporter->object_path = g_strdup (object_path);
 
-  exporter->signal_ids[0] =
-    g_signal_connect (action_group, "action-added",
-                      G_CALLBACK (g_action_group_exporter_action_added), exporter);
-  exporter->signal_ids[1] =
-    g_signal_connect (action_group, "action-removed",
-                      G_CALLBACK (g_action_group_exporter_action_removed), exporter);
-  exporter->signal_ids[2] =
-    g_signal_connect (action_group, "action-state-changed",
-                      G_CALLBACK (g_action_group_exporter_action_state_changed), exporter);
-  exporter->signal_ids[3] =
-    g_signal_connect (action_group, "action-enabled-changed",
-                      G_CALLBACK (g_action_group_exporter_action_enabled_changed), exporter);
+  g_signal_connect (action_group, "action-added",
+                    G_CALLBACK (g_action_group_exporter_action_added), exporter);
+  g_signal_connect (action_group, "action-removed",
+                    G_CALLBACK (g_action_group_exporter_action_removed), exporter);
+  g_signal_connect (action_group, "action-state-changed",
+                    G_CALLBACK (g_action_group_exporter_action_state_changed), exporter);
+  g_signal_connect (action_group, "action-enabled-changed",
+                    G_CALLBACK (g_action_group_exporter_action_enabled_changed), exporter);
 
   return id;
 }