2008-08-29 Mark Doffman <mark.doffman@codethink.co.uk>
authorMark Doffman <mdoff@silver-wind.(none)>
Tue, 2 Sep 2008 17:48:31 +0000 (18:48 +0100)
committerMark Doffman <mdoff@silver-wind.(none)>
Thu, 4 Sep 2008 13:07:49 +0000 (14:07 +0100)
* atk-adaptor/
Large rework of two sections of the code.
The event functionality has been moved out
of bridge.c and into event.c.

The events themselves have been changed.
Whereas previously all events came from
a single interface. "org.freedesktop.atspi.Accessible"
they now come from different interfaces, depending
on the event.

The 'major', 'minor' and 'klass' sections of the
interface name have been moved to the signal name
, first message argument, and interface name respectively.

        Also reworked was the Client side cache, and Tree interface
functionality. There was a problem with the old code where
accessibles not part of the main tree. (Theoretically possible
to access through a RelationSet) would not be transfered
to the cache.

There was a bug where Acessibles would not be removed from
the server side registry when the AtkObject backing them
had been deleted.

* pyatspi/
Add event registration to the registry interface.

Add a whole new event framework, begin rework of the
tree update structure.

Complete the modification of the event framework
on the client side and the rework of the client
side cache transfer framework.

26 files changed:
atk-adaptor/Makefile.am
atk-adaptor/accessible.c
atk-adaptor/accessible.h
atk-adaptor/action.c
atk-adaptor/application.c
atk-adaptor/atk-dbus.c
atk-adaptor/atk-dbus.h [new file with mode: 0644]
atk-adaptor/bridge.c
atk-adaptor/bridge.h [new file with mode: 0644]
atk-adaptor/collection.c
atk-adaptor/component.c
atk-adaptor/document.c
atk-adaptor/editabletext.c
atk-adaptor/event.c [new file with mode: 0644]
atk-adaptor/hyperlink.c
atk-adaptor/hypertext.c
atk-adaptor/image.c
atk-adaptor/selection.c
atk-adaptor/table.c
atk-adaptor/text.c
atk-adaptor/tree.c
atk-adaptor/value.c
pyatspi/cache.py
pyatspi/event.py
pyatspi/registry.py
xml/org.freedesktop.atspi.Event.xml

index f4c92fb..51c714f 100644 (file)
@@ -19,13 +19,16 @@ libspiatk_la_SOURCES =              \
        action.c                \
        application.c           \
        bridge.c                \
-       collection.c \
+       bridge.h                \
+       collection.c            \
        component.c             \
        document.c              \
        editabletext.c          \
+       event.c                 \
        hyperlink.c             \
        hypertext.c             \
        atk-dbus.c              \
+       atk-dbus.h              \
        image.c                 \
        selection.c             \
        spi-private.h           \
index 8154e4c..499e0ef 100644 (file)
 
 #include "accessible.h"
 
-#define get_object(message) spi_dbus_get_object(dbus_message_get_path(message))
+#define get_object(message) atk_dbus_get_object(dbus_message_get_path(message))
 
 static AtkObject *
 get_object_from_path (const char *path, void *user_data)
 {
-  return spi_dbus_get_object (path);
+  return atk_dbus_get_object (path);
 }
 
 static dbus_bool_t
 impl_get_name (const char *path, DBusMessageIter * iter, void *user_data)
 {
-  AtkObject *object = spi_dbus_get_object (path);
+  AtkObject *object = atk_dbus_get_object (path);
   if (!object)
     return FALSE;
   return droute_return_v_string (iter, atk_object_get_name (object));
@@ -44,7 +44,7 @@ impl_get_name (const char *path, DBusMessageIter * iter, void *user_data)
 static dbus_bool_t
 impl_set_name (const char *path, DBusMessageIter * iter, void *user_data)
 {
-  AtkObject *object = spi_dbus_get_object (path);
+  AtkObject *object = atk_dbus_get_object (path);
   const char *name = droute_get_v_string (iter);
   atk_object_set_name (object, name);
   return TRUE;
@@ -54,7 +54,7 @@ static dbus_bool_t
 impl_get_description (const char *path, DBusMessageIter * iter,
                      void *user_data)
 {
-  AtkObject *object = spi_dbus_get_object (path);
+  AtkObject *object = atk_dbus_get_object (path);
   if (!object)
     return FALSE;
   return droute_return_v_string (iter, atk_object_get_description (object));
@@ -64,7 +64,7 @@ static dbus_bool_t
 impl_set_description (const char *path, DBusMessageIter * iter,
                      void *user_data)
 {
-  AtkObject *object = spi_dbus_get_object (path);
+  AtkObject *object = atk_dbus_get_object (path);
   const char *description = droute_get_v_string (iter);
   atk_object_set_description (object, description);
   return TRUE;
@@ -73,7 +73,7 @@ impl_set_description (const char *path, DBusMessageIter * iter,
 static dbus_bool_t
 impl_get_parent (const char *path, DBusMessageIter * iter, void *user_data)
 {
-  AtkObject *object = spi_dbus_get_object (path);
+  AtkObject *object = atk_dbus_get_object (path);
 
   if (!object)
     return FALSE;
@@ -85,7 +85,7 @@ static dbus_bool_t
 impl_get_childCount (const char *path, DBusMessageIter * iter,
                     void *user_data)
 {
-  AtkObject *object = spi_dbus_get_object (path);
+  AtkObject *object = atk_dbus_get_object (path);
 
   if (!object)
     return FALSE;
@@ -116,7 +116,7 @@ impl_getChildren (DBusConnection * bus, DBusMessage * message,
   for (i = 0; i < count; i++)
     {
       AtkObject *child = atk_object_ref_accessible_child (object, i);
-      char *path = spi_dbus_get_path (child);
+      char *path = atk_dbus_get_path (child);
       if (path)
        {
          dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_OBJECT_PATH,
@@ -244,7 +244,7 @@ impl_getRelationSet (DBusConnection * bus, DBusMessage * message,
       AtkObject *obj = target->pdata[j];
       char *path;
       if (!obj) continue;
-      path = spi_dbus_get_path (obj);
+      path = atk_dbus_get_path (obj);
       dbus_message_iter_append_basic (&iter_targets, DBUS_TYPE_OBJECT_PATH, &path);
     }
     dbus_message_iter_close_container (&iter_struct, &iter_targets);
index 973b35a..ce904aa 100644 (file)
@@ -49,23 +49,16 @@ void spi_initialize_table(DRouteData *data);
 void spi_initialize_text(DRouteData *data);
 void spi_initialize_value(DRouteData *data);
 void spi_initialize_introspectable(DRouteData *data, DRouteGetDatumFunction verify_object);
-void spi_dbus_initialize(DRouteData *data);
-
-AtkObject *spi_dbus_get_object(const char *path);
-gchar *spi_dbus_get_path(AtkObject *obj);
-
-DBusMessage *spi_dbus_return_object(DBusMessage *message, AtkObject *obj, int unref);
-dbus_bool_t spi_dbus_return_v_object(DBusMessageIter *iter, AtkObject *obj, int unref);
 
 /* tree.c */
 void spi_register_tree_object(DBusConnection *bus, DRouteData *data, const char *path);
 
 dbus_bool_t spi_dbus_append_tree (DBusMessage * message, AtkObject * obj, DRouteData * data);
 
-void spi_dbus_notify_change(AtkObject *obj, gboolean new, DRouteData *data);
-void spi_dbus_notify_remove(AtkObject *obj, DRouteData *data);
-gboolean spi_dbus_update_cache(DRouteData *data);
-gboolean spi_dbus_object_is_known(AtkObject *obj);
+void
+atk_tree_cache_needs_update(void);
+
+#include "atk-dbus.h"
 
 G_END_DECLS
 
index ef801be..693a9a4 100644 (file)
@@ -27,7 +27,7 @@
 static AtkAction *
 get_action (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_ACTION (obj);
@@ -36,7 +36,7 @@ get_action (DBusMessage * message)
 static AtkAction *
 get_action_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_ACTION(obj))
     return NULL;
   return ATK_ACTION (obj);
index 5ae7196..ccaa6c2 100644 (file)
@@ -103,7 +103,7 @@ static DRouteProperty properties[] = {
 static long
 obj_is_root (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   return (obj == atk_get_root ());
 }
 
index e68d3a9..94b8a8a 100644 (file)
 
 #include "accessible.h"
 
-GHashTable *path2ptr;
-static guint objindex;
+/* TODO 
+ * Need to add concurrency support.
+ */
+
+#define ATK_BRIDGE_OBJECT_PATH_PREFIX "/org/freedesktop/atspi/accessible"
+#define ATK_BRIDGE_OBJECT_REFERENCE_TEMPLATE ATK_BRIDGE_OBJECT_PATH_PREFIX "/%d"
+#define ATK_BRIDGE_PATH_PREFIX_LENGTH 33
+
+/*
+ * This module is responsible for keeping track of all the AtkObjects in
+ * the application, so that they can be accessed remotely and placed in
+ * a client side cache.
+ *
+ * To access an AtkObject remotely we need to provide a D-Bus object 
+ * path for it. The D-Bus object paths used have a standard prefix
+ * (ATK_BRIDGE_OBJECT_PATH_PREFIX). Appended to this prefix is a string
+ * representation of an integer reference. So to access an AtkObject 
+ * remotely we keep a Hashtable that maps the given reference to 
+ * the AtkObject pointer. An object in this hash table is said to be 'registered'.
+ *
+ * The architecture of AT-SPI dbus is such that AtkObjects are not
+ * remotely reference counted. This means that we need to keep track of
+ * object destruction. When an object is destroyed it must be 'deregistered'
+ * To do this lookup we keep a dbus-id attribute on each AtkObject.
+ *
+ * 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.
+ * 
+ * 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;
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Each AtkObject must be asssigned a D-Bus path (Reference)
+ *
+ * This function provides an integer reference for a new
+ * AtkObject.
+ */
+static guint
+assign_reference(void)
+{
+  counter++;
+  /* Reference of 0 not allowed as used as direct key in hash table */
+  if (counter == 0)
+    counter++;
+}
+
+/*---------------------------------------------------------------------------*/
 
+/*
+ * 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.
+ */
 static void
-deregister_object (gpointer data, GObject *obj)
+deregister_accessible(gpointer data, GObject *accessible)
 {
-  spi_dbus_notify_remove(ATK_OBJECT(obj), NULL);
-  g_hash_table_remove (path2ptr, &obj);
+  guint ref;
+
+  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));
+
+  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);
+    }
+
+  atk_tree_cache_needs_update();
 }
 
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Registers a new AtkObject.
+ *
+ * Adds the AtkObject to the reference lookup tables.
+ * Adds the AtkObject to the update list.
+ */
 static guint
-register_object (GObject * obj)
+register_accessible (AtkObject *accessible)
+{
+  guint reference;
+
+  g_assert(ATK_IS_OBJECT(accessible));
+
+  reference = 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_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();
+
+  return reference;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* TODO Turn these into an iterator API - Think about the locking */
+
+void
+atk_dbus_foreach_registered(GHFunc func, gpointer data)
+{
+  g_hash_table_foreach(ref2ptr, func, data);
+}
+
+/*---------------------------------------------------------------------------*/
+
+void
+atk_dbus_foreach_update_list(GHFunc func, gpointer data)
 {
-  gint *new_int;
+  g_hash_table_foreach(update_list, func, data);
+  g_hash_table_remove_all(update_list);
+}
+
+/*---------------------------------------------------------------------------*/
+
+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);
+}
+
+/*---------------------------------------------------------------------------*/
 
-  if (!path2ptr)
+/* 
+ * Called on an AtkObject when it has changed in such a way
+ * that the client side cache of the object will need updating.
+ */
+void 
+atk_dbus_notify_change(AtkObject *accessible)
+{
+  guint ref;
+  g_assert(ATK_IS_OBJECT(accessible));
+  
+  if (!g_object_get_data (G_OBJECT (accessible), "dbus-id"))
     {
-      path2ptr = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL);
-      if (!path2ptr)
-       return ++objindex;
+      register_accessible(accessible);
     }
-  objindex++;
-  while (g_hash_table_lookup (path2ptr, &objindex))
+  else
     {
-      objindex++;
-      /* g_object_get_data returning 0 means no data, so handle wrap-around */
-      if (objindex == 0)
-       objindex++;
+      ref = g_object_get_data (G_OBJECT (accessible), "dbus-id");
+      g_hash_table_insert (update_list, ref, accessible);
+      atk_tree_cache_needs_update();
     }
-  new_int = (gint *)g_malloc(sizeof(gint));
-  if (new_int)
-  {
-    *new_int = objindex;
-    g_hash_table_insert (path2ptr, new_int, obj);
-  }
-  g_object_set_data (G_OBJECT (obj), "dbus-id", (gpointer) objindex);
-  g_object_weak_ref(G_OBJECT(obj), deregister_object, NULL);
-  spi_dbus_notify_change(obj, TRUE, NULL);
-  return objindex;
 }
 
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Used to lookup an AtkObject from its D-Bus path.
+ */
 AtkObject *
-spi_dbus_get_object (const char *path)
+atk_dbus_get_object (const char *path)
 {
   guint index;
   void *data;
 
   g_assert (path);
-  if (strncmp(path, "/org/freedesktop/atspi/accessible", 33) != 0) return NULL;
-  path += 33;  /* skip over preamble */
-  if (path[0] == '\0') return atk_get_root();
-  if (path[0] != '/') return NULL;
+  if (strncmp(path, ATK_BRIDGE_OBJECT_PATH_PREFIX, ATK_BRIDGE_PATH_PREFIX_LENGTH) != 0) 
+    return NULL;
+
+  path += ATK_BRIDGE_PATH_PREFIX_LENGTH; /* Skip over the prefix */
+
+  if (path[0] == '\0')
+     return atk_get_root();
+  if (path[0] != '/')
+     return NULL;
+
   path++;
   index = atoi (path);
-  data = g_hash_table_lookup (path2ptr, &index);
+  data = g_hash_table_lookup (ref2ptr, GINT_TO_POINTER(index));
   if (data)
     return ATK_OBJECT (data);
-  return NULL;
+  else
+    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 *
-spi_dbus_get_path (AtkObject * obj)
+atk_dbus_get_path (AtkObject *accessible)
 {
-  if (!obj) return NULL;
-  guint index = (guint) g_object_get_data (G_OBJECT (obj), "dbus-id");
+  guint index;
+
+  g_assert (accessible);
+
+  index = GPOINTER_TO_INT(g_object_get_data (G_OBJECT (accessible), "dbus-id"));
   if (!index)
-    index = register_object (obj);
-  return g_strdup_printf ("/org/freedesktop/atspi/accessible/%d", index);
+    index = register_accessible(G_OBJECT(accessible));
+
+  return g_strdup_printf(ATK_BRIDGE_OBJECT_REFERENCE_TEMPLATE, index);
 }
 
-/* Reply with the given object and dereference it if unref is TRUE */
+/*---------------------------------------------------------------------------*/
+
+/*
+ * 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)
+{
+  AtkObject *child;
+  guint i, n_children;
+  guint ref;
+
+  ref = GPOINTER_TO_INT(g_object_get_data (G_OBJECT (accessible), "dbus-id"));
+  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;
+} 
+
+/*---------------------------------------------------------------------------*/
+
+/* 
+ * Marshals a single object into a D-Bus message.
+ *
+ * Unrefs the AtkObject if unref is true.
+ */
 DBusMessage *
-spi_dbus_return_object (DBusMessage * message, AtkObject * obj, int unref)
+spi_dbus_return_object (DBusMessage *message, AtkObject *obj, gboolean unref)
 {
   DBusMessage *reply;
-  const char *path = spi_dbus_get_path (obj);
+  gchar *path;
+  
+  path = atk_dbus_get_path (obj);
+
   if (unref)
     g_object_unref (obj);
-  if (!path)
-    {
-      /* Should we have a more specific error for this? */
-      return spi_dbus_general_error (message);
-    }
+
   reply = dbus_message_new_method_return (message);
   if (reply)
     {
@@ -118,20 +330,48 @@ spi_dbus_return_object (DBusMessage * message, AtkObject * obj, int unref)
   return reply;
 }
 
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Marshals a variant containing the object reference into the message iter
+ * provided.
+ *
+ * Unrefs the object if unref is true.
+ */
 dbus_bool_t
-spi_dbus_return_v_object (DBusMessageIter * iter, AtkObject * obj, int unref)
+spi_dbus_return_v_object (DBusMessageIter *iter, AtkObject *obj, int unref)
 {
-  const char *path = spi_dbus_get_path (obj);
+  char *path;
+  
+  path = atk_dbus_get_path (obj);
+
   if (unref)
     g_object_unref (obj);
-  if (!path)
-    return FALSE;
+
   return droute_return_v_object (iter, path);
 }
 
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Initializes required global data. The update and removal lists
+ * and the reference lookup tables.
+ *
+ * Initializes all of the required D-Bus interfaces.
+ */
 void
-spi_dbus_initialize (DRouteData * data)
+atk_dbus_initialize (DRouteData * data)
 {
+  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(atk_get_root());
+
   spi_initialize_accessible (data);
   spi_initialize_action(data);
   spi_initialize_collection (data);
@@ -145,50 +385,8 @@ spi_dbus_initialize (DRouteData * data)
   spi_initialize_table (data);
   spi_initialize_text (data);
   spi_initialize_value (data);
-  spi_initialize_introspectable(data, (DRouteGetDatumFunction) spi_dbus_get_object);
+  spi_initialize_introspectable(data, (DRouteGetDatumFunction) atk_dbus_get_object);
 }
 
-static GString *
-spi_get_tree (AtkObject * obj, GString * str, DRouteData * data)
-{
-  int role;
-  const char *name;
-  gchar *path;
-  GSList *l;
-  gint childcount;
-  gint i;
+/*END------------------------------------------------------------------------*/
 
-  if (!obj)
-    return NULL;
-  role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));;
-  name = atk_object_get_name (obj);
-  if (!name)
-    name = "";
-  path = spi_dbus_get_path (obj);
-  g_string_append_printf (str,
-                         "<object path=\"%s\" name=\"%s\" role=\"%d\">\n",
-                         path, name, role);
-  for (l = data->interfaces; l; l = g_slist_next (l))
-    {
-      DRouteInterface *iface_def = (DRouteInterface *) l->data;
-      void *datum = NULL;
-      if (iface_def->get_datum)
-       datum = (*iface_def->get_datum) (path, data->user_data);
-      if (datum)
-       {
-         g_string_append_printf (str, "<interface name=\"%s\"/>\n",
-                                 iface_def->name);
-         if (iface_def->free_datum)
-           (*iface_def->free_datum) (datum);
-       }
-    }
-  childcount = atk_object_get_n_accessible_children (obj);
-  for (i = 0; i < childcount; i++)
-    {
-      AtkObject *child = atk_object_ref_accessible_child (obj, i);
-      str = spi_get_tree (child, str, data);
-      g_object_unref (child);
-    }
-  str = g_string_append (str, "</object>\n");
-  return str;
-}
diff --git a/atk-adaptor/atk-dbus.h b/atk-adaptor/atk-dbus.h
new file mode 100644 (file)
index 0000000..b3b6507
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2008 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+#ifndef __ATK_DBUS__
+#define __ATK_DBUS__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "accessible.h"
+
+void
+atk_dbus_foreach_registered(GHFunc func, gpointer data);
+
+void
+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);
+
+AtkObject *
+atk_dbus_get_object (const char *path);
+
+gchar *
+atk_dbus_get_path_from_ref(guint ref);
+
+gchar *
+atk_dbus_get_path (AtkObject *accessible);
+
+guint
+atk_dbus_register_subtree(AtkObject *accessible);
+
+DBusMessage *
+spi_dbus_return_object (DBusMessage *message, AtkObject *obj, gboolean unref);
+
+dbus_bool_t
+spi_dbus_return_v_object (DBusMessageIter *iter, AtkObject *obj, int unref);
+
+void
+atk_dbus_initialize (DRouteData * data);
+
+#endif /* __ATK_DBUS__ */
index cface25..5560d95 100644 (file)
 #include <atk/atkobject.h>
 #include <atk/atknoopobject.h>
 #include "accessible.h"
+#include "bridge.h"
+#include "atk-dbus.h"
 
 #undef SPI_BRIDGE_DEBUG
 
 #define DBG(a,b) if(_dbg>=(a))b
 
-#define bridge_threads_leave() \
-  if (!during_init_shutdown && !g_main_context_is_owner (NULL)) atk_misc_threads_leave(misc);
-#define bridge_threads_enter() \
-  if (!during_init_shutdown && !g_main_context_is_owner (NULL)) atk_misc_threads_enter(misc);
-
-typedef struct _SpiAppData SpiAppData;
-struct _SpiAppData
-{
-  AtkObject *root;
-  DRouteData droute;
-};
-
 int _dbg = 0;
 static const char *registry = NULL;
-static char *device_event_controller = NULL;
-static SpiAppData *this_app = NULL;
+SpiAppData *this_app = NULL;
 static gboolean registry_died = FALSE;
-static gboolean atk_listeners_registered = FALSE;
 static gint toplevels = 0;
 static gboolean exiting = FALSE;
 static AtkMisc *misc = NULL;
 static gboolean during_init_shutdown = TRUE;
 
-static guint atk_signal_text_changed;
-static guint atk_signal_children_changed;
-static guint atk_signal_active_descendant_changed;
-static guint atk_signal_text_selection_changed;
-
-/* NOT YET USED
-   static guint atk_signal_row_reordered;
-   static guint atk_signal_row_inserted;
-   static guint atk_signal_row_deleted;
-   static guint atk_signal_column_reordered;
-   static guint atk_signal_column_inserted;
-   static guint atk_signal_column_deleted;
-*/
-
-static guint atk_signal_link_selected;
-static guint atk_signal_bounds_changed;
-
 static const char *spi_atk_bridge_get_registry (void);
+static char *device_event_controller = NULL;
+static void     spi_atk_bridge_register_application    (const char *registry);
+static gchar   *spi_atk_bridge_get_registry_ior        (void);
 static gboolean spi_atk_bridge_do_registration         (void);
 static void     spi_atk_bridge_toplevel_added          (AtkObject             *object,
                                                         guint                 index,
@@ -90,36 +64,6 @@ static void     spi_atk_bridge_toplevel_removed        (AtkObject             *o
                                                         AtkObject             *child);
 
 static void     spi_atk_bridge_exit_func               (void);
-static void     spi_atk_register_event_listeners       (void);
-static void     spi_atk_bridge_focus_tracker           (AtkObject             *object);
-static gchar   *spi_atk_bridge_get_registry_ior        (void);
-static void     spi_atk_bridge_register_application    (const char *registry);
-static gboolean spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
-                                                       guint                  n_param_values,
-                                                       const GValue          *param_values,
-                                                       gpointer               data);
-
-static gboolean
-spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
-                               guint n_param_values,
-                               const GValue *param_values,
-                               gpointer data);
-static gboolean
-spi_atk_bridge_document_event_listener (GSignalInvocationHint *signal_hint,
-                                guint n_param_values,
-                                const GValue *param_values,
-                                gpointer data);
-static gboolean
-spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
-                                    guint n_param_values,
-                                    const GValue *param_values,
-                                    gpointer data);
-static gboolean spi_atk_bridge_signal_listener         (GSignalInvocationHint *signal_hint,
-                                                       guint                  n_param_values,
-                                                       const GValue          *param_values,
-                                                       gpointer               data);
-static gint     spi_atk_bridge_key_listener            (AtkKeyEventStruct     *event,
-                                                       gpointer               data);
 static void     spi_atk_tidy_windows                   (void);
 static void     deregister_application                 (SpiAppData *app);
 static void reinit_register_vars (void);
@@ -130,9 +74,6 @@ extern void gnome_accessibility_module_shutdown (void);
 
 static int     atk_bridge_initialized = FALSE;
 static pid_t   atk_bridge_pid = 0;
-static guint   atk_bridge_focus_tracker_id = 0;
-static guint   atk_bridge_key_event_listener_id = 0;
-static GArray *listener_ids = NULL;
 
 /*
  *   These exported symbols are hooked by gnome-program
@@ -141,29 +82,11 @@ static GArray *listener_ids = NULL;
 extern void gnome_accessibility_module_init     (void);
 extern void gnome_accessibility_module_shutdown (void);
 
-static void
-spi_atk_bridge_init_event_type_consts ()
-{
-  static gboolean done = FALSE;
-
-  if (done)
-    return;
+void
+spi_atk_register_event_listeners(void);
 
-  atk_signal_children_changed = g_signal_lookup ("children_changed", 
-                                             ATK_TYPE_OBJECT);
-  atk_signal_text_changed = g_signal_lookup ("text_changed", 
-                                            ATK_TYPE_TEXT);
-  atk_signal_bounds_changed = g_signal_lookup ("bounds_changed", 
-                                             ATK_TYPE_COMPONENT);
-  atk_signal_active_descendant_changed = 
-         g_signal_lookup ("active_descendant_changed", 
-                         ATK_TYPE_OBJECT); 
-  atk_signal_link_selected = g_signal_lookup ("link_selected", 
-                                             ATK_TYPE_HYPERTEXT);
-  atk_signal_text_selection_changed = g_signal_lookup ("text_selection_changed", 
-                                             ATK_TYPE_TEXT);
-  done = TRUE;
-}
+void
+spi_atk_deregister_event_listeners (void);
 
 static gboolean
 post_init (gpointer data)
@@ -236,7 +159,7 @@ spi_app_init (AtkObject *root, gint *argc, gchar **argv[])
 
   dbus_connection_setup_with_g_main(ad->droute.bus, g_main_context_default());
 
-  spi_dbus_initialize (&ad->droute);
+  atk_dbus_initialize (&ad->droute);
   return ad;
 }
 
@@ -307,7 +230,6 @@ atk_bridge_init (gint *argc, gchar **argv[])
   if (success) 
     {
       spi_atk_register_event_listeners ();
-      spi_atk_bridge_init_event_type_consts ();
     }
   else
     {
@@ -377,14 +299,12 @@ spi_atk_bridge_register_application (const char *registry)
   DBusMessage *message, *reply;
   DBusError error;
 
-  bridge_threads_leave ();
   message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, SPI_DBUS_PATH_REGISTRY, SPI_DBUS_INTERFACE_REGISTRY, "registerApplication");
   dbus_error_init (&error);
   reply = dbus_connection_send_with_reply_and_block(this_app->droute.bus, message, 1000, &error);
-  if (error.message) g_warning (error.message);
+  if (error.message) g_print (error.message);
   if (reply) dbus_message_unref (reply);
   if (message) dbus_message_unref (message);
-  bridge_threads_enter ();
 }
 
 /* 
@@ -452,7 +372,6 @@ spi_atk_bridge_get_registry_ior (void)
      
 }
 
-
 static const char *
 spi_atk_bridge_get_registry (void)
 {
@@ -474,116 +393,10 @@ gtk_module_init (gint *argc, gchar **argv[])
 }
 
 static void
-add_signal_listener (const char *signal_name)
-{
-  guint id;
-
-  id = atk_add_global_event_listener (
-    spi_atk_bridge_signal_listener, signal_name);
-
-  g_array_append_val (listener_ids, id);
-}
-
-static void
-spi_atk_register_event_listeners (void)
-{
-  /*
-   * kludge to make sure the Atk interface types are registered, otherwise
-   * the AtkText signal handlers below won't get registered
-   */
-  guint      id;
-  GObject   *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
-  AtkObject *bo = atk_no_op_object_new (ao);
-
-
-  if (atk_listeners_registered) 
-    {
-      g_object_unref (G_OBJECT (bo));
-      g_object_unref (ao);
-      return;
-    }
-
-  atk_listeners_registered = TRUE;
-
-  /* Register for focus event notifications, and register app with central registry  */
-
-  listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
-
-  atk_bridge_focus_tracker_id = atk_add_focus_tracker (spi_atk_bridge_focus_tracker);
-
-  id = atk_add_global_event_listener (spi_atk_bridge_property_event_listener,
-                                     "Gtk:AtkObject:property-change");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
-                                     "window:create");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
-                                     "window:destroy");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
-                                     "window:minimize");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
-                                     "window:maximize");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
-                                     "window:restore");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
-                                     "window:activate");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
-                                     "window:deactivate");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_document_event_listener,
-                                     "Gtk:AtkDocument:load-complete");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_document_event_listener,
-                                     "Gtk:AtkDocument:reload");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_document_event_listener,
-                                     "Gtk:AtkDocument:load-stopped");
-  g_array_append_val (listener_ids, id);
-  id = atk_add_global_event_listener (spi_atk_bridge_state_event_listener,
-                                     "Gtk:AtkObject:state-change");
-  g_array_append_val (listener_ids, id);
-
-  add_signal_listener ("Gtk:AtkObject:children-changed");
-  add_signal_listener ("Gtk:AtkObject:visible-data-changed");
-  add_signal_listener ("Gtk:AtkObject:active-descendant-changed");
-  add_signal_listener ("Gtk:AtkComponent:bounds-changed");
-  add_signal_listener ("Gtk:AtkSelection:selection-changed");
-  add_signal_listener ("Gtk:AtkText:text-selection-changed");
-  add_signal_listener ("Gtk:AtkText:text-changed");
-  add_signal_listener ("Gtk:AtkText:text-caret-moved");
-  add_signal_listener ("Gtk:AtkTable:row-inserted");
-  add_signal_listener ("Gtk:AtkTable:row-reordered");
-  add_signal_listener ("Gtk:AtkTable:row-deleted");
-  add_signal_listener ("Gtk:AtkTable:column-inserted");
-  add_signal_listener ("Gtk:AtkTable:column-reordered");
-  add_signal_listener ("Gtk:AtkTable:column-deleted");
-  add_signal_listener ("Gtk:AtkTable:model-changed");
-  add_signal_listener ("Gtk:AtkHypertext:link-selected");
-/*
- * May add the following listeners to implement preemptive key listening for GTK+
- *
- * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
- * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
- */
-  atk_bridge_key_event_listener_id = atk_add_key_event_listener (
-    spi_atk_bridge_key_listener, NULL);
-  
-  g_object_unref (G_OBJECT (bo));
-  g_object_unref (ao);
-}
-
-static void
 deregister_application (SpiAppData *app)
 {
   const char *registry = spi_atk_bridge_get_registry ();
-  bridge_threads_leave ();
   // todo: deregister
-  bridge_threads_enter ();
 }
 
 static void
@@ -640,7 +453,6 @@ void
 gnome_accessibility_module_shutdown (void)
 {
   int     i;
-  GArray *ids = listener_ids;
   
   if (!atk_bridge_initialized)
     {
@@ -654,17 +466,7 @@ gnome_accessibility_module_shutdown (void)
        g_print("Atk Accessibility bridge shutdown\n");
     }
 
-  listener_ids = NULL;
-  if (atk_bridge_focus_tracker_id)
-        atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
-  
-  for (i = 0; ids && i < ids->len; i++)
-    {
-          atk_remove_global_event_listener (g_array_index (ids, guint, i));
-    }
-  
-  if (atk_bridge_key_event_listener_id)
-          atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
+  spi_atk_deregister_event_listeners();
 
   deregister_application (this_app);
   this_app = NULL;
@@ -672,542 +474,6 @@ gnome_accessibility_module_shutdown (void)
   misc = NULL;
 }
 
-static void emit(AtkObject *object,
-                const char *name,
-                const char *detail,
-                dbus_int32_t detail1,
-                dbus_int32_t detail2,
-                int type,
-                const void *val)
-{
-  DBusMessage *sig;
-  char *path = spi_dbus_get_path(object);
-  DBusMessageIter iter, sub;
-  const char *type_as_string = NULL;
-  dbus_int32_t dummy = 0;
-
-  spi_dbus_update_cache(&this_app->droute);
-  if (type == DBUS_TYPE_OBJECT_PATH)
-  {
-    type_as_string = "o";
-    if (!val) val = "";
-  }
-  else if (type == DBUS_TYPE_STRING)
-  {
-    type_as_string = "s";
-    if (!val) val = "";
-  }
-  else if (type == DBUS_TYPE_INT32) type_as_string = "i";
-  else if (type == DBUS_TYPE_UINT32) type_as_string = "u";
-  else if (type == DBUS_TYPE_INVALID)
-  {
-    type = DBUS_TYPE_UINT32;
-    type_as_string = "u";
-    if (!val) val = &dummy;
-  }
-  else
-  {
-    g_warning("Unknown type %d in property change signal", type);
-  }
-  sig = dbus_message_new_signal(path, SPI_DBUS_INTERFACE_ACCESSIBLE, name);
-  dbus_message_iter_init_append(sig, &iter);
-  if (!detail) detail = "";
-  dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &detail);
-  dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1);
-  dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2);
-  dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type_as_string, &sub);
-  dbus_message_iter_append_basic(&sub, type, &val);
-  dbus_message_iter_close_container(&iter, &sub);
-  dbus_connection_send(this_app->droute.bus, sig, NULL);
-  g_free(path);
-  dbus_message_unref(sig);
-}
-
-static void
-spi_atk_bridge_focus_tracker (AtkObject *object)
-{
-  emit(object, "focus", NULL, 0, 0, DBUS_TYPE_INVALID, NULL);
-}
-
-static void emit_rect(AtkObject *object, const char *name, const char *detail, AtkRectangle *rect)
-{
-  DBusMessage *sig;
-  char *path = spi_dbus_get_path(object);
-  DBusMessageIter iter, iter_variant, sub;
-  dbus_uint32_t x, y, width, height;
-  dbus_int32_t dummy = 0;
-
-  spi_dbus_update_cache(&this_app->droute);
-  x = rect->x;
-  y = rect->y;
-  width = rect->width;
-  height = rect->height;
-  sig = dbus_message_new_signal(path, SPI_DBUS_INTERFACE_ACCESSIBLE, name);
-  if (!detail) detail = "";
-  if (sig)
-    {
-      dbus_message_iter_init_append (sig, &iter);
-      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &detail);
-      dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
-      dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
-      if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(iiii)", &iter_variant))
-       goto oom;
-      if (!dbus_message_iter_open_container (&iter_variant, DBUS_TYPE_STRUCT, NULL, &sub))
-       goto oom;
-      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &x);
-      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &y);
-      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &width);
-      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &height);
-      if (!dbus_message_iter_close_container (&iter_variant, &sub))
-       goto oom;
-      if (!dbus_message_iter_close_container (&iter, &iter_variant))
-       goto oom;
-    }
-  dbus_connection_send(this_app->droute.bus, sig, NULL);
-oom:
-  g_free(path);
-  dbus_message_unref(sig);
-}
-
-static const char *PropertyChange = "object_property_change";
-
-static gboolean
-spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
-                                       guint n_param_values,
-                                       const GValue *param_values,
-                                       gpointer data)
-{
-  AtkPropertyValues *values;
-  AtkObject *obj;
-  const gchar *prop_name;
-  const gchar *sp = NULL;
-  AtkObject *ao;
-  char *s_ao = NULL;
-  gint i;
-  const gchar *name = NULL;
-
-#ifdef SPI_BRIDGE_DEBUG
-  GSignalQuery signal_query;
-  const gchar *signame;
-  const gchar *s, *s2;
-  
-  g_signal_query (signal_hint->signal_id, &signal_query);
-  signame = signal_query.signal_name;
-
-  s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
-  s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
-  values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
-  DBG (2, g_message ("Received (property) signal %s:%s:%s from object %s (gail %s)\n",
-          g_type_name (signal_query.itype), signame, values->property_name, s, s2));
-  
-#endif
-
-  obj = g_value_get_object (param_values + 0);
-  name = atk_object_get_name (obj);
-  values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
-
-  prop_name = values->property_name;
-  if (strcmp (prop_name, "accessible-name") == 0)
-    {
-      spi_dbus_notify_change(obj, FALSE, &this_app->droute);
-    }
-  else if (strcmp (prop_name, "accessible-description") == 0)
-    {
-      spi_dbus_notify_change(obj, FALSE, &this_app->droute);
-    }
-  else if (strcmp (prop_name, "accessible-parent") == 0)
-    {
-      spi_dbus_notify_change(obj, FALSE, &this_app->droute);
-    }
-  else if (strcmp (prop_name, "accessible-table-summary") == 0)
-    {
-      ao = atk_table_get_summary (ATK_TABLE (obj));
-      s_ao = spi_dbus_get_path(ao);
-      emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_OBJECT_PATH, s_ao);
-    }
-  else if (strcmp (prop_name, "accessible-table-column-header") == 0)
-    {
-      i = g_value_get_int (&(values->new_value));
-      ao = atk_table_get_column_header (ATK_TABLE (obj), i);
-      s_ao = spi_dbus_get_path(ao);
-      emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_OBJECT_PATH, s_ao);
-    }
-  else if (strcmp (prop_name, "accessible-table-row-header") == 0)
-    {
-      i = g_value_get_int (&(values->new_value));
-      ao = atk_table_get_row_header (ATK_TABLE (obj), i);
-      s_ao = spi_dbus_get_path(ao);
-      emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_OBJECT_PATH, s_ao);
-    }
-  else if (strcmp (prop_name, "accessible-table-row-description") == 0)
-    {
-      i = g_value_get_int (&(values->new_value));
-      sp = atk_table_get_row_description (ATK_TABLE (obj), i);
-      emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_STRING, sp);
-    }
-  else if (strcmp (prop_name, "accessible-table-column-description") == 0)
-    {
-      i = g_value_get_int (&(values->new_value));
-      sp = atk_table_get_column_description (ATK_TABLE(obj), i);
-      emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_STRING, sp);
-    }
-  else if (strcmp (prop_name, "accessible-table-caption-object") == 0)
-    {
-      ao = atk_table_get_caption (ATK_TABLE(obj));
-      sp = atk_object_get_name (ao);
-      emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_STRING, sp);
-    }
-  else
-    {
-      emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_INVALID, NULL);
-    }
-  if (s_ao) g_free(s_ao);
-  return TRUE;
-}
-
-static gboolean
-spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
-                                    guint n_param_values,
-                                    const GValue *param_values,
-                                    gpointer data)
-{
-  AtkObject *obj;
-  gchar *property_name;
-  unsigned long detail1;
-#ifdef SPI_BRIDGE_DEBUG
-  GSignalQuery signal_query;
-  const gchar *name;
-  
-  g_signal_query (signal_hint->signal_id, &signal_query);
-  name = signal_query.signal_name;
-  fprintf (stderr, "Received (state) signal %s:%s\n",
-          g_type_name (signal_query.itype), name);
-#endif
-
-  obj = ATK_OBJECT(g_value_get_object (param_values + 0));
-  property_name = g_strdup (g_value_get_string (param_values + 1));
-  /* Ignore defunct for now; we'll send a tree update to remove it when
-     the object goes away */
-  /* Also ignore state changes for objects not yet broadcast */
-  if ((property_name && !strcmp(property_name, "defunct")) ||
-      !spi_dbus_object_is_known(obj))
-  {
-    g_free(property_name);
-    return TRUE;
-  }
-  detail1 = (g_value_get_boolean (param_values + 2))
-    ? 1 : 0;
-  emit(obj, "object_state_changed", property_name, detail1, 0, DBUS_TYPE_INVALID, NULL);
-  g_free (property_name);
-  return TRUE;
-}
-
-static void
-spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent  *keystroke,
-                                      AtkKeyEventStruct          *event)
-{
-#ifdef SPI_DEBUG
-  if (event)
-    {
-      g_print ("event %c (%d)\n", (int) event->keyval, (int) event->keycode);
-    }
-  else
-#endif
-  if (!event)
-    { /* this doesn't really need translating */
-      g_print (_("WARNING: NULL key event reported."));
-    }
-  
-  keystroke->id        = (dbus_int32_t) event->keyval;
-  keystroke->hw_code   = (dbus_int16_t) event->keycode;
-  keystroke->timestamp = (dbus_uint32_t) event->timestamp;
-  keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF);
-  if (event->string)
-    {
-      gunichar c;
-
-      keystroke->event_string = g_strdup (event->string);
-      c = g_utf8_get_char_validated (event->string, -1);
-      if (c > 0 && g_unichar_isprint (c))
-        keystroke->is_text = TRUE;
-      else
-        keystroke->is_text = FALSE;
-    }
-  else
-    {
-      keystroke->event_string = g_strdup ("");
-      keystroke->is_text = FALSE;
-    }
-  switch (event->type)
-    {
-    case (ATK_KEY_EVENT_PRESS):
-      keystroke->type = Accessibility_KEY_PRESSED_EVENT;
-      break;
-    case (ATK_KEY_EVENT_RELEASE):
-      keystroke->type = Accessibility_KEY_RELEASED_EVENT;
-      break;
-    default:
-      keystroke->type = 0;
-      break;
-    }
-#if 0  
-  g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
-          (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
-          (int) keystroke->modifiers,
-          keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
-#endif
-}
-
-static gboolean Accessibility_DeviceEventController_notifyListenersSync(const Accessibility_DeviceEvent *key_event)
-{
-  DBusMessage *message = dbus_message_new_method_call(SPI_DBUS_NAME_REGISTRY, SPI_DBUS_PATH_REGISTRY, SPI_DBUS_INTERFACE_DEC, "notifyListenersSync");
-  DBusError error;
-  dbus_bool_t consumed = FALSE;
-
-  dbus_error_init(&error);
-  if (spi_dbus_marshal_deviceEvent(message, key_event))
-  {
-    DBusMessage *reply = dbus_connection_send_with_reply_and_block(this_app->droute.bus, message, 1000, &error);
-    if (reply)
-    {
-      DBusError error;
-      dbus_error_init(&error);
-      dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &consumed, DBUS_TYPE_INVALID);
-      dbus_message_unref(reply);
-    }
-  }
-  dbus_message_unref(message);
-  return consumed;
-}
-
-static gint
-spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
-{
-  gboolean             result;
-  Accessibility_DeviceEvent key_event;
-
-  spi_init_keystroke_from_atk_key_event (&key_event, event);
-
-  bridge_threads_leave ();
-  result = Accessibility_DeviceEventController_notifyListenersSync (&key_event);
-  bridge_threads_enter ();
-
-  if (key_event.event_string) g_free (key_event.event_string);
-
-  return result;
-}
-
-static gboolean
-spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
-                               guint n_param_values,
-                               const GValue *param_values,
-                               gpointer data)
-{
-  AtkObject *obj;
-  GSignalQuery signal_query;
-  const gchar *name;
-  const gchar *detail = NULL;
-  char *sp = NULL;
-  AtkObject *ao;
-  gint detail1 = 0, detail2 = 0;
-  char *s_ao = NULL;
-  gchar *sig_name;
-  char *p;
-#ifdef SPI_BRIDGE_DEBUG
-  const gchar *s, *s2;
-#endif 
-  
-  g_signal_query (signal_hint->signal_id, &signal_query);
-
-  name = signal_query.signal_name;
-  if (signal_hint->detail)
-  {
-    detail = g_quark_to_string (signal_hint->detail);
-  }
-  sig_name = g_strdup_printf("object_%s", name);
-  while ((p = strchr(sig_name, '-')) != NULL) *p = '_';
-
-#ifdef SPI_BRIDGE_DEBUG
-  s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
-  s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
-  fprintf (stderr, "Received signal %s:%s detail: %s from object %s (gail %s)\n",
-          g_type_name (signal_query.itype), name, 
-          detail ? detail : "<NULL>", s ? s : "<NULL>" , s2);
-#endif
-  
-  obj = ATK_OBJECT(g_value_get_object (param_values + 0));
-
-  if (signal_query.signal_id == atk_signal_active_descendant_changed)
-    {
-      gpointer child = g_value_get_pointer (param_values + 1);
-
-      g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
-
-      ao = ATK_OBJECT (child);
-
-      detail1 = atk_object_get_index_in_parent (ao);
-      s_ao = spi_dbus_get_path(child);
-      emit(obj, sig_name, detail, detail1, 0, DBUS_TYPE_OBJECT_PATH, s_ao);
-      g_free(s_ao);
-    }
-  else if (signal_query.signal_id == atk_signal_link_selected)
-    {
-      if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
-        detail1 = g_value_get_int (param_values + 1);
-      emit(obj, sig_name, detail, detail1, 0, DBUS_TYPE_INVALID, NULL);
-    }
-  else if (signal_query.signal_id == atk_signal_bounds_changed)
-    {
-      AtkRectangle *atk_rect = NULL;
-
-      if (G_VALUE_HOLDS_BOXED (param_values + 1))
-         atk_rect = g_value_get_boxed (param_values + 1);
-      emit_rect(obj, sig_name, detail, atk_rect);
-    }
-  else if ((signal_query.signal_id == atk_signal_children_changed) && obj)
-    {
-      spi_dbus_notify_change(obj, FALSE, &this_app->droute);
-    }
-  else
-    {
-      if (n_param_values >= 2)
-        {
-          if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
-            detail1 = g_value_get_int (param_values + 1);
-          if (n_param_values >= 3)
-            {
-              if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT)
-                detail2 = g_value_get_int (param_values + 2);
-            }
-        }
-
-      if (signal_query.signal_id == atk_signal_text_changed)
-        {
-          sp = atk_text_get_text (ATK_TEXT (obj),
-                                 detail1,
-                                 detail1+detail2);
-          emit(obj, sig_name, detail, detail1, detail2, DBUS_TYPE_STRING, sp);
-        }
-      else if (signal_query.signal_id == atk_signal_text_selection_changed)
-        {
-          /* Return NULL as the selected string */
-          emit(obj, sig_name, detail, detail1, detail2, DBUS_TYPE_STRING, "");
-        }
-      else
-        {
-          emit(obj, sig_name, detail, 0, 0, DBUS_TYPE_INVALID, NULL);
-        }
-    }
-
-  if (sp) 
-    g_free (sp);
-
-  if (s_ao)
-     g_free (s_ao);
-  g_free(sig_name);
-
-  return TRUE;
-}
-
-static gboolean
-spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
-                                     guint n_param_values,
-                                     const GValue *param_values,
-                                     gpointer data)
-{
-  AtkObject *obj;
-  GSignalQuery signal_query;
-  const gchar *name, *s;
-#ifdef SPI_BRIDGE_DEBUG
-  const gchar *s2;
-#endif
-  
-  g_signal_query (signal_hint->signal_id, &signal_query);
-
-  name = signal_query.signal_name;
-  gchar *sig_name;
-
-#ifdef SPI_BRIDGE_DEBUG
-  s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
-  s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
-  fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
-          g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
-#endif
-  
-  obj = ATK_OBJECT(g_value_get_object (param_values + 0));
-
-  s = atk_object_get_name (obj);
-  sig_name = g_strdup_printf("window_%s", name);
-  emit(obj, sig_name, NULL, 0, 0, DBUS_TYPE_STRING, s);
-  g_free(sig_name);
-
-  return TRUE;
-}
-
-static gboolean
-spi_atk_bridge_document_event_listener (GSignalInvocationHint *signal_hint,
-                                      guint n_param_values,
-                                      const GValue *param_values,
-                                      gpointer data)
-{
-  AtkObject *obj;
-  GSignalQuery signal_query;
-  const gchar *name, *s;
-  gchar *sig_name;
-#ifdef SPI_BRIDGE_DEBUG
-  const gchar *s2;
-#endif
-
-  g_signal_query (signal_hint->signal_id, &signal_query);
-
-  name = signal_query.signal_name;
-
-#ifdef SPI_BRIDGE_DEBUG
-  s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
-  s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
-  fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
-           g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
-#endif
-
-  obj = ATK_OBJECT(g_value_get_object (param_values + 0));
-
-  s = atk_object_get_name (obj);
-  sig_name = g_strdup_printf("document_%s", name);
-  emit(obj, sig_name, NULL, 0, 0, DBUS_TYPE_STRING, s);
-  g_free(sig_name);
-  return TRUE;
-}
-
-static void
-spi_atk_tidy_windows (void)
-{
-  AtkObject *root;
-  gint n_children;
-  gint i;
-
-  root = atk_get_root ();
-  n_children = atk_object_get_n_accessible_children (root);
-  for (i = 0; i < n_children; i++)
-    {
-      AtkObject *child;
-      AtkStateSet *stateset;
-      const gchar *name;
-     
-      child = atk_object_ref_accessible_child (root, i);
-      stateset = atk_object_ref_state_set (child);
-      
-      name = atk_object_get_name (child);
-      if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
-        {
-         emit(child, "window_deactivate", NULL, 0, 0, DBUS_TYPE_STRING, name);
-        }
-      g_object_unref (stateset);
-
-      emit(child, "window_destroy", NULL, 0, 0, DBUS_TYPE_STRING, name);
-      g_object_unref (child);
-    }
-}
-
 static void
 reinit_register_vars (void)
 {
diff --git a/atk-adaptor/bridge.h b/atk-adaptor/bridge.h
new file mode 100644 (file)
index 0000000..b2bfc51
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
+ * Copyright 2001, 2002, 2003 Ximian, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+typedef struct _SpiAppData SpiAppData;
+struct _SpiAppData
+{
+  AtkObject *root;
+  DRouteData droute;
+};
index 4cfe6d3..317e147 100644 (file)
@@ -26,7 +26,7 @@
 #include "spi-common/bitarray.h"
 #include <string.h>
 
-#define get_object(message) spi_dbus_get_object(dbus_message_get_path(message))
+#define get_object(message) atk_dbus_get_object(dbus_message_get_path(message))
 
 typedef struct _MatchRulePrivate MatchRulePrivate;
 struct _MatchRulePrivate
@@ -788,7 +788,7 @@ getMatchesInOrder (DBusMessage *message,
 
   ls = g_list_append (ls, current_object);
 
-  obj = spi_dbus_get_object (dbus_message_get_path (message));
+  obj = atk_dbus_get_object (dbus_message_get_path (message));
   
   kount = inorder (obj, mrp, ls, 0, count, 
                    current_object, TRUE, NULL, traverse);
@@ -820,7 +820,7 @@ getMatchesInBackOrder (DBusMessage *message,
 
   ls = g_list_append (ls, current_object);
 
-  collection = spi_dbus_get_object (dbus_message_get_path (message));
+  collection = atk_dbus_get_object (dbus_message_get_path (message));
 
   kount = sort_order_rev_canonical (mrp, ls, 0, count, current_object, 
                                    FALSE, collection);
@@ -856,7 +856,7 @@ getMatchesTo (DBusMessage *message,
                          obj, 0, TRUE, current_object, TRUE, traverse);
   }
   else{ 
-    obj = spi_dbus_get_object (dbus_message_get_path (message));
+    obj = atk_dbus_get_object (dbus_message_get_path (message));
     kount = query_exec (mrp,  sortby, ls, 0, count,
                         obj, 0, TRUE, current_object, TRUE, traverse); 
 
@@ -886,7 +886,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 = spi_dbus_get_object (current_object_path);
+  current_object = atk_dbus_get_object (current_object_path);
   if (!current_object)
   {
     // TODO: object-not-found error
@@ -939,7 +939,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 = spi_dbus_get_object (current_object_path);
+  current_object = atk_dbus_get_object (current_object_path);
   if (!current_object)
   {
     // TODO: object-not-found error
index 8230dc1..fdfaaee 100644 (file)
@@ -27,7 +27,7 @@
 static AtkComponent *
 get_component (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_COMPONENT (obj);
@@ -36,7 +36,7 @@ get_component (DBusMessage * message)
 static AtkComponent *
 get_component_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_COMPONENT(obj))
     return NULL;
   return ATK_COMPONENT (obj);
index e576e51..302f4ab 100644 (file)
@@ -27,7 +27,7 @@
 static AtkDocument *
 get_document (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_DOCUMENT (obj);
@@ -36,7 +36,7 @@ get_document (DBusMessage * message)
 static AtkDocument *
 get_document_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_DOCUMENT(obj))
     return NULL;
   return ATK_DOCUMENT (obj);
index 4de77cb..8e16060 100644 (file)
@@ -27,7 +27,7 @@
 static AtkEditableText *
 get_editable (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_EDITABLE_TEXT (obj);
@@ -36,7 +36,7 @@ get_editable (DBusMessage * message)
 static AtkEditableText *
 get_editable_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_EDITABLE_TEXT(obj))
     return NULL;
   return ATK_EDITABLE_TEXT (obj);
diff --git a/atk-adaptor/event.c b/atk-adaptor/event.c
new file mode 100644 (file)
index 0000000..a4cbd9d
--- /dev/null
@@ -0,0 +1,877 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2008, Codethink Ltd.
+ * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
+ * Copyright 2001, 2002, 2003 Ximian, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+#include <atk/atk.h>
+#include <string.h>
+#include "accessible.h"
+#include "bridge.h"
+#include "atk-dbus.h"
+
+extern SpiAppData *this_app;
+
+static GArray *listener_ids = NULL;
+
+static gint atk_bridge_key_event_listener_id;
+static gint atk_bridge_focus_tracker_id;
+
+
+/*---------------------------------------------------------------------------*/
+
+#define ITF_EVENT_OBJECT   "org.freedesktop.atspi.Event.Object"
+#define ITF_EVENT_WINDOW   "org.freedesktop.atspi.Event.Window"
+#define ITF_EVENT_DOCUMENT "org.freedekstop.atspi.Event.Document"
+#define ITF_EVENT_FOCUS    "org.freedesktop.atspi.Event.Focus"
+
+/*---------------------------------------------------------------------------*/
+
+static gboolean
+Accessibility_DeviceEventController_notifyListenersSync(const Accessibility_DeviceEvent *key_event)
+{
+  DBusMessage *message;
+  DBusError error;
+  dbus_bool_t consumed = FALSE;
+
+  message = 
+  dbus_message_new_method_call(SPI_DBUS_NAME_REGISTRY, 
+                              SPI_DBUS_PATH_REGISTRY,
+                              SPI_DBUS_INTERFACE_DEC,
+                              "notifyListenersSync");
+
+  dbus_error_init(&error);
+  if (spi_dbus_marshal_deviceEvent(message, key_event))
+  {
+    DBusMessage *reply = dbus_connection_send_with_reply_and_block(this_app->droute.bus, message, 1000, &error);
+    if (reply)
+    {
+      DBusError error;
+      dbus_error_init(&error);
+      dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &consumed, DBUS_TYPE_INVALID);
+      dbus_message_unref(reply);
+    }
+  }
+  dbus_message_unref(message);
+  return consumed;
+}
+
+static void
+spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent  *keystroke,
+                                      AtkKeyEventStruct          *event)
+{
+  keystroke->id        = (dbus_int32_t) event->keyval;
+  keystroke->hw_code   = (dbus_int16_t) event->keycode;
+  keystroke->timestamp = (dbus_uint32_t) event->timestamp;
+  keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF);
+  if (event->string)
+    {
+      gunichar c;
+
+      keystroke->event_string = g_strdup (event->string);
+      c = g_utf8_get_char_validated (event->string, -1);
+      if (c > 0 && g_unichar_isprint (c))
+        keystroke->is_text = TRUE;
+      else
+        keystroke->is_text = FALSE;
+    }
+  else
+    {
+      keystroke->event_string = g_strdup ("");
+      keystroke->is_text = FALSE;
+    }
+  switch (event->type)
+    {
+    case (ATK_KEY_EVENT_PRESS):
+      keystroke->type = Accessibility_KEY_PRESSED_EVENT;
+      break;
+    case (ATK_KEY_EVENT_RELEASE):
+      keystroke->type = Accessibility_KEY_RELEASED_EVENT;
+      break;
+    default:
+      keystroke->type = 0;
+      break;
+    }
+#if 0  
+  g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
+          (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
+          (int) keystroke->modifiers,
+          keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
+#endif
+}
+
+
+static gint
+spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
+{
+  gboolean             result;
+  Accessibility_DeviceEvent key_event;
+
+  spi_init_keystroke_from_atk_key_event (&key_event, event);
+
+  result = Accessibility_DeviceEventController_notifyListenersSync (&key_event);
+
+  if (key_event.event_string) g_free (key_event.event_string);
+
+  return result;
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Emits an AT-SPI event.
+ * AT-SPI events names are split into three parts:
+ * class:major:minor
+ * This is mapped onto D-Bus events as:
+ * D-Bus Interface:Signal Name:Detail argument
+ *
+ * Marshals a basic type into the 'any_data' attribute of
+ * the AT-SPI event.
+ */
+
+/*
+ * This is a rather annoying function needed to replace
+ * NULL values of strings with the empty string. Null string
+ * values can be created by the atk_object_get_name or text selection
+ */
+static const void *
+provide_defaults(const gint type,
+                const void *val)
+{
+  switch (type)
+    {
+      case DBUS_TYPE_STRING:
+      case DBUS_TYPE_OBJECT_PATH:
+          if (!val)
+             return "";
+          else
+             return val;
+      default:
+          return val;
+    }
+}
+
+
+/* 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,
+     const char *major,
+     const char *minor,
+     dbus_int32_t detail1,
+     dbus_int32_t detail2,
+     const char *type,
+     const void *val)
+{
+  DBusMessage *sig;
+  DBusMessageIter iter, sub;
+  gchar *path, *cname, *t;
+
+  if (!klass) klass = "";
+  if (!major) major = "";
+  if (!minor) minor = "";
+
+  /*
+   * This is very annoying, but as '-' isn't a legal signal
+   * name in D-Bus (Why not??!?) The names need converting
+   * on this side, and again on the client side.
+   */
+  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);
+
+  dbus_message_iter_init_append(sig, &iter);
+
+  dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
+  dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1);
+  dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2);
+
+  dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type, &sub);
+  /*
+   * I need to convert the string signature to an integer type signature.
+   * DBUS_TYPE_INT32 is defined as 'i' whereas the string is "i".
+   * I should just be able to cast the first character of the string to an
+   * integer.
+   */
+  val = provide_defaults((int) *type, val);
+  dbus_message_iter_append_basic(&sub, (int) *type, &val);
+  dbus_message_iter_close_container(&iter, &sub);
+
+  dbus_connection_send(this_app->droute.bus, sig, NULL);
+  dbus_message_unref(sig);
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Emits an AT-SPI event, marshalling a BoundingBox structure into the 
+ * 'any_data' variant of the event.
+ */
+static void
+emit_rect(AtkObject  *accessible,
+         const char *klass,
+         const char *major,
+         const char *minor,
+         AtkRectangle *rect)
+{
+  DBusMessage *sig;
+  DBusMessageIter iter, variant, sub;
+  gchar *path, *cname, *t;
+  dbus_int32_t dummy = 0;
+
+  if (!klass) klass = "";
+  if (!major) major = "";
+  if (!minor) minor = "";
+
+  /*
+   * This is very annoying, but as '-' isn't a legal signal
+   * name in D-Bus (Why not??!?) The names need converting
+   * on this side, and again on the client side.
+   */
+  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);
+
+  dbus_message_iter_init_append (sig, &iter);
+  dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(iiii)", &variant);
+    dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub);
+      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x));
+      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y));
+      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width));
+      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height));
+    dbus_message_iter_close_container (&variant, &sub);
+  dbus_message_iter_close_container (&iter, &variant);
+
+  dbus_connection_send(this_app->droute.bus, sig, NULL);
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * The tree update listener handles the following Atk signals:
+ *
+ * Gtk:AtkObject:property-change
+ * 
+ * With the folowing property names:
+ *
+ * accessible-name
+ * accessible-description
+ * accessible-parent
+ *
+ * It updates the server side accessible-object database, which
+ * will then syncronize with the client-side accessible cache.
+ * 
+ */
+static gboolean
+tree_update_listener (GSignalInvocationHint *signal_hint,
+                     guint                  n_param_values,
+                     const GValue          *param_values,
+                     gpointer               data)
+{
+  AtkObject *accessible;
+  AtkPropertyValues *values;
+  const gchar *pname = NULL;
+
+  accessible = g_value_get_object (&param_values[0]);
+  values = (AtkPropertyValues*) g_value_get_pointer (&param_values[1]);
+
+  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)
+    {
+      atk_dbus_notify_change(accessible);
+    }
+  return TRUE;
+}
+
+/* 
+ * Handles the ATK signal 'Gtk:AtkObject:children-changed'.
+ *
+ * It updates the server side accessible-object database, which
+ * will then syncronize with the client-side accessible cache.
+ *
+ */
+static gboolean
+tree_update_children_listener (GSignalInvocationHint *signal_hint,
+                              guint                  n_param_values,
+                              const GValue          *param_values,
+                              gpointer               data)
+{
+  AtkObject *accessible;
+
+  accessible = g_value_get_object (&param_values[0]);
+  atk_dbus_register_subtree(accessible);
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * The focus listener handles the ATK 'focus' signal and forwards it
+ * as the AT-SPI event, 'focus:'
+ */
+static void
+focus_tracker (AtkObject *accessible)
+{
+  emit(accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
+}
+
+/*---------------------------------------------------------------------------*/
+
+#define PCHANGE "property-change"
+
+/* 
+ * This handler handles the following ATK signals and
+ * converts them to AT-SPI events:
+ *  
+ * Gtk:AtkObject:property-change -> object:property-change:(property-name)
+ *
+ * The property-name is part of the ATK property-change signal.
+ */
+static gboolean
+property_event_listener (GSignalInvocationHint *signal_hint,
+                        guint                  n_param_values,
+                        const GValue          *param_values,
+                        gpointer               data)
+{
+  AtkObject *accessible;
+  AtkPropertyValues *values;
+
+  const gchar *pname = NULL;
+
+  AtkObject *otemp;
+  const gchar *stemp;
+  gint i;
+  
+  accessible = g_value_get_object (&param_values[0]);
+  values = (AtkPropertyValues*) g_value_get_pointer (&param_values[1]);
+
+  pname = values[0].property_name;
+
+  /* TODO Could improve this control statement by matching
+   * on only the end of the signal names,
+   */
+  if (strcmp (pname, "accessible-table-summary") == 0)
+    {
+      otemp = atk_table_get_summary(ATK_TABLE (accessible));
+      stemp = atk_dbus_get_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);
+      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);
+      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)
+    {
+      i = g_value_get_int (&(values->new_value));
+      stemp = atk_table_get_row_description(ATK_TABLE (accessible), i);
+      emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
+    }
+  else if (strcmp (pname, "accessible-table-column-description") == 0)
+    {
+      i = g_value_get_int (&(values->new_value));
+      stemp = atk_table_get_column_description(ATK_TABLE (accessible), i);
+      emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
+    }
+  else if (strcmp (pname, "accessible-table-caption-object") == 0)
+    {
+      otemp = atk_table_get_caption(ATK_TABLE(accessible));
+      stemp = atk_object_get_name(otemp);
+      emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
+    }
+  else
+    {
+      emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
+    }
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+#define STATE_CHANGED "state-changed"
+
+/*
+ * The state event listener handles 'Gtk:AtkObject:state-change' ATK signals
+ * and forwards them as object:state-changed:(param-name) AT-SPI events. Where
+ * the param-name is part of the ATK state-change signal.
+ */
+static gboolean
+state_event_listener (GSignalInvocationHint *signal_hint,
+                     guint n_param_values,
+                     const GValue *param_values,
+                     gpointer data)
+{
+  AtkObject *accessible;
+  gchar *pname;
+  guint detail1;
+
+  accessible = ATK_OBJECT(g_value_get_object (&param_values[0]));
+  pname = g_strdup (g_value_get_string (&param_values[1]));
+
+  /* TODO - Possibly ignore a change to the 'defunct' state.
+   * This is because without reference counting defunct objects should be removed.
+   */
+  detail1 = (g_value_get_boolean (&param_values[2])) ? 1 : 0;
+  emit(accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
+  g_free (pname);
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * The window event listener handles the following ATK signals and forwards
+ * them as AT-SPI events:
+ *
+ * window:create     -> window:create
+ * window:destroy    -> window:destroy
+ * window:minimize   -> window:minimize
+ * window:maximize   -> window:maximize
+ * window:activate   -> window:activate
+ * window:deactivate -> window:deactivate
+ */
+static gboolean
+window_event_listener (GSignalInvocationHint *signal_hint,
+                      guint n_param_values,
+                      const GValue *param_values,
+                      gpointer data)
+{
+  AtkObject *accessible;
+  GSignalQuery signal_query;
+  const gchar *name, *s;
+  
+  g_signal_query (signal_hint->signal_id, &signal_query);
+  name = signal_query.signal_name;
+
+  accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
+  s = atk_object_get_name (accessible);
+  emit(accessible, ITF_EVENT_WINDOW, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
+
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* 
+ * The document event listener handles the following ATK signals
+ * and converts them to AT-SPI events:
+ *
+ * Gtk:AtkDocument:load-complete ->  document:load-complete
+ * Gtk:AtkDocument:load-stopped  ->  document:load-stopped
+ * Gtk:AtkDocument:reload        ->  document:reload
+ */
+static gboolean
+document_event_listener (GSignalInvocationHint *signal_hint,
+                         guint n_param_values,
+                         const GValue *param_values,
+                         gpointer data)
+{
+  AtkObject *accessible;
+  GSignalQuery signal_query;
+  const gchar *name, *s;
+
+  g_signal_query (signal_hint->signal_id, &signal_query);
+  name = signal_query.signal_name;
+
+  accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
+  s = atk_object_get_name (accessible);
+  emit(accessible, ITF_EVENT_DOCUMENT, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
+
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Signal handler for  "Gtk:AtkComponent:bounds-changed". Converts
+ * this to an AT-SPI event - "object:bounds-changed".
+ */
+static gboolean
+bounds_event_listener (GSignalInvocationHint *signal_hint,
+                       guint n_param_values,
+                       const GValue *param_values,
+                       gpointer data)
+{
+  AtkObject *accessible;
+  AtkRectangle *atk_rect;
+  GSignalQuery signal_query;
+  const gchar *name, *s;
+
+  g_signal_query (signal_hint->signal_id, &signal_query);
+  name = signal_query.signal_name;
+
+  accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
+
+  if (G_VALUE_HOLDS_BOXED (param_values + 1))
+    atk_rect = g_value_get_boxed (param_values + 1);
+
+  emit_rect(accessible, ITF_EVENT_OBJECT, name, "", atk_rect);
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* 
+ * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and 
+ * converts it to the AT-SPI signal - 'object:active-descendant-changed'.
+ *
+ */
+static gboolean
+active_descendant_event_listener (GSignalInvocationHint *signal_hint,
+                                 guint n_param_values,
+                                 const GValue *param_values,
+                                 gpointer data)
+{
+  AtkObject *accessible;
+  AtkObject *child;
+  GSignalQuery signal_query;
+  const gchar *name, *minor;
+  gchar *s;
+  gint detail1;
+
+  g_signal_query (signal_hint->signal_id, &signal_query);
+  name = signal_query.signal_name;
+
+  accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
+  child = ATK_OBJECT(g_value_get_pointer (&param_values[1]));
+  g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
+  minor = g_quark_to_string (signal_hint->detail);
+
+  detail1 = atk_object_get_index_in_parent (child);
+  s = atk_dbus_get_path(child);
+
+  emit(accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, s);
+  g_free(s);
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* 
+ * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and
+ * converts it to the AT-SPI signal - 'object:link-selected'
+ *
+ */
+static gboolean
+link_selected_event_listener (GSignalInvocationHint *signal_hint,
+                             guint n_param_values,
+                             const GValue *param_values,
+                             gpointer data)
+{
+  AtkObject *accessible;
+  GSignalQuery signal_query;
+  const gchar *name, *minor;
+  gint detail1;
+
+  g_signal_query (signal_hint->signal_id, &signal_query);
+  name = signal_query.signal_name;
+
+  accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
+  minor = g_quark_to_string (signal_hint->detail);
+
+  if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
+        detail1 = g_value_get_int (&param_values[1]);
+
+  emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* 
+ * Handles the ATK signal 'Gtk:AtkText:text-changed' and
+ * converts it to the AT-SPI signal - 'object:text-changed'
+ *
+ */
+static gboolean
+text_changed_event_listener (GSignalInvocationHint *signal_hint,
+                            guint n_param_values,
+                            const GValue *param_values,
+                            gpointer data)
+{
+  AtkObject *accessible;
+  GSignalQuery signal_query;
+  const gchar *name, *minor;
+  gchar *selected;
+  gint detail1, detail2;
+
+  g_signal_query (signal_hint->signal_id, &signal_query);
+  name = signal_query.signal_name;
+
+  accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
+  minor = g_quark_to_string (signal_hint->detail);
+
+  if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
+        detail1 = g_value_get_int (&param_values[1]);
+
+  if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
+        detail2 = g_value_get_int (&param_values[2]);
+
+  selected = atk_text_get_text (ATK_TEXT (accessible), detail1, detail1+detail2);
+
+  emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, selected);
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* 
+ * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and
+ * converts it to the AT-SPI signal - 'object:text-selection-changed'
+ *
+ */
+static gboolean
+text_selection_changed_event_listener (GSignalInvocationHint *signal_hint,
+                                      guint n_param_values,
+                                      const GValue *param_values,
+                                      gpointer data)
+{
+  AtkObject *accessible;
+  GSignalQuery signal_query;
+  const gchar *name, *minor;
+  gint detail1, detail2;
+
+  g_signal_query (signal_hint->signal_id, &signal_query);
+  name = signal_query.signal_name;
+
+  accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
+  minor = g_quark_to_string (signal_hint->detail);
+
+  if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
+        detail1 = g_value_get_int (&param_values[1]);
+
+  if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
+        detail2 = g_value_get_int (&param_values[2]);
+
+  emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, "");
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Generic signal converter and forwarder.
+ *
+ * Klass (Interface) org.freedesktop.atspi.Event.Object
+ * Major is the signal name.
+ * Minor is NULL.
+ * detail1 is 0.
+ * detail2 is 0.
+ * any_data is NULL.
+ */
+static gboolean
+generic_event_listener (GSignalInvocationHint *signal_hint,
+                       guint n_param_values,
+                       const GValue *param_values,
+                       gpointer data)
+{
+  AtkObject *accessible;
+  GSignalQuery signal_query;
+  const gchar *name;
+
+  g_signal_query (signal_hint->signal_id, &signal_query);
+  name = signal_query.signal_name;
+
+  accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
+  emit(accessible, ITF_EVENT_OBJECT, name, "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Registers the provided function as a handler for the given signal name
+ * and stores the signal id returned so that the function may be
+ * de-registered later.
+ */
+static void
+add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
+{
+  guint id;
+
+  id = atk_add_global_event_listener (listener, signal_name);
+  g_array_append_val (listener_ids, id);
+}
+
+/*
+ * Initialization for the signal handlers.
+ *
+ * Registers all required signal handlers.
+ */
+void
+spi_atk_register_event_listeners (void)
+{
+  /*
+   * Kludge to make sure the Atk interface types are registered, otherwise
+   * the AtkText signal handlers below won't get registered
+   */
+  GObject   *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
+  AtkObject *bo = atk_no_op_object_new (ao);
+
+  g_object_unref (G_OBJECT (bo));
+  g_object_unref (ao);
+
+  /* Register for focus event notifications, and register app with central registry  */
+  listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
+
+  atk_bridge_focus_tracker_id = atk_add_focus_tracker (focus_tracker);
+
+  add_signal_listener (tree_update_listener,          "Gtk:AtkObject:property-change");
+  add_signal_listener (tree_update_children_listener, "Gtk:AtkObject:children-changed");
+
+  add_signal_listener (property_event_listener,               "Gtk:AtkObject:property-change");
+  add_signal_listener (window_event_listener,                 "window:create");
+  add_signal_listener (window_event_listener,                 "window:destroy");
+  add_signal_listener (window_event_listener,                 "window:minimize");
+  add_signal_listener (window_event_listener,                 "window:maximize");
+  add_signal_listener (window_event_listener,                 "window:restore");
+  add_signal_listener (window_event_listener,                 "window:activate");
+  add_signal_listener (window_event_listener,                 "window:deactivate");
+  add_signal_listener (document_event_listener,               "Gtk:AtkDocument:load-complete");
+  add_signal_listener (document_event_listener,               "Gtk:AtkDocument:reload");
+  add_signal_listener (document_event_listener,               "Gtk:AtkDocument:load-stopped");
+  add_signal_listener (state_event_listener,                  "Gtk:AtkObject:state-change");
+  add_signal_listener (active_descendant_event_listener,      "Gtk:AtkObject:active-descendant-changed");
+  add_signal_listener (bounds_event_listener,                 "Gtk:AtkComponent:bounds-changed");
+  add_signal_listener (text_selection_changed_event_listener, "Gtk:AtkText:text-selection-changed");
+  add_signal_listener (text_changed_event_listener,           "Gtk:AtkText:text-changed");
+  add_signal_listener (link_selected_event_listener,          "Gtk:AtkHypertext:link-selected");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkObject:visible-data-changed");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkSelection:selection-changed");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkText:text-caret-moved");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkTable:row-inserted");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkTable:row-reordered");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkTable:row-deleted");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkTable:column-inserted");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkTable:column-reordered");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkTable:column-deleted");
+  add_signal_listener (generic_event_listener,                "Gtk:AtkTable:model-changed");
+
+  /*
+   * May add the following listeners to implement preemptive key listening for GTK+
+   *
+   * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
+   * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
+   */
+  atk_bridge_key_event_listener_id = atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL);
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* 
+ * De-registers all ATK signal handlers.
+ */
+void
+spi_atk_deregister_event_listeners (void)
+{
+  gint i;
+  GArray *ids = listener_ids;
+  listener_ids = NULL;
+
+  if (atk_bridge_focus_tracker_id)
+        atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
+  
+  for (i = 0; ids && i < ids->len; i++)
+    {
+          atk_remove_global_event_listener (g_array_index (ids, guint, i));
+    }
+  
+  if (atk_bridge_key_event_listener_id)
+          atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * TODO This function seems out of place here.
+ *
+ * Emits fake deactivate signals on all top-level windows.
+ * Used when shutting down AT-SPI, ensuring that all
+ * windows have been removed on the client side.
+ */
+void
+spi_atk_tidy_windows (void)
+{
+  AtkObject *root;
+  gint n_children;
+  gint i;
+
+  root = atk_get_root ();
+  n_children = atk_object_get_n_accessible_children (root);
+  for (i = 0; i < n_children; i++)
+    {
+      AtkObject *child;
+      AtkStateSet *stateset;
+      const gchar *name;
+     
+      child = atk_object_ref_accessible_child (root, i);
+      stateset = atk_object_ref_state_set (child);
+      
+      name = atk_object_get_name (child);
+      if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
+        {
+         emit(child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
+        }
+      g_object_unref (stateset);
+
+      emit(child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
+      g_object_unref (child);
+    }
+}
+
+/*END------------------------------------------------------------------------*/
index 1e6bb7b..42b2096 100644 (file)
@@ -27,7 +27,7 @@
 static AtkHyperlink *
 get_hyperlink (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_HYPERLINK (obj);
@@ -36,7 +36,7 @@ get_hyperlink (DBusMessage * message)
 static AtkHyperlink *
 get_hyperlink_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_HYPERLINK(obj))
     return NULL;
   return ATK_HYPERLINK (obj);
index d8f8330..8b5d4d6 100644 (file)
@@ -27,7 +27,7 @@
 static AtkHypertext *
 get_hypertext (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_HYPERTEXT (obj);
@@ -36,7 +36,7 @@ get_hypertext (DBusMessage * message)
 static AtkHypertext *
 get_hypertext_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_HYPERTEXT(obj))
     return NULL;
   return ATK_HYPERTEXT (obj);
index 90d4f4a..f754749 100644 (file)
@@ -27,7 +27,7 @@
 static AtkImage *
 get_image (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_IMAGE (obj);
@@ -36,7 +36,7 @@ get_image (DBusMessage * message)
 static AtkImage *
 get_image_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_IMAGE(obj))
     return NULL;
   return ATK_IMAGE (obj);
index aa84a4d..d7c9b2f 100644 (file)
@@ -27,7 +27,7 @@
 static AtkSelection *
 get_selection (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_SELECTION (obj);
@@ -36,7 +36,7 @@ get_selection (DBusMessage * message)
 static AtkSelection *
 get_selection_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || ~ATK_IS_SELECTION(obj))
     return NULL;
   return ATK_SELECTION (obj);
index 83de354..868ae1f 100644 (file)
@@ -27,7 +27,7 @@
 static AtkTable *
 get_table (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_TABLE (obj);
@@ -36,7 +36,7 @@ get_table (DBusMessage * message)
 static AtkTable *
 get_table_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_TABLE(obj))
     return NULL;
   return ATK_TABLE (obj);
index ca51ce1..c22b0ea 100644 (file)
@@ -28,7 +28,7 @@
 static AtkText *
 get_text (DBusMessage * message)
 {
-  AtkObject *obj = spi_dbus_get_object (dbus_message_get_path (message));
+  AtkObject *obj = atk_dbus_get_object (dbus_message_get_path (message));
   if (!obj)
     return NULL;
   return ATK_TEXT (obj);
@@ -37,7 +37,7 @@ get_text (DBusMessage * message)
 static AtkText *
 get_text_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_TEXT(obj))
     return NULL;
   return ATK_TEXT (obj);
index 64258ba..7efca21 100644 (file)
 #include <droute/introspect-loader.h>
 
 #include "accessible.h"
+#include "bridge.h"
 
-#define get_object(message) spi_dbus_get_object(dbus_message_get_path(message))
+extern SpiAppData *this_app;
+static gboolean update_pending = FALSE;
 
-static dbus_bool_t
-append_update (DBusMessageIter * iter_array, AtkObject * obj,
-                            dbus_bool_t include_children, DRouteData * data)
+/*---------------------------------------------------------------------------*/
+
+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 strucuture is (ooaoassus).
+ * This is used in the updateTree signal and the getTree method
+ * of the org.freedesktop.atspi.Tree interface.
+ */
+static void
+append_accessible(gpointer ref, gpointer obj_data, gpointer iter)
 {
+  AtkObject *obj;
+  DBusMessageIter *iter_array;
   DBusMessageIter iter_struct, iter_sub_array;
-  char *path = NULL;
-  char *path_parent;
+  DRouteData *data;
+
   const char *name, *desc;
   int i;
   dbus_uint32_t role;
-  AtkObject *parent = NULL;
-
-  gint childcount;
   GSList *l;
 
-  g_assert(data != NULL);
+  obj = ATK_OBJECT(obj_data);
+  iter_array = (DBusMessageIter *) iter;
+  data = &(this_app->droute);
 
-  parent = atk_object_get_parent(obj);
-  if (parent == NULL)
-    {
-      path_parent = g_strdup("/");
-    }
-  else
+  dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct);
     {
-      path_parent = spi_dbus_get_path (parent);
-    }
+      AtkObject *parent;
+      gchar *path, *path_parent;
 
-  dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL,
-                                   &iter_struct);
-  path = spi_dbus_get_path (obj);
-  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
-  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);
-  childcount = atk_object_get_n_accessible_children (obj);
-  for (i = 0; i < childcount; i++)
-    {
-      AtkObject *child = atk_object_ref_accessible_child (obj, i);
-      char *child_path = spi_dbus_get_path (child);
-      if (child_path)
-       {
-         dbus_message_iter_append_basic (&iter_sub_array,
-                                         DBUS_TYPE_OBJECT_PATH, &child_path);
-         g_free (child_path);
-       }
-      if (child)
-       g_object_unref (child);
-    }
-  if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
-    goto oom;
-  dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
-                                   &iter_sub_array);
-  if (data) for (l = data->interfaces; l; l = g_slist_next (l))
-    {
-      DRouteInterface *iface_def = (DRouteInterface *) l->data;
-      void *datum = NULL;
-      if (iface_def->get_datum)
-       {
-         datum = (*iface_def->get_datum) (path, data->user_data);
-         if (!datum)
-           continue;
-       }
-      dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_STRING,
-                                     &iface_def->name);
-      if (iface_def->free_datum)
-       (*iface_def->free_datum) (datum);
-    }
-  if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
-    goto oom;
-  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);
-  if (!dbus_message_iter_close_container (iter_array, &iter_struct))
-    goto oom;
-  if (!include_children) childcount = 0;
-  for (i = 0; i < childcount; i++)
-    {
-      AtkObject *child = atk_object_ref_accessible_child (obj, i);
-      dbus_bool_t result;
-      if (!child)
-       continue;
-      result = append_update (iter_array, child, TRUE, data);
-      g_object_unref (child);
-      if (!result)
-       goto oom;
-    }
-  g_free (path);
-  return TRUE;
-oom:
-  if (path) g_free(path);
-  return FALSE;
+      path = atk_dbus_get_path_from_ref(GPOINTER_TO_INT(ref));
+      dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
+      g_free(path);
+
+      parent = atk_object_get_parent(obj);
+      if (parent == NULL)
+        path_parent = g_strdup("/");
+      else
+        path_parent = atk_dbus_get_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_get_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);
+           }
+        }
+      dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
+
+      dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_sub_array);
+        {
+          for (l = data->interfaces; l; l = g_slist_next (l))
+            {
+              DRouteInterface *iface_def = (DRouteInterface *) l->data;
+              void *datum = NULL;
+
+              if (iface_def->get_datum)
+               {
+                 datum = (*iface_def->get_datum) (path, data->user_data);
+                 if (!datum)
+                   continue;
+               }
+              dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_STRING, &iface_def->name);
+              if (iface_def->free_datum)
+               (*iface_def->free_datum) (datum);
+            }
+        }
+      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);
+    }      
+  dbus_message_iter_close_container (iter_array, &iter_struct);
 }
 
-dbus_bool_t
-spi_dbus_append_tree (DBusMessage * message, AtkObject * obj,
-                     DRouteData * data)
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Used to marshal array of objects to remove.
+ * Marshalls an object path onto the iter provided.
+ */
+static void
+append_accessible_path(gpointer ref_data, gpointer null, gpointer data)
 {
-  DBusMessageIter iter, iter_array;
-  dbus_bool_t result;
+  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);
+}
+
+/*---------------------------------------------------------------------------*/
+
+static gboolean
+send_cache_update(gpointer d)
+{
+  DBusMessage *message;
+  DBusMessageIter iter;
+  DBusMessageIter iter_array;
+  DRouteData *data;
 
-  g_assert(data != NULL);
+  data = &(this_app->droute);
+
+  message = dbus_message_new_signal ("/org/freedesktop/atspi/tree", SPI_DBUS_INTERFACE_TREE, "updateTree");
 
   dbus_message_iter_init_append (message, &iter);
-  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)",
-                                   &iter_array);
-  result = append_update (&iter_array, obj, TRUE, data);
-  if (result)
-    result = dbus_message_iter_close_container (&iter, &iter_array);
-  return result;
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)", &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);
+
+  dbus_connection_send(data->bus, message, NULL);
+  update_pending = FALSE;
+
+  return FALSE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+void
+atk_tree_cache_needs_update(void)
+{
+  if (!update_pending)
+    {
+      g_idle_add(send_cache_update, NULL);
+      update_pending = TRUE;
+    }
 }
 
+/*---------------------------------------------------------------------------*/
+
 static DBusMessage *
-impl_getRoot (DBusConnection * bus, DBusMessage * message, void *user_data)
+impl_getRoot (DBusConnection *bus, DBusMessage *message, void *user_data)
 {
   AtkObject *root = atk_get_root();
   char *path;
   DBusMessage *reply;
 
-  if (root) path = spi_dbus_get_path(root);
+  if (root) path = atk_dbus_get_path(root);
   if (!root || !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);
+  dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
   g_free (path);
   return reply;
 }
 
+/*---------------------------------------------------------------------------*/
+
 static DBusMessage *
-impl_getTree (DBusConnection * bus, DBusMessage * message, void *user_data)
+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);
+  if (!root) 
+     return spi_dbus_general_error(message);
   reply = dbus_message_new_method_return (message);
-  spi_dbus_append_tree (reply, root, (DRouteData *) user_data);
+
+  dbus_message_iter_init_append (reply, &iter);
+  dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ooaoassus)", &iter_array);
+  atk_dbus_foreach_registered(append_accessible, &iter_array);
+  dbus_message_iter_close_container(&iter, &iter_array);
   return reply;
 }
 
+/*---------------------------------------------------------------------------*/
+
 static DBusMessage *
 impl_introspect (DBusConnection *bus, DBusMessage *message, void *user_data)
 {
@@ -206,6 +263,8 @@ impl_introspect (DBusConnection *bus, DBusMessage *message, void *user_data)
   return reply;
 }
 
+/*---------------------------------------------------------------------------*/
+
 static DBusHandlerResult
 message_handler (DBusConnection *bus, DBusMessage *message, void *user_data)
 {
@@ -246,6 +305,8 @@ message_handler (DBusConnection *bus, DBusMessage *message, void *user_data)
   return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+/*---------------------------------------------------------------------------*/
+
 static DBusObjectPathVTable tree_vtable =
 {
   NULL,
@@ -253,6 +314,8 @@ static DBusObjectPathVTable tree_vtable =
   NULL, NULL, NULL, NULL
 };
 
+/*---------------------------------------------------------------------------*/
+
 void
 spi_register_tree_object(DBusConnection *bus,
                         DRouteData *data,
@@ -263,152 +326,4 @@ spi_register_tree_object(DBusConnection *bus,
   g_assert(mem == TRUE);
 }
 
-static GHashTable *cache_list;
-
-#define UPDATE_NEW     1
-#define UPDATE_REFRESH 2
-#define UPDATE_REMOVE 3
-
-static int update_pending = 0;
-static gint update_pending_id;
-
-typedef struct
-{
-  DBusMessageIter iter;
-  DRouteData *droute;
-  gboolean removing;
-} CacheIterData;
-
-static void handle_cache_item(char *path, guint action, CacheIterData *d)
-{
-  AtkObject *obj;
-
-  switch (action)
-  {
-  case UPDATE_NEW:
-  case UPDATE_REFRESH:
-  default:
-    if (d->removing) return;
-    obj = spi_dbus_get_object(path);
-//printf("update %s\n", path);
-    append_update(&d->iter, obj, FALSE, d->droute);
-    break;
-  case UPDATE_REMOVE:
-//printf("remove: %s\n", path);
-    if (!d->removing) return;
-    dbus_message_iter_append_basic(&d->iter, DBUS_TYPE_OBJECT_PATH, &path);
-    break;
-  }
-  g_hash_table_remove(cache_list, path);
-}
-
-gboolean
-spi_dbus_update_cache(DRouteData *data)
-{
-  DBusMessage *message;
-  DBusMessageIter iter;
-  CacheIterData d;
-  static gboolean in_update_cache = FALSE;
-
-  if (in_update_cache) return TRUE;
-  g_assert(data != NULL);
-
-  if (update_pending == 0) return FALSE;
-//printf("Sending cache\n");
-  message = dbus_message_new_signal ("/org/freedesktop/atspi/tree", SPI_DBUS_INTERFACE_TREE, "updateTree");
-  if (!message) goto done;
-  dbus_message_iter_init_append (message, &iter);
-  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)",
-                                   &d.iter);
-  d.droute = data;
-  d.removing = FALSE;
-  in_update_cache = TRUE;
-  do
-  {
-    /* This loop is needed because appending an item may cause new children
-     * to be registered and consequently added to the hash, so they, too,
-     * will need to be sent with the update */
-    update_pending = 0;
-    g_hash_table_foreach(cache_list, (GHFunc)handle_cache_item, &d);
-  } while (update_pending);
-  dbus_message_iter_close_container(&iter, &d.iter);
-  dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &d.iter);
-  d.removing = TRUE;
-  g_hash_table_foreach(cache_list, (GHFunc)handle_cache_item, &d);
-  in_update_cache = FALSE;
-  dbus_message_iter_close_container(&iter, &d.iter);
-  dbus_connection_send(data->bus, message, NULL);
-done:
-  return FALSE;
-}
-
-void spi_dbus_notify_change(AtkObject *obj, gboolean new, DRouteData *data)
-{
-  guint action = (new? UPDATE_NEW: UPDATE_REFRESH);
-  char *path = spi_dbus_get_path(obj);
-
-  if (!cache_list)
-  {
-    cache_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
-    if (!cache_list)
-    {
-      g_free(path);
-      return;
-    }
-  }
-  if (g_hash_table_lookup(cache_list, path))
-  {
-    g_free(path);
-    return;
-  }
-//printf("change: %s\n", path);
-  g_hash_table_insert(cache_list, path, (gpointer)action);
-  if (update_pending != 2 && data)
-  {
-    update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
-    update_pending = 2;
-  }
-  else if (!update_pending) update_pending = 1;
-}
-
-void spi_dbus_notify_remove(AtkObject *obj, DRouteData *data)
-{
-  guint action = UPDATE_REMOVE;
-  guint cur_action;
-  gchar *path = spi_dbus_get_path(obj);
-
-//printf("notify remove: %s\n", path);
-  if (!cache_list)
-  {
-    g_free(path);
-    return;
-  }
-  cur_action = (guint)g_hash_table_lookup(cache_list, path);
-  if (cur_action == UPDATE_NEW)
-  {
-    /* No one knew that this object ever existed, so just remove it */
-//printf("Removing object from send queue\n");
-    g_hash_table_remove(cache_list, path);
-    g_free(path);
-  }
-  else
-  {
-    g_hash_table_insert(cache_list, path, (gpointer)action);
-    if (update_pending != 2 && data)
-    {
-      update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
-      update_pending = 2;
-    }
-    else if (!update_pending) update_pending = 1;
-  }
-}
-
-gboolean spi_dbus_object_is_known(AtkObject *obj)
-{
-  guint cur_action;
-  char *path = spi_dbus_get_path(obj);
-  if (!path) return FALSE;
-  cur_action = (guint)g_hash_table_lookup(cache_list, path);
-  g_free(path);
-  return (cur_action != UPDATE_NEW);
-}
+/*END------------------------------------------------------------------------*/
index 5ace1a2..33b67c7 100644 (file)
@@ -28,7 +28,7 @@
 static AtkValue *
 get_value_from_path (const char *path, void *user_data)
 {
-  AtkObject *obj = spi_dbus_get_object (path);
+  AtkObject *obj = atk_dbus_get_object (path);
   if (!obj || !ATK_IS_VALUE(obj))
     return NULL;
   return ATK_VALUE (obj);
index 9113c76..56ab0f6 100644 (file)
@@ -69,10 +69,9 @@ class _BaseCache(object):
        def __contains__(self, key):
                return key in self._objects
 
-       def _update_handler(self, updates):
-               update, remove = updates
-               self._remove_objects(update)
-               self._update_objects(remove)
+       def _update_handler(self, update, remove):
+               self._remove_objects(remove)
+               self._update_objects(update)
 
        def _update_objects(self, objects):
                for data in objects:
index 185080d..c7ef618 100644 (file)
@@ -19,10 +19,38 @@ from accessible import BoundingBox
 __all__ = [
                "Event",
                "EventType",
+               "event_type_to_signal_reciever",
          ]
 
 #------------------------------------------------------------------------------
 
+_interface_to_klass = {
+               "org.freedesktop.atspi.Event.Object":"object",
+               "org.freedesktop.atspi.Event.Window":"window",
+               "org.freedesktop.atspi.Event.Mouse":"mouse",
+               "org.freedesktop.atspi.Event.Terminal":"terminal",
+               "org.freedesktop.atspi.Event.Document":"document",
+               "org.freedesktop.atspi.Event.Focus":"focus",
+               }
+
+_klass_to_interface = {
+               "object":"org.freedesktop.atspi.Event.Object",
+               "window":"org.freedesktop.atspi.Event.Window",
+               "mouse":"org.freedesktop.atspi.Event.Mouse",
+               "terminal":"org.freedesktop.atspi.Event.Terminal",
+               "document":"org.freedesktop.atspi.Event.Document",
+               "focus":"org.freedesktop.atspi.Event.Focus",
+               }
+
+#------------------------------------------------------------------------------
+
+class _ELessList(list):
+       def __getitem__(self, index):
+               try:
+                       return list.__getitem__(self, index)
+               except IndexError:
+                       return None
+
 class EventType(str):
        """
        Wraps the AT-SPI event type string so its components can be accessed 
@@ -44,7 +72,8 @@ class EventType(str):
        @cvar format: Names of the event string components
        @type format: 4-tuple of string
        """
-       format = ('klass', 'major', 'minor', 'detail')
+
+       _SEPARATOR = ':'
 
        def __init__(self, name):
                """             
@@ -56,20 +85,62 @@ class EventType(str):
                @type name: string
                @raise AttributeError: When the given event name is not a valid string 
                """
-               # get rid of any leading and trailing '_' separators
-               self.value = name.strip('_')
-               self.name = self.value # Backward compatability
-               self.klass = None
-               self.major = None
-               self.minor = None
-               self.detail = None
-               
-               # split type according to delimiters
-               split = self.value.split('_', 3)
-               # loop over all the components
-               for i in xrange(len(split)):
-                       # store values of attributes in this object
-                       setattr(self, self.format[i], split[i])
+               stripped = name.strip(self._SEPARATOR)
+               separated = stripped.split(self._SEPARATOR, 3) 
+               self._separated = _ELessList(separated)
+
+               self.klass = self._separated[0]
+               self.major = self._separated[1]
+               self.minor = self._separated[2]
+               self.detail = self._separated[3]
+
+               self._name = ":".join(separated)
+
+       def is_subtype(self, event_type):
+               """
+               Determines if the passed event type is a subtype
+               of this event.
+               """
+               if event_type.klass and event_type.klass !=  self.klass:
+                       return False
+               else:
+                       if event_type.major and event_type.major != self.major:
+                               return False
+                       else:
+                               if event_type.minor and event_type.minor != self.minor:
+                                       return False
+               return True
+
+       @property
+       def name(self):
+               return self._name
+
+       @property
+       def value(self):
+               return self._name
+
+#------------------------------------------------------------------------------
+
+def event_type_to_signal_reciever(bus, cache, event_handler, event_type):
+       kwargs = {
+                       'sender_keyword':'sender',
+                       'interface_keyword':'interface',
+                       'member_keyword':'member',
+                       'path_keyword':'path',
+                }
+       if event_type.klass:
+               kwargs['dbus_interface'] = _klass_to_interface[event_type.klass]
+       if event_type.major:
+               kwargs['signal_name'] = event_type.major
+       if event_type.minor:
+               kwargs['arg0'] = event_type.minor
+
+       def handler_wrapper(minor, detail1, detail2, any_data, 
+                           sender=None, interface=None, member=None, path=None):
+               event = Event(cache, path, sender, interface, member, (minor, detail1, detail2, any_data))
+               return event_handler(event)
+
+       return bus.add_signal_receiver(handler_wrapper, **kwargs) 
 
 #------------------------------------------------------------------------------
 
@@ -99,7 +170,7 @@ class Event(object):
        @ivar source: Source of the event
        @type source: Accessibility.Accessible
        """
-       def __init__(self, cache, source_path, source_application, name, event):
+       def __init__(self, cache, source_path, source_application, interface, name, event):
                """
                Extracts information from the provided event. If the event is a "normal" 
                event, pulls the detail1, detail2, any_data, and source values out of the
@@ -115,11 +186,16 @@ class Event(object):
                self._cache = cache
                self._source_path = source_path
                self._source_application = source_application
+
                self._source = None
                self._application = None
 
-               self._detail = event[0]
-               self.type = EventType(name + '_' + self._detail)
+               self._klass = _interface_to_klass[interface]
+               # The replace is neccessary as '-' not legal as signal name
+               # so translated on the server side.
+               self._major = name.replace('_', '-')
+               self._minor = event[0]
+               self.type = EventType(':'.join([self._klass, self._major, self._minor]))
                self.detail1 = event[1]
                self.detail2 = event[2]
 
index f938ba1..164fa49 100644 (file)
 #authors: Peter Parente, Mark Doffman
 
 import dbus
+import gobject
 
 from dbus.mainloop.glib import DBusGMainLoop
 DBusGMainLoop(set_as_default=True)
 
 from test import TestApplicationCache
 from desktop import Desktop
+from event import EventType, event_type_to_signal_reciever
 
 class Registry(object):
        """
@@ -66,6 +68,8 @@ class Registry(object):
                if app_name:
                        self._app_name = app_name
                        self._cache = TestApplicationCache(self._bus, app_name)
+
+               self._event_listeners = {}
                
        def __call__(self):
                """
@@ -89,6 +93,10 @@ class Registry(object):
                """
                self._loop = gobject.MainLoop()
                self._loop.run()
+               try:
+                       loop.run()
+               except KeyboardInterrupt:
+                       pass
 
        def stop(self, *args):
                """Quits the main loop."""
@@ -114,3 +122,71 @@ class Registry(object):
                @rtype: Accessibility.Desktop
                """
                return Desktop(self._cache)
+
+       def registerEventListener(self, client, *names):
+               """
+               Registers a new client callback for the given event names. Supports 
+               registration for all subevents if only partial event name is specified.
+               Do not include a trailing colon.
+               
+               For example, 'object' will register for all object events, 
+               'object:property-change' will register for all property change events,
+               and 'object:property-change:accessible-parent' will register only for the
+               parent property change event.
+               
+               Registered clients will not be automatically removed when the client dies.
+               To ensure the client is properly garbage collected, call 
+               L{deregisterEventListener}.
+
+               @param client: Callable to be invoked when the event occurs
+               @type client: callable
+               @param names: List of full or partial event names
+               @type names: list of string
+               """
+               try:
+                       registered = self._event_listeners[client]
+               except KeyError:
+                       registered = []
+                       self._event_listeners[client] = registered
+
+               for name in names:
+                       new_type = EventType(name)
+                       registered.append((new_type.name,
+                                          event_type_to_signal_reciever(self._bus, self._cache, client, new_type)))
+
+       def deregisterEventListener(self, client, *names):
+               """
+               Unregisters an existing client callback for the given event names. Supports 
+               unregistration for all subevents if only partial event name is specified.
+               Do not include a trailing colon.
+               
+               This method must be called to ensure a client registered by
+               L{registerEventListener} is properly garbage collected.
+
+               @param client: Client callback to remove
+               @type client: callable
+               @param names: List of full or partial event names
+               @type names: list of string
+               @return: Were event names specified for which the given client was not
+                       registered?
+               @rtype: boolean
+               """
+               try:
+                       registered = self._event_listeners[client]
+               except KeyError:
+                       # Presumably if were trying to deregister a client with
+                       # no names then the return type is always true.
+                       return True
+               
+               for name in names:
+                       remove_type = EventType(name)
+
+                       for i in range(0, len(registered)):
+                               (type_name, signal_match) = registered[i]
+                               registered_type = EventType(type_name)
+                               if remove_type.is_subtype(registered_type):
+                                       signal_match.remove()
+                                       del(registered[i])
+
+               if registered == []:
+                       del(self._event_listeners[client])
index aaaf981..d8644c9 100644 (file)
@@ -47,6 +47,7 @@
        <signal name="reparent"><arg direction="out" type="suuv" tp:type="Event"/></signal>
        <signal name="desktop-create"><arg direction="out" type="suuv" tp:type="Event"/></signal>
        <signal name="desktop-destroy"><arg direction="out" type="suuv" tp:type="Event"/></signal>
+       <signal name="destroy"><arg direction="out" type="suuv" tp:type="Event"/></signal>
        <signal name="activate"><arg direction="out" type="suuv" tp:type="Event"/></signal>
        <signal name="deactivate"><arg direction="out" type="suuv" tp:type="Event"/></signal>
        <signal name="raise"><arg direction="out" type="suuv" tp:type="Event"/></signal>