2009-01-09 Mark Doffman <mark.doffman@codethink.co.uk>
authorMark Doffman <mdoff@silver-wind.(none)>
Fri, 9 Jan 2009 11:38:21 +0000 (11:38 +0000)
committerMark Doffman <mdoff@silver-wind.(none)>
Fri, 9 Jan 2009 11:39:49 +0000 (11:39 +0000)
        Large reorg to the protocol, atk-adaptor and
        pyatspi bindings. Changes the tree cache update
        so that signals are queued as changes are made rather
        than in idle loop. This preserves event ordering
        and is probably as efficient because the D-Bus
        queue is not flushed.

atk-adaptor/accessible.c
atk-adaptor/atk-dbus.c
atk-adaptor/atk-dbus.h
atk-adaptor/bridge.c
atk-adaptor/collection.c
atk-adaptor/event.c
atk-adaptor/tree.c
atk-adaptor/tree.h
pyatspi/accessiblecache.py
xml/org.freedesktop.atspi.Tree.xml

index 9d2b62a..e1e34aa 100644 (file)
@@ -138,7 +138,7 @@ impl_getChildren (DBusConnection *bus,
   for (i = 0; i < count; i++)
     {
       AtkObject *child = atk_object_ref_accessible_child (object, i);
-      char *path = atk_dbus_get_path (child);
+      char *path = atk_dbus_object_to_path (child);
       if (path)
        {
          dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_OBJECT_PATH,
@@ -268,7 +268,7 @@ impl_getRelationSet (DBusConnection *bus,
       AtkObject *obj = target->pdata[j];
       char *path;
       if (!obj) continue;
-      path = atk_dbus_get_path (obj);
+      path = atk_dbus_object_to_path (obj);
       dbus_message_iter_append_basic (&iter_targets, DBUS_TYPE_OBJECT_PATH, &path);
     }
     dbus_message_iter_close_container (&iter_struct, &iter_targets);
index 6bf009c..956fb29 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "bridge.h"
 #include "accessible.h"
+#include "atk-dbus.h"
 
 /* TODO
  * Need to add concurrency support.
  * 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.
- * 
- * To enbale this two hash tables are kept. One keeps a list of AtkObjects
- * that have been updated. The other a list of objects that have been
- * removed since the last 'update' signal. The reason they are
- * stored as hash tables is to ensure that if an AtkObject is removed
- * and then added between update signals then the object is easy to delete
- * from the 'update' list without doing a costly lookup.
- *
- * The mappings will be called 'reference lookup tables'
- * The hashtable containing AtkObjects that need updating in the client side
- * cache will be called the 'update list'
- * The hashtable containing the AtkObjects that need removing from the client
- * side cache will be called the 'removal list'
  */
 
-GHashTable *ref2ptr = NULL; /* Used for converting a D-Bus path (Reference) to the object pointer */
-
-GHashTable *update_list = NULL; /* Stores the objects that need a client side cache update */
-GHashTable *remove_list = NULL; /* Stores the objects that need to be removed from the client side cache */
-
-static guint counter = 1;
-
 /* Amazingly the ATK event callbacks dont have a user
  * data parameter. Instead, with great sadness, we use
  * some global data. Data is declared and initialized
  * in bridge.c.
  */
+
+GHashTable *ref2ptr = NULL; /* Used for converting a D-Bus path (Reference) to the object pointer */
+
+static guint counter = 1;
+
 extern SpiAppData *atk_adaptor_app_data;
 
 /*---------------------------------------------------------------------------*/
@@ -106,135 +92,201 @@ assign_reference(void)
 
 /*---------------------------------------------------------------------------*/
 
+void
+atk_dbus_foreach_registered(GHFunc func, gpointer data)
+{
+  g_hash_table_foreach(ref2ptr, func, data);
+}
+
+/*---------------------------------------------------------------------------*/
+
 /*
  * Called when a registered AtkObject is deleted.
- *
  * Removes the AtkObject from the reference lookup tables.
- * Adds the reference of the object to the removal list.
+ * Sets the client side cache to be updated.
  */
 static void
 deregister_accessible(gpointer data, GObject *accessible)
 {
   guint ref;
+  gchar *path;
 
   g_assert(ATK_IS_OBJECT(accessible));
 
 
-  ref = GPOINTER_TO_INT(g_object_get_data (accessible, "dbus-id"));
-
-  /* Remove from update list */
-  g_hash_table_remove(update_list, GINT_TO_POINTER(ref));
+  ref = atk_dbus_object_to_ref (ATK_OBJECT(accessible));
 
   if (ref != 0)
     {
       g_hash_table_remove(ref2ptr, GINT_TO_POINTER(ref));
-      /* Add to removal list */
       /*
        * TODO
        * Pyatspi client side exceptions have occured indicating
        * that an object has been removed twice.
        * This should not be possible and needs investigation.
        */
-      g_hash_table_insert(remove_list, GINT_TO_POINTER(ref), NULL);
+      spi_emit_cache_removal (ref, atk_adaptor_app_data->bus);
     }
-
-  atk_tree_cache_needs_update(atk_adaptor_app_data->bus);
 }
 
 /*---------------------------------------------------------------------------*/
 
 /*
- * Registers a new AtkObject.
- *
- * Adds the AtkObject to the reference lookup tables.
- * Adds the AtkObject to the update list.
+ * This function registers the object so that it is exported
+ * over D-Bus and schedules an update to client side cache.
  */
 static guint
-register_accessible (AtkObject *accessible)
+export (GList **uplist, AtkObject *accessible)
 {
-  guint reference;
+  guint ref;
+  gchar *path;
 
   g_assert(ATK_IS_OBJECT(accessible));
 
-  reference = assign_reference();
+  ref = assign_reference();
 
-  g_hash_table_insert (ref2ptr, GINT_TO_POINTER(reference), accessible);
-  g_object_set_data (G_OBJECT(accessible), "dbus-id", GINT_TO_POINTER(reference));
+  g_hash_table_insert (ref2ptr, GINT_TO_POINTER(ref), accessible);
+  g_object_set_data (G_OBJECT(accessible), "dbus-id", GINT_TO_POINTER(ref));
   g_object_weak_ref(G_OBJECT(accessible), deregister_accessible, NULL);
 
-  /* Add to update list */
-  g_hash_table_insert (update_list, GINT_TO_POINTER(reference), accessible);
-
-  atk_tree_cache_needs_update(atk_adaptor_app_data->bus);
+  *uplist = g_list_prepend (*uplist, accessible);
 
-  return reference;
+  return ref;
 }
 
-/*---------------------------------------------------------------------------*/
-
-/* TODO Turn these into an iterator API - Think about the locking */
-
-void
-atk_dbus_foreach_registered(GHFunc func, gpointer data)
+/*
+ * This does a depth first traversal of a subtree of AtkObject
+ * and exports them as Accessible objects if they are not exported
+ * already.
+ */
+static guint
+export_subtree (AtkObject *accessible)
 {
-  g_hash_table_foreach(ref2ptr, func, data);
+  AtkObject *current, *tmp;
+  GQueue    *stack;
+  GList     *uplist = NULL;
+  guint      i, ref;
+  gboolean   recurse;
+
+  stack = g_queue_new ();
+
+  current = g_object_ref (accessible);
+  ref = export (&uplist, current);
+  g_queue_push_head (stack, GINT_TO_POINTER (0));
+
+  /*
+   * The index held on the stack is the next child node
+   * that needs processing at the corresponding level in the tree.
+   */
+  while (!g_queue_is_empty (stack))
+    {
+      /* This while loop finds the next node that needs processing,
+       * if one exists.
+       */
+      i = GPOINTER_TO_INT(g_queue_peek_head (stack));
+      recurse = FALSE;
+      while (i < atk_object_get_n_accessible_children (current) &&
+             recurse == FALSE)
+        {
+          tmp = atk_object_ref_accessible_child (current, i);
+          if (!atk_dbus_object_to_ref (tmp))
+            {
+              recurse = TRUE;
+            }
+          else
+            {
+              i++;
+              g_object_unref (G_OBJECT (tmp));
+            }
+        }
+      if (recurse)
+        {
+          /* Still children to process */
+          current = tmp;
+          export (&uplist, current);
+          /* Update parent nodes next child index */
+          g_queue_peek_head_link (stack)->data = GINT_TO_POINTER (i+1);
+          /* Push a new child index for the current node */
+          g_queue_push_head (stack, GINT_TO_POINTER (0));
+        }
+      else
+        {
+          /* No more children, move to parent */
+          tmp = current;
+          current = atk_object_get_parent (current);
+          g_object_unref (G_OBJECT (tmp));
+          g_queue_pop_head (stack);
+        }
+    }
+  spi_emit_cache_update (uplist, atk_adaptor_app_data->bus);
+  g_list_free (uplist);
+  return ref;
 }
 
 /*---------------------------------------------------------------------------*/
 
-void
-atk_dbus_foreach_update_list(GHFunc func, gpointer data)
+/* Called to register an AtkObject with AT-SPI and expose it over D-Bus. */
+guint
+atk_dbus_register_accessible (AtkObject *accessible)
 {
-  g_hash_table_foreach(update_list, func, data);
-  g_hash_table_remove_all(update_list);
-}
-
-/*---------------------------------------------------------------------------*/
+  guint ref;
+  g_assert(ATK_IS_OBJECT(accessible));
 
-void
-atk_dbus_foreach_remove_list(GHFunc func, gpointer data)
-{
-  g_hash_table_foreach(remove_list, func, data);
-  g_hash_table_remove_all(remove_list);
+  ref = atk_dbus_object_to_ref (accessible);
+  if (!ref)
+      return export_subtree (accessible);
+  else
+      return ref;
 }
 
-/*---------------------------------------------------------------------------*/
-
-/* 
- * Called on an AtkObject when it has changed in such a way
- * that the client side cache of the object will need updating.
+/* Called when an already registered object is updated in such a
+ * way that client side cache needs to be updated.
  */
-void 
-atk_dbus_notify_change(AtkObject *accessible)
+guint
+atk_dbus_update_accessible (AtkObject *accessible)
 {
-  guint ref;
+  guint ref = 0;
   g_assert(ATK_IS_OBJECT(accessible));
 
-  if (!g_object_get_data (G_OBJECT (accessible), "dbus-id"))
+  ref = atk_dbus_object_to_ref (accessible);
+  if (ref)
     {
-      register_accessible(accessible);
-    }
-  else
-    {
-      ref = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (accessible), "dbus-id"));
-      g_hash_table_insert (update_list, GINT_TO_POINTER (ref), accessible);
-      atk_tree_cache_needs_update(atk_adaptor_app_data->bus);
+      spi_emit_cache_update (accessible, atk_adaptor_app_data->bus);
     }
+  return ref;
 }
 
 /*---------------------------------------------------------------------------*/
 
 /*
+ * Returns the reference of the object, or 0 if it is not exported over D-Bus.
+ */
+guint
+atk_dbus_object_to_ref (AtkObject *accessible)
+{
+  return GPOINTER_TO_INT(g_object_get_data (G_OBJECT (accessible), "dbus-id"));
+}
+
+/*
+ * Converts the Accessible object reference to its D-Bus object path
+ */
+gchar *
+atk_dbus_ref_to_path (guint ref)
+{
+  return g_strdup_printf(ATK_BRIDGE_OBJECT_REFERENCE_TEMPLATE, ref);
+}
+
+/*
  * Used to lookup an AtkObject from its D-Bus path.
  */
 AtkObject *
-atk_dbus_get_object (const char *path)
+atk_dbus_path_to_object (const char *path)
 {
   guint index;
   void *data;
 
   g_assert (path);
+
   if (strncmp(path, ATK_BRIDGE_OBJECT_PATH_PREFIX, ATK_BRIDGE_PATH_PREFIX_LENGTH) != 0) 
     return NULL;
 
@@ -254,66 +306,26 @@ atk_dbus_get_object (const char *path)
     return NULL;
 }
 
-/*---------------------------------------------------------------------------*/
-
-gchar *
-atk_dbus_get_path_from_ref(guint ref)
-{
-  return g_strdup_printf(ATK_BRIDGE_OBJECT_REFERENCE_TEMPLATE, ref);
-}
-
-/*---------------------------------------------------------------------------*/
 
 /*
  * Used to lookup a D-Bus path from the AtkObject.
- *
- * Objects without a path are registered and provided with one.
  */
 gchar *
-atk_dbus_get_path (AtkObject *accessible)
-{
-  guint index;
-
-  g_assert (accessible);
-
-  index = GPOINTER_TO_INT(g_object_get_data (G_OBJECT (accessible), "dbus-id"));
-  if (!index)
-    index = register_accessible(accessible);
-
-  return g_strdup_printf(ATK_BRIDGE_OBJECT_REFERENCE_TEMPLATE, index);
-}
-
-/*---------------------------------------------------------------------------*/
-
-/*
- * Used to recursively register accessibles.
- *
- * When children are added to an accessible we need to 
- * iterate over the new subtree provided to register new accessibles.
- */
-guint
-atk_dbus_register_subtree(AtkObject *accessible)
+atk_dbus_object_to_path (AtkObject *accessible)
 {
-  AtkObject *child;
-  guint i, n_children;
   guint ref;
+  g_assert(ATK_IS_OBJECT(accessible));
 
-  ref = GPOINTER_TO_INT(g_object_get_data (G_OBJECT (accessible), "dbus-id"));
+  ref = atk_dbus_object_to_ref (accessible);
   if (!ref)
-     ref = register_accessible(accessible);
-
-  n_children = atk_object_get_n_accessible_children(accessible);
-  for (i=0; i < n_children; i++)
-    {
-      child = atk_object_ref_accessible_child(accessible, i);
-      atk_dbus_register_subtree(child);
-    } 
-  return ref;
-} 
+      return NULL;
+  else
+      return atk_dbus_ref_to_path (ref);
+}
 
 /*---------------------------------------------------------------------------*/
 
-/* 
+/*
  * Marshals a single object into a D-Bus message.
  *
  * Unrefs the AtkObject if unref is true.
@@ -323,8 +335,8 @@ spi_dbus_return_object (DBusMessage *message, AtkObject *obj, gboolean unref)
 {
   DBusMessage *reply;
   gchar *path;
-  
-  path = atk_dbus_get_path (obj);
+
+  path = atk_dbus_object_to_path (obj);
 
   if (unref)
     g_object_unref (obj);
@@ -333,7 +345,7 @@ spi_dbus_return_object (DBusMessage *message, AtkObject *obj, gboolean unref)
   if (reply)
     {
       dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, path,
-                               DBUS_TYPE_INVALID);
+                                DBUS_TYPE_INVALID);
     }
   return reply;
 }
@@ -350,8 +362,8 @@ dbus_bool_t
 spi_dbus_return_v_object (DBusMessageIter *iter, AtkObject *obj, int unref)
 {
   char *path;
-  
-  path = atk_dbus_get_path (obj);
+
+  path = atk_dbus_object_to_path (obj);
 
   if (unref)
     g_object_unref (obj);
@@ -372,13 +384,9 @@ atk_dbus_initialize (AtkObject *root)
 {
   if (!ref2ptr)
     ref2ptr = g_hash_table_new(g_direct_hash, g_direct_equal);
-  if (!update_list)
-    update_list = g_hash_table_new(g_direct_hash, g_direct_equal);
-  if (!remove_list)
-    remove_list = g_hash_table_new(g_direct_hash, g_direct_equal);
 
   /* Get the root accessible and add */
-  atk_dbus_register_subtree(root);
+  atk_dbus_register_accessible (root);
 }
 
 /*END------------------------------------------------------------------------*/
index b409c2b..17dc50d 100644 (file)
@@ -37,20 +37,32 @@ atk_dbus_foreach_update_list(GHFunc func, gpointer data);
 void
 atk_dbus_foreach_remove_list(GHFunc func, gpointer data);
 
-void 
-atk_dbus_notify_change(AtkObject *accessible);
+/*---------------------------------------------------------------------------*/
+
+guint
+atk_dbus_register_accessible (AtkObject *accessible);
+
+guint
+atk_dbus_update_accessible (AtkObject *accessible);
+
+/*---------------------------------------------------------------------------*/
+
+guint
+atk_dbus_object_to_ref (AtkObject *accessible);
+
+gchar *
+atk_dbus_ref_to_path (guint ref);
 
 AtkObject *
-atk_dbus_get_object (const char *path);
+atk_dbus_path_to_object (const char *path);
 
 gchar *
-atk_dbus_get_path_from_ref(guint ref);
+atk_dbus_ref_to_path (guint ref);
 
 gchar *
-atk_dbus_get_path (AtkObject *accessible);
+atk_dbus_object_to_path (AtkObject *accessible);
 
-guint
-atk_dbus_register_subtree(AtkObject *accessible);
+/*---------------------------------------------------------------------------*/
 
 DBusMessage *
 spi_dbus_return_object (DBusMessage *message, AtkObject *obj, gboolean unref);
index da7d331..80d8a5b 100644 (file)
@@ -290,7 +290,7 @@ adaptor_init (gint *argc, gchar **argv[])
   accpath = droute_add_many (atk_adaptor_app_data->droute,
                              "/org/freedesktop/atspi/accessible",
                              NULL,
-                             (DRouteGetDatumFunction) atk_dbus_get_object);
+                             (DRouteGetDatumFunction) atk_dbus_path_to_object);
 
   /* Register all interfaces with droute and set up application accessible db */
   spi_initialize_tree (treepath);
index 304ed62..c5fe2c0 100644 (file)
@@ -32,8 +32,6 @@
 #include "spi-common/bitarray.h"
 #include "spi-common/spi-dbus.h"
 
-#define get_object(message) atk_dbus_get_object(dbus_message_get_path(message))
-
 typedef struct _MatchRulePrivate MatchRulePrivate;
 struct _MatchRulePrivate
 {
@@ -677,7 +675,7 @@ return_and_free_list(DBusMessage *message, GList *ls)
   if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &iter_array)) goto oom;
   for (item = ls; item; item = g_list_next(item))
   {
-      char *path = (char *) spi_dbus_get_path((AtkObject *)item->data);
+      char *path = (char *) spi_dbus_object_to_path ((AtkObject *)item->data);
       dbus_message_iter_append_basic(&iter_array, DBUS_TYPE_OBJECT_PATH, &path);
       g_free(path);
   }
@@ -794,7 +792,7 @@ getMatchesInOrder (DBusMessage *message,
 
   ls = g_list_append (ls, current_object);
 
-  obj = atk_dbus_get_object (dbus_message_get_path (message));
+  obj = atk_dbus_path_to_object (dbus_message_get_path (message));
   
   kount = inorder (obj, mrp, ls, 0, count, 
                    current_object, TRUE, NULL, traverse);
@@ -826,7 +824,7 @@ getMatchesInBackOrder (DBusMessage *message,
 
   ls = g_list_append (ls, current_object);
 
-  collection = atk_dbus_get_object (dbus_message_get_path (message));
+  collection = atk_dbus_path_to_object (dbus_message_get_path (message));
 
   kount = sort_order_rev_canonical (mrp, ls, 0, count, current_object, 
                                    FALSE, collection);
@@ -862,7 +860,7 @@ getMatchesTo (DBusMessage *message,
                          obj, 0, TRUE, current_object, TRUE, traverse);
   }
   else{ 
-    obj = atk_dbus_get_object (dbus_message_get_path (message));
+    obj = atk_dbus_path_to_object (dbus_message_get_path (message));
     kount = query_exec (mrp,  sortby, ls, 0, count,
                         obj, 0, TRUE, current_object, TRUE, traverse); 
 
@@ -892,7 +890,7 @@ impl_getMatchesFrom (DBusConnection *bus, DBusMessage *message, void *user_data)
 
   dbus_message_iter_init(message, &iter);
   dbus_message_iter_get_basic (&iter, current_object_path);
-  current_object = atk_dbus_get_object (current_object_path);
+  current_object = atk_dbus_path_to_object (current_object_path);
   if (!current_object)
   {
     // TODO: object-not-found error
@@ -945,7 +943,7 @@ impl_getMatchesTo (DBusConnection *bus, DBusMessage *message, void *user_data)
 
   dbus_message_iter_init(message, &iter);
   dbus_message_iter_get_basic (&iter, current_object_path);
-  current_object = atk_dbus_get_object (current_object_path);
+  current_object = atk_dbus_path_to_object (current_object_path);
   if (!current_object)
   {
     // TODO: object-not-found error
@@ -987,7 +985,7 @@ impl_getMatchesTo (DBusConnection *bus, DBusMessage *message, void *user_data)
 static DBusMessage *
 impl_getMatches(DBusConnection *bus, DBusMessage *message, void *user_data)
 {
-  AtkObject *obj = get_object(message);
+  AtkObject *obj = path_to_object (message);
   DBusMessageIter iter;
   MatchRulePrivate rule;
   dbus_uint16_t sortby;
index fef3916..cc03daf 100644 (file)
@@ -53,6 +53,22 @@ extern SpiAppData *atk_adaptor_app_data;
 
 /*---------------------------------------------------------------------------*/
 
+/* When sending events it is safe to register an accessible object if
+ * one does not already exist for a given AtkObject.
+ * This is because the cache update signal should then be send before
+ * the event signal is sent.
+ */
+static gchar *
+get_object_path (AtkObject *accessible)
+{
+    guint ref;
+
+    ref = atk_dbus_register_accessible (accessible);
+    return atk_dbus_ref_to_path (ref);
+}
+
+/*---------------------------------------------------------------------------*/
+
 static gboolean
 Accessibility_DeviceEventController_notifyListenersSync(const Accessibility_DeviceEvent *key_event)
 {
@@ -178,14 +194,6 @@ provide_defaults(const gint type,
     }
 }
 
-
-/* TODO Should we bother emiting events for objects that have not yet
- * been added to the tree?
- * 
- * This gets difficult. Its entirely possible that an Accessible would have been
- * added to the tree, but not yet reached the clients.
- * In this case we would be wrongly surpressing an event.
- */
 static void 
 emit(AtkObject  *accessible,
      const char *klass,
@@ -200,6 +208,15 @@ emit(AtkObject  *accessible,
   DBusMessageIter iter, sub;
   gchar *path, *cname, *t;
 
+  path = get_object_path (accessible);
+
+  /* Tough decision here
+   * We won't send events from accessible
+   * objects that have not yet been added to the accessible tree.
+   */
+  if (path == NULL)
+      return;
+
   if (!klass) klass = "";
   if (!major) major = "";
   if (!minor) minor = "";
@@ -212,7 +229,6 @@ emit(AtkObject  *accessible,
   cname = g_strdup(major);
   while ((t = strchr(cname, '-')) != NULL) *t = '_';
 
-  path = atk_dbus_get_path(accessible);
   sig = dbus_message_new_signal(path, klass, cname);
   g_free(cname);
   g_free(path);
@@ -256,6 +272,15 @@ emit_rect(AtkObject  *accessible,
   gchar *path, *cname, *t;
   dbus_int32_t dummy = 0;
 
+  path = get_object_path (accessible);
+
+  /* Tough decision here
+   * We won't send events from accessible
+   * objects that have not yet been added to the accessible tree.
+   */
+  if (path == NULL)
+      return;
+
   if (!klass) klass = "";
   if (!major) major = "";
   if (!minor) minor = "";
@@ -268,7 +293,6 @@ emit_rect(AtkObject  *accessible,
   cname = g_strdup(major);
   while ((t = strchr(cname, '-')) != NULL) *t = '_';
 
-  path = atk_dbus_get_path(accessible);
   sig = dbus_message_new_signal(path, klass, cname);
   g_free(path);
   g_free(cname);
@@ -322,17 +346,11 @@ tree_update_listener (GSignalInvocationHint *signal_hint,
 
   pname = values[0].property_name;
 
-  if (strcmp (pname, "accessible-name") == 0)
-    {
-      atk_dbus_notify_change(accessible);
-    }
-  else if (strcmp (pname, "accessible-description") == 0)
-    {
-      atk_dbus_notify_change(accessible);
-    }
-  else if (strcmp (pname, "accessible-parent") == 0)
+  if (strcmp (pname, "accessible-name") == 0 ||
+      strcmp (pname, "accessible-description") == 0 ||
+      strcmp (pname, "accessible-parent") == 0)
     {
-      atk_dbus_notify_change(accessible);
+      atk_dbus_update_accessible (accessible);
     }
   return TRUE;
 }
@@ -346,9 +364,9 @@ tree_update_listener (GSignalInvocationHint *signal_hint,
  */
 static gboolean
 tree_update_children_listener (GSignalInvocationHint *signal_hint,
-                              guint                  n_param_values,
-                              const GValue          *param_values,
-                              gpointer               data)
+                               guint                  n_param_values,
+                               const GValue          *param_values,
+                               gpointer               data)
 {
   AtkObject *accessible;
   const gchar *detail = NULL;
@@ -364,17 +382,14 @@ tree_update_children_listener (GSignalInvocationHint *signal_hint,
       gpointer child;
       int index = g_value_get_uint (param_values + 1);
       child = g_value_get_pointer (param_values + 2);
+
       if (ATK_IS_OBJECT (child))
-       g_object_ref (child);
-      else
-       child = atk_object_ref_accessible_child (accessible, index);
-      if (ATK_IS_OBJECT (child))
-       {
-         atk_dbus_register_subtree (child);
-         g_object_unref (child);
-       }
+          g_object_ref (child);
       else
-       atk_dbus_register_subtree(accessible);
+          child = atk_object_ref_accessible_child (accessible, index);
+
+      atk_dbus_register_accessible (child);
+      g_object_unref (child);
     }
   return TRUE;
 }
@@ -429,21 +444,21 @@ property_event_listener (GSignalInvocationHint *signal_hint,
   if (strcmp (pname, "accessible-table-summary") == 0)
     {
       otemp = atk_table_get_summary(ATK_TABLE (accessible));
-      stemp = atk_dbus_get_path(otemp);
+      stemp = get_object_path (otemp);
       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
     }
   else if (strcmp (pname, "accessible-table-column-header") == 0)
     {
       i = g_value_get_int (&(values->new_value));
       otemp = atk_table_get_column_header(ATK_TABLE (accessible), i);
-      stemp = atk_dbus_get_path(otemp);
+      stemp = get_object_path (otemp);
       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
     }
   else if (strcmp (pname, "accessible-table-row-header") == 0)
     {
       i = g_value_get_int (&(values->new_value));
       otemp = atk_table_get_row_header(ATK_TABLE (accessible), i);
-      stemp = atk_dbus_get_path(otemp);
+      stemp = get_object_path (otemp);
       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
     }
   else if (strcmp (pname, "accessible-table-row-description") == 0)
@@ -623,7 +638,7 @@ active_descendant_event_listener (GSignalInvocationHint *signal_hint,
   minor = g_quark_to_string (signal_hint->detail);
 
   detail1 = atk_object_get_index_in_parent (child);
-  s = atk_dbus_get_path(child);
+  s = get_object_path (child);
 
   emit(accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, s);
   g_free(s);
index feffb33..ddac5e3 100644 (file)
@@ -31,8 +31,6 @@
 #include "spi-common/spi-dbus.h"
 #include "bridge.h"
 
-static gboolean update_pending = FALSE;
-
 /*---------------------------------------------------------------------------*/
 
 static void
@@ -122,6 +120,7 @@ append_atk_object_interfaces (AtkObject *object, DBusMessageIter *iter)
 
 static const char *dumm = "/APath/1";
 
+
 /*
  * Marshals the given AtkObject into the provided D-Bus iterator.
  *
@@ -131,7 +130,7 @@ static const char *dumm = "/APath/1";
  * of the org.freedesktop.atspi.Tree interface.
  */
 static void
-append_accessible(gpointer ref, gpointer obj_data, gpointer iter)
+append_accessible(gpointer obj_data, gpointer iter)
 {
   AtkObject *obj;
   DBusMessageIter *iter_array;
@@ -152,14 +151,14 @@ append_accessible(gpointer ref, gpointer obj_data, gpointer iter)
       AtkObject *parent;
       gchar *path, *path_parent;
 
-      path = atk_dbus_get_path_from_ref(GPOINTER_TO_INT(ref));
+      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_get_path (parent);
+        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);
 
@@ -174,10 +173,17 @@ append_accessible(gpointer ref, gpointer obj_data, gpointer iter)
               gchar *child_path;
 
               child = atk_object_ref_accessible_child (obj, i);
-              child_path = atk_dbus_get_path (child);
+              child_path = atk_dbus_object_to_path (child);
               g_object_unref(G_OBJECT(child));
-              dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_OBJECT_PATH, &child_path);
-              g_free (child_path);
+              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);
@@ -209,66 +215,49 @@ append_accessible(gpointer ref, gpointer obj_data, gpointer iter)
   dbus_message_iter_close_container (iter_array, &iter_struct);
 }
 
-/*---------------------------------------------------------------------------*/
-
-/*
- * Used to marshal array of objects to remove.
- * Marshalls an object path onto the iter provided.
- */
+/* For use as a GHFunc */
 static void
-append_accessible_path(gpointer ref_data, gpointer null, gpointer data)
+append_accessible_hf (gpointer key, gpointer obj_data, gpointer iter)
 {
-  guint ref;
-  gchar *path;
-  DBusMessageIter *iter_array;
-
-  iter_array = (DBusMessageIter *) data;
-  ref = GPOINTER_TO_INT(ref_data);
-  path = atk_dbus_get_path_from_ref(ref);
-  dbus_message_iter_append_basic (iter_array, DBUS_TYPE_OBJECT_PATH, &path);
-  g_free(path);
+  append_accessible (obj_data, iter);
 }
 
 /*---------------------------------------------------------------------------*/
 
-static gboolean
-send_cache_update(gpointer data)
+void
+spi_emit_cache_removal (guint ref,  DBusConnection *bus)
 {
   DBusMessage *message;
   DBusMessageIter iter;
-  DBusMessageIter iter_array;
-  DBusConnection *bus = (DBusConnection *) data;
+  gchar *path;
 
-  message = dbus_message_new_signal ("/org/freedesktop/atspi/tree", SPI_DBUS_INTERFACE_TREE, "updateTree");
+  message = dbus_message_new_signal ("/org/freedesktop/atspi/tree", SPI_DBUS_INTERFACE_TREE, "removeAccessible");
 
   dbus_message_iter_init_append (message, &iter);
 
-  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassusau)", &iter_array);
-  atk_dbus_foreach_update_list(append_accessible, &iter_array);
-  dbus_message_iter_close_container(&iter, &iter_array);
-
-  dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &iter_array);
-  atk_dbus_foreach_remove_list(append_accessible_path, &iter_array);
-  dbus_message_iter_close_container(&iter, &iter_array);
+  path = atk_dbus_ref_to_path (ref);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &path);
 
   dbus_connection_send(bus, message, NULL);
-  update_pending = FALSE;
-
-  return FALSE;
 }
 
-/*---------------------------------------------------------------------------*/
-
 void
-atk_tree_cache_needs_update(DBusConnection *bus)
+spi_emit_cache_update (const GList *accessibles, DBusConnection *bus)
 {
-  if (!update_pending)
-    {
-      g_idle_add(send_cache_update, bus);
-      update_pending = TRUE;
-    }
+   DBusMessage *message;
+   DBusMessageIter iter;
+   DBusMessageIter iter_array;
+   message = dbus_message_new_signal ("/org/freedesktop/atspi/tree", SPI_DBUS_INTERFACE_TREE, "updateAccessible");
+
+   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);
+   dbus_message_iter_close_container(&iter, &iter_array);
+
+   dbus_connection_send(bus, message, NULL);
 }
 
+
 /*---------------------------------------------------------------------------*/
 
 static DBusMessage *
@@ -278,9 +267,11 @@ impl_getRoot (DBusConnection *bus, DBusMessage *message, void *user_data)
   char *path;
   DBusMessage *reply;
 
-  if (root) path = atk_dbus_get_path(root);
-  if (!root || !path)
-    return spi_dbus_general_error (message);
+  if (!root)
+      return spi_dbus_general_error (message);
+  path = atk_dbus_object_to_path (root);
+  if (!path)
+      return spi_dbus_general_error (message);
   reply = dbus_message_new_method_return (message);
   dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
   g_free (path);
@@ -294,15 +285,12 @@ impl_getTree (DBusConnection *bus, DBusMessage *message, void *user_data)
 {
   DBusMessage *reply;
   DBusMessageIter iter, iter_array;
-  AtkObject *root = atk_get_root();
 
-  if (!root) 
-     return spi_dbus_general_error(message);
   reply = dbus_message_new_method_return (message);
 
   dbus_message_iter_init_append (reply, &iter);
   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ooaoassusau)", &iter_array);
-  atk_dbus_foreach_registered(append_accessible, &iter_array);
+  atk_dbus_foreach_registered(append_accessible_hf, &iter_array);
   dbus_message_iter_close_container(&iter, &iter_array);
   return reply;
 }
index 0670a45..cdd2b56 100644 (file)
 
 #include <atk/atk.h>
 #include <spi-common/spi-types.h>
+#include <dbus/dbus.h>
 
 G_BEGIN_DECLS
 
-void atk_tree_cache_needs_update(void);
+void
+spi_emit_cache_update  (AtkObject *accessible, DBusConnection *bus);
+
+void
+spi_emit_cache_removal (guint ref, DBusConnection *bus);
 
 G_END_DECLS
 
index c2c29e4..213d4af 100644 (file)
@@ -75,7 +75,8 @@ class AccessibleCache(object):
         _PATH = '/org/freedesktop/atspi/tree'
         _INTERFACE = 'org.freedesktop.atspi.Tree'
         _GET_METHOD = 'getTree'
-        _UPDATE_SIGNAL = 'updateTree'
+        _UPDATE_SIGNAL = 'updateAccessible'
+        _REMOVE_SIGNAL = 'removeAccessible'
 
         def __init__(self, registry, connection, bus_name):
                 """
@@ -96,7 +97,8 @@ class AccessibleCache(object):
                 get_method = itf.get_dbus_method(self._GET_METHOD)
                 self._update_objects(get_method())
 
-                self._signalMatch = itf.connect_to_signal(self._UPDATE_SIGNAL, self._update_handler)
+                self._updateMatch = itf.connect_to_signal(self._UPDATE_SIGNAL, self._update_objects)
+                self._removeMatch = itf.connect_to_signal(self._REMOVE_SIGNAL, self._remove_object)
 
                 obj = connection.get_object(self._bus_name, self._PATH, introspect=False)
                 itf = _dbus.Interface(obj, self._INTERFACE)
@@ -157,10 +159,6 @@ class AccessibleCache(object):
                                        ("remove", 0, 0, ""))
                         self._registry._notifyChildrenChange(event)
 
-        def _update_handler(self, update, remove):
-                self._remove_objects(remove)
-                self._update_objects(update)
-
         def _update_objects(self, objects):
                 cache_update_objects = []
                 for data in objects:
@@ -176,15 +174,14 @@ class AccessibleCache(object):
                 for old, new in cache_update_objects:
                         self._dispatch_event(old, new)
 
-        def _remove_objects(self, paths):
-                for path in paths:
-                        # TODO I'm squashing a possible error here
-                        # I've seen things appear to be deleted twice
-                        # which needs investigation
-                        try:
-                                del(self._objects[path])
-                        except KeyError:
-                                pass
+        def _remove_object(self, paths):
+                # TODO I'm squashing a possible error here
+                # I've seen things appear to be deleted twice
+                # which needs investigation
+                try:
+                        del(self._objects[path])
+                except KeyError:
+                        pass
 
         def _get_root(self):
                 return self._root
index 23ddb35..65936aa 100644 (file)
@@ -88,7 +88,7 @@
                        </arg>
                </method>
 
-               <signal name="updateTree">
+               <signal name="updateAccessible">
                        <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
                                <p>Updates all mirrors. Re-sends all all the 
                                accessible objects that have changed since the last
                                        <p>Array of Accessible Object proxy structures to be added.</p>
                                </tp:docstring>
                        </arg>
-                       <arg name="nodesRemoved" type="ao">
+               </signal>
+
+               <signal name="removeAccessible">
+                       <arg name="nodeRemoved" type="o">
                                <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
-                                       <p>Array of Accessible Object proxy structures to be removed.</p>
+                                       <p>Accessible Object proxy structure to be removed.</p>
                                </tp:docstring>
                        </arg>
                </signal>