2009-01-12 Mark Doffman <mark.doffman@codethink.co.uk>
authorMark Doffman <mdoff@silver-wind.(none)>
Mon, 12 Jan 2009 15:57:31 +0000 (15:57 +0000)
committerMark Doffman <mdoff@silver-wind.(none)>
Mon, 12 Jan 2009 15:57:31 +0000 (15:57 +0000)
        *atk-adaptor/accessible-adaptor.c
         Add missing header file, fix warning.

        *atk-adaptor/accessible-marshaller.*
         Move the accessible cache item marshalling
         here from tree-adaptor.

        *atk-adaptor/tree-adaptor.c
         Move out the cache item marshalling.

        *atk-adaptor/accessible-register.c
         Add ancestor registration, fix
         the update function. It previously
         sent an accessible rather than a list
         of accessibles to the tree-adaptor.

atk-adaptor/accessible-adaptor.c
atk-adaptor/accessible-marshaller.c
atk-adaptor/accessible-marshaller.h
atk-adaptor/accessible-register.c
atk-adaptor/tree-adaptor.c

index fd3df3c..d93a57f 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "spi-common/spi-dbus.h"
 #include "accessible-marshaller.h"
+#include "accessible-register.h"
 
 static dbus_bool_t
 impl_get_name (DBusMessageIter *iter, void *user_data)
index a60f2c8..cff97fa 100644 (file)
@@ -23,6 +23,8 @@
 #include "accessible-register.h"
 #include "accessible-marshaller.h"
 
+#include "spi-common/spi-dbus.h"
+
 /*---------------------------------------------------------------------------*/
 
 /*
@@ -71,5 +73,207 @@ spi_dbus_return_v_object (DBusMessageIter *iter, AtkObject *obj, int unref)
   return droute_return_v_object (iter, path);
 }
 
+/*---------------------------------------------------------------------------*/
+
+static void
+append_atk_object_interfaces (AtkObject *object, DBusMessageIter *iter)
+{
+  const gchar *itf;
+
+  itf = SPI_DBUS_INTERFACE_ACCESSIBLE;
+  dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+
+  if (ATK_IS_ACTION (object))
+    {
+      itf = SPI_DBUS_INTERFACE_ACTION;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_COMPONENT (object))
+    {
+      itf = SPI_DBUS_INTERFACE_COMPONENT;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_EDITABLE_TEXT (object))
+    {
+      itf = SPI_DBUS_INTERFACE_EDITABLE_TEXT;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_TEXT (object))
+    {
+      itf = SPI_DBUS_INTERFACE_TEXT;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_HYPERTEXT (object))
+    {
+      itf = SPI_DBUS_INTERFACE_HYPERTEXT;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_IMAGE (object))
+    {
+      itf = SPI_DBUS_INTERFACE_IMAGE;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_SELECTION (object))
+    {
+      itf = SPI_DBUS_INTERFACE_SELECTION;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_TABLE (object))
+    {
+      itf = SPI_DBUS_INTERFACE_TABLE;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_VALUE (object))
+    {
+      itf = SPI_DBUS_INTERFACE_VALUE;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_STREAMABLE_CONTENT (object))
+    {
+      itf = "org.freedesktop.atspi.StreamableContent";
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_DOCUMENT (object))
+    {
+      itf = "org.freedesktop.atspi.Collection";
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+      itf = SPI_DBUS_INTERFACE_DOCUMENT;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+
+  if (ATK_IS_HYPERLINK_IMPL (object))
+    {
+      itf = SPI_DBUS_INTERFACE_HYPERLINK;
+      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
+    }
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Marshals the given AtkObject into the provided D-Bus iterator.
+ *
+ * The object is marshalled including all its client side cache data.
+ * The format of the structure is (ooaoassusau).
+ * This is used in the updateTree signal and the getTree method
+ * of the org.freedesktop.atspi.Tree interface.
+ *
+ * To marshal an object its parent, and all its children must already
+ * be registered with D-Bus and have been given a D-Bus object path.
+ */
+void
+spi_atk_append_accessible(AtkObject *obj, gpointer iter)
+{
+  DBusMessageIter *iter_array;
+  DBusMessageIter iter_struct, iter_sub_array;
+  dbus_int32_t states [2];
+  int count;
+
+  const char *name, *desc;
+  int i;
+  dbus_uint32_t role;
+  GSList *l;
+
+  iter_array = (DBusMessageIter *) iter;
+
+  dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct);
+    {
+      AtkObject *parent;
+      gchar *path, *path_parent;
+
+      /* Marshall object path */
+      path = atk_dbus_object_to_path (obj);
+      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
+
+      /* Marshall parent */
+      parent = atk_object_get_parent(obj);
+      if (parent == NULL)
+        {
+          path_parent = g_strdup("/");
+        }
+      else
+        {
+          path_parent = atk_dbus_object_to_path (parent);
+          if (!path_parent)
+            {
+              g_critical ("AT-SPI: Object registered without registering parent");
+              path_parent = g_strdup("/");
+            }
+        }
+      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path_parent);
+      g_free(path_parent);
+
+      /* Marshall children */
+      dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "o", &iter_sub_array);
+        {
+          gint childcount, i;
+
+          childcount = atk_object_get_n_accessible_children (obj);
+          for (i = 0; i < childcount; i++)
+            {
+              AtkObject *child;
+              gchar *child_path;
+
+              child = atk_object_ref_accessible_child (obj, i);
+              child_path = atk_dbus_object_to_path (child);
+              g_object_unref(G_OBJECT(child));
+              if (G_LIKELY (child_path))
+                {
+                  dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_OBJECT_PATH, &child_path);
+                }
+              else
+                {
+                  g_critical ("AT-SPI: Child object exists in accessible tree but has not been registered");
+                  g_free (child_path);
+                }
+            }
+        }
+      dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
+
+      /* Marshall interfaces */
+      dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_sub_array);
+      append_atk_object_interfaces (obj, &iter_sub_array);
+      dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
+
+      /* Marshall name */
+      name = atk_object_get_name (obj);
+      if (!name)
+        name = "";
+      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
+
+      /* Marshall role */
+      role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
+      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
+
+      /* Marshall description */
+      desc = atk_object_get_description (obj);
+      if (!desc)
+        desc = "";
+      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
+
+      g_free(path);
+
+      /* Marshall state set */
+      spi_atk_state_to_dbus_array (obj, &states);
+      dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "u", &iter_sub_array);
+      for (count = 0; count < 2; count++)
+        {
+          dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_UINT32, &states[count]);
+        }
+      dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
+    }
+  dbus_message_iter_close_container (iter_array, &iter_struct);
+}
+
 /*END------------------------------------------------------------------------*/
 
index b133abc..afeeb1b 100644 (file)
@@ -21,8 +21,8 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __ACCESSIBLE_MARSHALLER__
-#define __ACCESSIBLE_MARSHALLER__
+#ifndef ACCESSIBLE_MARSHALLER
+#define ACCESSIBLE_MARSHALLER
 
 #include <dbus/dbus.h>
 #include <atk/atk.h>
@@ -33,4 +33,7 @@ spi_dbus_return_object (DBusMessage *message, AtkObject *obj, gboolean unref);
 dbus_bool_t
 spi_dbus_return_v_object (DBusMessageIter *iter, AtkObject *obj, int unref);
 
-#endif /* __ACCESSIBLE_MARSHALLER__ */
+void
+spi_atk_append_accessible(AtkObject *obj, gpointer iter);
+
+#endif /* ACCESSIBLE_MARSHALLER */
index 61521e6..6b89ba5 100644 (file)
@@ -56,9 +56,7 @@
  * In addition to accessing the objects remotely we must be able to update
  * the client side cache. This is done using the 'update' signal of the 
  * org.freedesktop.atspi.Tree interface. The update signal should send out
- * all of the cacheable data for each AtkObject that has changed since the
- * last update. It should also provide a list of objects that have been
- * removed since the last update.
+ * all of the cacheable data for an Accessible object.
  */
 
 GHashTable *ref2ptr = NULL; /* Used for converting a D-Bus path (Reference) to the object pointer */
@@ -123,6 +121,46 @@ deregister_accessible(gpointer data, GObject *accessible)
 
 /*---------------------------------------------------------------------------*/
 
+/* FIXME
+ * Horrible hack warning.
+ *
+ * Problem 1:
+ *
+ * In an ideal world there would be a signal "Accessible created" that we could
+ * use to register all new AtkObjects with D-Bus. The AtkObjects would be
+ * created at the time of their implementing widget. This is how things
+ * happen in Qt and its damn sensible.
+ *
+ * In GTK Gail objects are created 'lazily' when they are accessed. This is
+ * presumably an optimization to reduce memory. I happen to think its a very
+ * very bad one. Anyway, there is no signal, and Gail objects don't get created
+ * automatically for each widget, so how do we register AtkObjects with D-Bus?
+ *
+ * Answer, we have one guaranteed AtkObject, the root. We traverse the tree provided
+ * by the root object, registering as we go. When new objects are created we use
+ * the children-changed signal of their parent to find out. As we don't know
+ * if a new object has any children that have not been registered we must traverse
+ * the decendants of every new object to find AtkObjects that have not been registered.
+ *
+ * Problem 2:
+ *
+ * For whatever reason events are generated for objects that have not yet been
+ * registered with D-Bus. This means that when translating an Atk signal to an
+ * AT-SPI one it may be neccessary to register objects first. 
+ *
+ * The caveat is that when registering an object somewhere in the middle of the
+ * AtkObject tree there is no guarantee that its parent objects have been registered.
+ * So when registering a new object we also need to register its parents back to the
+ * root object.
+ *
+ * Other solutions:
+ *
+ * The original solution was completely recursive. So when the reference of an AtkObject
+ * was requested it would be registered there and then. I didn't like the recursive
+ * solution, it was a very very deep stack in some cases.
+ *
+ */
+
 /*
  * This function registers the object so that it is exported
  * over D-Bus and schedules an update to client side cache.
@@ -147,12 +185,22 @@ export (GList **uplist, AtkObject *accessible)
 }
 
 /*
+ * Exports all the dependencies of an AtkObject.
+ * This is the subtree and the ancestors.
+ *
+ * Dependencies are the objects that get included in the
+ * cache, and therefore need to be registered before the
+ * update signal is sent.
+ *
  * This does a depth first traversal of a subtree of AtkObject
  * and exports them as Accessible objects if they are not exported
  * already.
+ *
+ * It exports all ancestors of the object if they are not
+ * exported already.
  */
 static guint
-export_subtree (AtkObject *accessible)
+export_deps (AtkObject *accessible)
 {
   AtkObject *current, *tmp;
   GQueue    *stack;
@@ -160,6 +208,13 @@ export_subtree (AtkObject *accessible)
   guint      i, ref;
   gboolean   recurse;
 
+
+  /* Export subtree including object itself */
+  /*========================================*/
+  ref = atk_dbus_object_to_ref (accessible);
+  if (ref)
+      return ref;
+
   stack = g_queue_new ();
 
   current = g_object_ref (accessible);
@@ -210,6 +265,15 @@ export_subtree (AtkObject *accessible)
           g_queue_pop_head (stack);
         }
     }
+
+  /* Export all neccessary ancestors of the object */
+  /*===============================================*/
+  current = atk_object_get_parent (accessible);
+  while (current && !atk_dbus_object_to_ref (current))
+    {
+      export (&uplist, current);
+    }
+
   spi_emit_cache_update (uplist, atk_adaptor_app_data->bus);
   g_list_free (uplist);
   return ref;
@@ -224,11 +288,7 @@ atk_dbus_register_accessible (AtkObject *accessible)
   guint ref;
   g_assert(ATK_IS_OBJECT(accessible));
 
-  ref = atk_dbus_object_to_ref (accessible);
-  if (!ref)
-      return export_subtree (accessible);
-  else
-      return ref;
+  return export_deps (accessible);
 }
 
 /* Called when an already registered object is updated in such a
@@ -237,13 +297,16 @@ atk_dbus_register_accessible (AtkObject *accessible)
 guint
 atk_dbus_update_accessible (AtkObject *accessible)
 {
-  guint ref = 0;
+  guint  ref = 0;
+  GList *uplist = NULL;
   g_assert(ATK_IS_OBJECT(accessible));
 
   ref = atk_dbus_object_to_ref (accessible);
   if (ref)
     {
-      spi_emit_cache_update (accessible, atk_adaptor_app_data->bus);
+      uplist = g_list_prepend (uplist, accessible);
+      spi_emit_cache_update (uplist, atk_adaptor_app_data->bus);
+      g_list_free (uplist);
     }
   return ref;
 }
index a4411d0..3f85f9b 100644 (file)
 
 #include "bridge.h"
 #include "accessible-register.h"
+#include "accessible-marshaller.h"
 #include "spi-common/spi-dbus.h"
 
 /*---------------------------------------------------------------------------*/
 
+/* For use as a GHFunc */
 static void
-append_atk_object_interfaces (AtkObject *object, DBusMessageIter *iter)
-{
-  const gchar *itf;
-
-  itf = SPI_DBUS_INTERFACE_ACCESSIBLE;
-  dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-
-  if (ATK_IS_ACTION (object))
-    {
-      itf = SPI_DBUS_INTERFACE_ACTION;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_COMPONENT (object))
-    {
-      itf = SPI_DBUS_INTERFACE_COMPONENT;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_EDITABLE_TEXT (object))
-    {
-      itf = SPI_DBUS_INTERFACE_EDITABLE_TEXT;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_TEXT (object))
-    {
-      itf = SPI_DBUS_INTERFACE_TEXT;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_HYPERTEXT (object))
-    {
-      itf = SPI_DBUS_INTERFACE_HYPERTEXT;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_IMAGE (object))
-    {
-      itf = SPI_DBUS_INTERFACE_IMAGE;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_SELECTION (object))
-    {
-      itf = SPI_DBUS_INTERFACE_SELECTION;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_TABLE (object))
-    {
-      itf = SPI_DBUS_INTERFACE_TABLE;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_VALUE (object))
-    {
-      itf = SPI_DBUS_INTERFACE_VALUE;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_STREAMABLE_CONTENT (object))
-    {
-      itf = "org.freedesktop.atspi.StreamableContent";
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_DOCUMENT (object))
-    {
-      itf = "org.freedesktop.atspi.Collection";
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-      itf = SPI_DBUS_INTERFACE_DOCUMENT;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-
-  if (ATK_IS_HYPERLINK_IMPL (object))
-    {
-      itf = SPI_DBUS_INTERFACE_HYPERLINK;
-      dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
-    }
-}
-
-/*---------------------------------------------------------------------------*/
-
-static const char *dumm = "/APath/1";
-
-
-/*
- * Marshals the given AtkObject into the provided D-Bus iterator.
- *
- * The object is marshalled including all its client side cache data.
- * The format of the structure is (ooaoassusau).
- * This is used in the updateTree signal and the getTree method
- * of the org.freedesktop.atspi.Tree interface.
- */
-static void
-append_accessible(gpointer obj_data, gpointer iter)
+append_accessible_hf (gpointer key, gpointer obj_data, gpointer iter)
 {
-  AtkObject *obj;
-  DBusMessageIter *iter_array;
-  DBusMessageIter iter_struct, iter_sub_array;
-  dbus_int32_t states [2];
-  int count;
-
-  const char *name, *desc;
-  int i;
-  dbus_uint32_t role;
-  GSList *l;
-
-  obj = ATK_OBJECT(obj_data);
-  iter_array = (DBusMessageIter *) iter;
-
-  dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct);
-    {
-      AtkObject *parent;
-      gchar *path, *path_parent;
-
-      path = atk_dbus_object_to_path (obj);
-      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
-
-      parent = atk_object_get_parent(obj);
-      if (parent == NULL)
-        path_parent = g_strdup("/");
-      else
-        path_parent = atk_dbus_object_to_path (parent);
-      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path_parent);
-      g_free(path_parent);
-
-      dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "o", &iter_sub_array);
-        {
-          gint childcount, i;
-
-          childcount = atk_object_get_n_accessible_children (obj);
-          for (i = 0; i < childcount; i++)
-            {
-              AtkObject *child;
-              gchar *child_path;
-
-              child = atk_object_ref_accessible_child (obj, i);
-              child_path = atk_dbus_object_to_path (child);
-              g_object_unref(G_OBJECT(child));
-              if (G_LIKELY (child_path))
-                {
-                  dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_OBJECT_PATH, &child_path);
-                }
-              else
-                {
-                  g_critical ("AT-SPI: Child object exists in accessible tree but has not been registered");
-                  g_free (child_path);
-                }
-            }
-        }
-      dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
-
-      dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_sub_array);
-      append_atk_object_interfaces (obj, &iter_sub_array);
-      dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
-
-      name = atk_object_get_name (obj);
-      if (!name)
-        name = "";
-      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
-
-      role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
-      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
-
-      desc = atk_object_get_description (obj);
-      if (!desc)
-        desc = "";
-      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
-
-      g_free(path);
-    }
-  spi_atk_state_to_dbus_array (obj, &states);
-      dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "u", &iter_sub_array);
-  for (count = 0; count < 2; count++)
-    dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_UINT32, &states[count]);
-      dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
-  dbus_message_iter_close_container (iter_array, &iter_struct);
+  spi_atk_append_accessible (ATK_OBJECT(obj_data), iter);
 }
 
-/* For use as a GHFunc */
+/* For use as a GFunc */
 static void
-append_accessible_hf (gpointer key, gpointer obj_data, gpointer iter)
+append_accessible_lf (gpointer obj_data, gpointer iter)
 {
-  append_accessible (obj_data, iter);
+  spi_atk_append_accessible (ATK_OBJECT(obj_data), iter);
 }
 
 /*---------------------------------------------------------------------------*/
@@ -252,7 +78,7 @@ spi_emit_cache_update (const GList *accessibles, DBusConnection *bus)
 
    dbus_message_iter_init_append (message, &iter);
    dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassusau)", &iter_array);
-   g_list_foreach ((GList *)accessibles, append_accessible, &iter_array);
+   g_list_foreach ((GList *)accessibles, append_accessible_lf, &iter_array);
    dbus_message_iter_close_container(&iter, &iter_array);
 
    dbus_connection_send(bus, message, NULL);