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