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 \
#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));
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;
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));
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;
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;
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;
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,
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);
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
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);
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);
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 ());
}
#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)
{
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);
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;
-}
--- /dev/null
+/*
+ * 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__ */
#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,
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);
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
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)
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;
}
if (success)
{
spi_atk_register_event_listeners ();
- spi_atk_bridge_init_event_type_consts ();
}
else
{
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 ();
}
/*
}
-
static const char *
spi_atk_bridge_get_registry (void)
{
}
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
gnome_accessibility_module_shutdown (void)
{
int i;
- GArray *ids = listener_ids;
if (!atk_bridge_initialized)
{
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;
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)
{
--- /dev/null
+/*
+ * 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;
+};
#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
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);
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);
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);
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
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
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);
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);
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);
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);
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);
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);
--- /dev/null
+/*
+ * 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 (¶m_values[0]);
+ values = (AtkPropertyValues*) g_value_get_pointer (¶m_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 (¶m_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 (¶m_values[0]);
+ values = (AtkPropertyValues*) g_value_get_pointer (¶m_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 (¶m_values[0]));
+ pname = g_strdup (g_value_get_string (¶m_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 (¶m_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(¶m_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(¶m_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(¶m_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(¶m_values[0]));
+ child = ATK_OBJECT(g_value_get_pointer (¶m_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(¶m_values[0]));
+ minor = g_quark_to_string (signal_hint->detail);
+
+ if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT)
+ detail1 = g_value_get_int (¶m_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(¶m_values[0]));
+ minor = g_quark_to_string (signal_hint->detail);
+
+ if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT)
+ detail1 = g_value_get_int (¶m_values[1]);
+
+ if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT)
+ detail2 = g_value_get_int (¶m_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(¶m_values[0]));
+ minor = g_quark_to_string (signal_hint->detail);
+
+ if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT)
+ detail1 = g_value_get_int (¶m_values[1]);
+
+ if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT)
+ detail2 = g_value_get_int (¶m_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(¶m_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------------------------------------------------------------------------*/
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
#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)
{
return reply;
}
+/*---------------------------------------------------------------------------*/
+
static DBusHandlerResult
message_handler (DBusConnection *bus, DBusMessage *message, void *user_data)
{
return DBUS_HANDLER_RESULT_HANDLED;
}
+/*---------------------------------------------------------------------------*/
+
static DBusObjectPathVTable tree_vtable =
{
NULL,
NULL, NULL, NULL, NULL
};
+/*---------------------------------------------------------------------------*/
+
void
spi_register_tree_object(DBusConnection *bus,
DRouteData *data,
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------------------------------------------------------------------------*/
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);
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:
__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
@cvar format: Names of the event string components
@type format: 4-tuple of string
"""
- format = ('klass', 'major', 'minor', 'detail')
+
+ _SEPARATOR = ':'
def __init__(self, name):
"""
@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)
#------------------------------------------------------------------------------
@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
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]
#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):
"""
if app_name:
self._app_name = app_name
self._cache = TestApplicationCache(self._bus, app_name)
+
+ self._event_listeners = {}
def __call__(self):
"""
"""
self._loop = gobject.MainLoop()
self._loop.run()
+ try:
+ loop.run()
+ except KeyboardInterrupt:
+ pass
def stop(self, *args):
"""Quits the main loop."""
@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])
<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>