X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git;a=blobdiff_plain;f=atk-adaptor%2Faccessible-register.c;h=7ca416fd2aef8494006e3873e3ee2d57d6157e88;hp=d4e1cbd59b65b962602bc428fc1a0f6fb8c184ee;hb=17b84f5bd418999040720f012f56abad66055f8a;hpb=1889d0741dab87cb6a46246a1ebd8267f72ad148 diff --git a/atk-adaptor/accessible-register.c b/atk-adaptor/accessible-register.c index d4e1cbd..7ca416f 100644 --- a/atk-adaptor/accessible-register.c +++ b/atk-adaptor/accessible-register.c @@ -3,7 +3,7 @@ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) * * Copyright 2008 Novell, Inc. - * Copyright 2008, 2009 Codethink Ltd. + * Copyright 2008, 2009, 2010 Codethink Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -28,10 +28,6 @@ #include "bridge.h" #include "accessible-register.h" -#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 @@ -39,7 +35,7 @@ * * 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 + * (SPI_ATK_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'. @@ -49,15 +45,101 @@ * 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 an Accessible object. */ -GHashTable *ref2ptr = NULL; /* Used for converting a D-Bus path (Reference) to the object pointer */ +#define SPI_ATK_PATH_PREFIX_LENGTH 27 +#define SPI_ATK_OBJECT_PATH_PREFIX "/org/a11y/atspi/accessible/" +#define SPI_ATK_OBJECT_PATH_ROOT "root" + +#define SPI_ATK_OBJECT_REFERENCE_TEMPLATE SPI_ATK_OBJECT_PATH_PREFIX "%d" + +#define SPI_DBUS_ID "spi-dbus-id" + +SpiRegister *spi_global_register = NULL; + +static const gchar * spi_register_root_path = SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT; + +enum +{ + OBJECT_REGISTERED, + OBJECT_DEREGISTERED, + LAST_SIGNAL +}; +static guint register_signals[LAST_SIGNAL] = { 0 }; + +/*---------------------------------------------------------------------------*/ + +static void +spi_register_finalize (GObject * object); + +/*---------------------------------------------------------------------------*/ + +G_DEFINE_TYPE (SpiRegister, spi_register, G_TYPE_OBJECT) + +static void spi_register_class_init (SpiRegisterClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + spi_register_parent_class = g_type_class_ref (G_TYPE_OBJECT); + + object_class->finalize = spi_register_finalize; + + register_signals [OBJECT_REGISTERED] = + g_signal_new ("object-registered", + SPI_REGISTER_TYPE, + G_SIGNAL_ACTION, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + register_signals [OBJECT_DEREGISTERED] = + g_signal_new ("object-deregistered", + SPI_REGISTER_TYPE, + G_SIGNAL_ACTION, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} + +static void +spi_register_init (SpiRegister * reg) +{ + reg->ref2ptr = g_hash_table_new (g_direct_hash, g_direct_equal); + reg->reference_counter = 0; +} + +static void +deregister_object (gpointer data, GObject * gobj) +{ + SpiRegister *reg = SPI_REGISTER (data); + + spi_register_deregister_object (reg, gobj, FALSE); +} + +static void +spi_register_remove_weak_ref (gpointer key, gpointer val, gpointer reg) +{ + g_object_weak_unref (val, deregister_object, reg); +} + +static void +spi_register_finalize (GObject * object) +{ + SpiRegister *reg = SPI_REGISTER (object); + + g_hash_table_foreach (reg->ref2ptr, spi_register_remove_weak_ref, reg); + g_hash_table_unref (reg->ref2ptr); -static guint counter = 1; + G_OBJECT_CLASS (spi_register_parent_class)->finalize (object); +} /*---------------------------------------------------------------------------*/ @@ -66,23 +148,28 @@ static guint counter = 1; * * This function provides an integer reference for a new * AtkObject. + * + * TODO: Make this reference a little more unique, this is shoddy. */ static guint -assign_reference(void) +assign_reference (SpiRegister * reg) { - counter++; + reg->reference_counter++; /* Reference of 0 not allowed as used as direct key in hash table */ - if (counter == 0) - counter++; + if (reg->reference_counter == 0) + reg->reference_counter++; + return reg->reference_counter; } +/*---------------------------------------------------------------------------*/ + /* * Returns the reference of the object, or 0 if it is not registered. */ static guint -object_to_ref (AtkObject *accessible) +object_to_ref (GObject * gobj) { - return GPOINTER_TO_INT(g_object_get_data (G_OBJECT (accessible), "dbus-id")); + return GPOINTER_TO_INT (g_object_get_data (gobj, SPI_DBUS_ID)); } /* @@ -91,305 +178,143 @@ object_to_ref (AtkObject *accessible) static gchar * ref_to_path (guint ref) { - return g_strdup_printf(ATK_BRIDGE_OBJECT_REFERENCE_TEMPLATE, ref); + return g_strdup_printf (SPI_ATK_OBJECT_REFERENCE_TEMPLATE, ref); } /*---------------------------------------------------------------------------*/ /* - * Called when a registered AtkObject is deleted. - * Removes the AtkObject from the reference lookup tables. + * Callback for when a registered AtkObject is destroyed. + * + * Removes the AtkObject from the reference lookup tables, meaning + * it is no longer exposed over D-Bus. */ -static void -deregister_accessible(gpointer data, GObject *accessible) +void +spi_register_deregister_object (SpiRegister *reg, GObject *gobj, gboolean unref) { guint ref; - gchar *path; - - g_assert(ATK_IS_OBJECT(accessible)); - - ref = atk_dbus_object_to_ref (ATK_OBJECT(accessible)); + ref = object_to_ref (gobj); if (ref != 0) { - g_hash_table_remove(ref2ptr, GINT_TO_POINTER(ref)); + g_signal_emit (reg, + register_signals [OBJECT_DEREGISTERED], + 0, + gobj); + if (unref) + g_object_weak_unref (gobj, deregister_object, reg); + g_hash_table_remove (reg->ref2ptr, GINT_TO_POINTER (ref)); + +#ifdef SPI_ATK_DEBUG + g_debug ("DEREG - %d", ref); +#endif } } -/* - * Called to register an AtkObject with AT-SPI and expose it over D-Bus. - */ static void -register_accessible (AtkObject *accessible) +register_object (SpiRegister * reg, GObject * gobj) { guint ref; - g_assert(ATK_IS_OBJECT(accessible)); - - ref = assign_reference(); + g_return_if_fail (G_IS_OBJECT (gobj)); - g_hash_table_insert (ref2ptr, GINT_TO_POINTER(ref), accessible); - g_object_set_data (G_OBJECT(accessible), "dbus-id", GINT_TO_POINTER(ref)); - g_object_weak_ref(G_OBJECT(accessible), deregister_accessible, NULL); -} - -/*---------------------------------------------------------------------------*/ + ref = assign_reference (reg); -typedef void (*ActionFunc) (AtkObject *); + g_hash_table_insert (reg->ref2ptr, GINT_TO_POINTER (ref), gobj); + g_object_set_data (G_OBJECT (gobj), SPI_DBUS_ID, GINT_TO_POINTER (ref)); + g_object_weak_ref (G_OBJECT (gobj), deregister_object, reg); -/* Return true if action should be performed */ -typedef gboolean (*FilterFunc) (AtkObject *); +#ifdef SPI_ATK_DEBUG + g_debug ("REG - %d", ref); +#endif -/* - * This function performs a depth first traversal of a tree of AtkObjects. - * - * It uses a FilterFunc to determine if a node needs to have an action - * performed on it. - * - * Nodes that are filtered out become leaves and no recursion is performed on - * them. - * - * Nodes where an action was performed are returned in a GList. - */ -void -traverse_atk_tree (AtkObject *accessible, - GList **visited, - ActionFunc action, - FilterFunc filter) -{ - AtkObject *current, *tmp; - GQueue *stack; - GList *uplist = NULL; - guint i, ref; - gboolean recurse; - - if (!filter (accessible)) - return; - - stack = g_queue_new (); - current = g_object_ref (accessible); - action (current) - *visited = g_list_prepend (*visited, current); - g_queue_push_head (stack, GINT_TO_POINTER (0)); - - /* - * The index held on the stack is the next child node - * that needs processing at the corresponding level in the tree. - */ - while (!g_queue_is_empty (stack)) - { - /* Find the next child node that needs processing */ - i = GPOINTER_TO_INT(g_queue_peek_head (stack)); - recurse = FALSE; - while (i < atk_object_get_n_accessible_children (current) && - recurse == FALSE) - { - tmp = atk_object_ref_accessible_child (current, i); - /* If filter function */ - if (filter (tmp)) - { - recurse = TRUE; - } - else - { - i++; - g_object_unref (G_OBJECT (tmp)); - } - } - - if (recurse) - { - /* Push onto stack */ - current = tmp; - action (current); - *visited = g_list_prepend (*visited, current); - g_queue_peek_head_link (stack)->data = GINT_TO_POINTER (i+1); - g_queue_push_head (stack, GINT_TO_POINTER (0)); - } - else - { - /* Pop from stack */ - tmp = current; - current = atk_object_get_parent (current); - g_object_unref (G_OBJECT (tmp)); - g_queue_pop_head (stack); - } - } - - return ref; -} - -/*---------------------------------------------------------------------------*/ - -/* - * Called when an already registered object is updated in such a - * way that client side cache needs to be updated. - */ -static void -update_accessible (AtkObject *accessible) -{ - guint ref = 0; - g_assert(ATK_IS_OBJECT(accessible)); - - ref = atk_dbus_object_to_ref (accessible); - if (ref) - { - spi_emit_cache_update (accessible, atk_adaptor_app_data->bus); - } + g_signal_emit (reg, register_signals [OBJECT_REGISTERED], 0, gobj); } /*---------------------------------------------------------------------------*/ -void -atk_dbus_foreach_registered(GHFunc func, gpointer data) -{ - g_hash_table_foreach(ref2ptr, func, data); -} - /* - * Used to lookup an AtkObject from its D-Bus path. + * Used to lookup an GObject from its D-Bus path. + * + * If the D-Bus path is not found this function returns NULL. */ -AtkObject * -atk_dbus_path_to_object (const char *path) +GObject * +spi_register_path_to_object (SpiRegister * reg, const char *path) { guint index; void *data; - g_assert (path); + g_return_val_if_fail (path, NULL); - if (strncmp(path, ATK_BRIDGE_OBJECT_PATH_PREFIX, ATK_BRIDGE_PATH_PREFIX_LENGTH) != 0) + if (strncmp (path, SPI_ATK_OBJECT_PATH_PREFIX, SPI_ATK_PATH_PREFIX_LENGTH) + != 0) return NULL; - path += ATK_BRIDGE_PATH_PREFIX_LENGTH; /* Skip over the prefix */ + path += SPI_ATK_PATH_PREFIX_LENGTH; /* Skip over the prefix */ - if (path[0] == '\0') - return atk_get_root(); - if (path[0] != '/') - return NULL; + /* Map the root path to the root object. */ + if (!g_strcmp0 (SPI_ATK_OBJECT_PATH_ROOT, path)) + return G_OBJECT (spi_global_app_data->root); - path++; index = atoi (path); - data = g_hash_table_lookup (ref2ptr, GINT_TO_POINTER(index)); + data = g_hash_table_lookup (reg->ref2ptr, GINT_TO_POINTER (index)); if (data) - return ATK_OBJECT (data); + return G_OBJECT(data); else return NULL; } -/* - * Used to lookup a D-Bus path from the AtkObject. - */ -gchar * -atk_dbus_object_to_path (AtkObject *accessible) +GObject * +spi_global_register_path_to_object (const char * path) { - guint ref; - g_assert(ATK_IS_OBJECT(accessible)); - - ref = atk_dbus_object_to_ref (accessible); - if (!ref) - return NULL; - else - return atk_dbus_ref_to_path (ref); + return spi_register_path_to_object (spi_global_register, path); } -/*---------------------------------------------------------------------------*/ - /* - * Events are not evaluated for non-registered accessibles. - * - * When a property change is made on a registered accessible - * the client side cache should be updated. - * - * When a parent is changed the subtree is de-registered - * if the parent is not attached to the root accessible. + * Used to lookup a D-Bus path from the GObject. + * + * If the objects is not already registered, + * this function will register it. */ -static gboolean -tree_update_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +gchar * +spi_register_object_to_path (SpiRegister * reg, GObject * gobj) { - 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]); + guint ref; - pname = values[0].property_name; + if (gobj == NULL) + return NULL; - if (!atk_dbus_object_to_ref (accessible)) - return TRUE; + /* Map the root object to the root path. */ + if ((void *)gobj == (void *)spi_global_app_data->root) + return g_strdup (spi_register_root_path); - if (strcmp (pname, "accessible-name") == 0 || - strcmp (pname, "accessible-description")) + ref = object_to_ref (gobj); + if (!ref) { - atk_dbus_update_accessible (accessible); + register_object (reg, gobj); + ref = object_to_ref (gobj); } - else if (strcmp (pname, "accessible-parent")) - { - guint ref; - ref = atk_dbus_object_to_ref; - if (!ref) - } - return TRUE; + if (!ref) + return NULL; + else + return ref_to_path (ref); } -/* - * Events are not evaluated for non registered accessibles. - * - * When the children of a registered accessible are changed - * the subtree, rooted at the child is registered. - */ -static gboolean -tree_update_children_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +guint +spi_register_object_to_ref (GObject * gobj) { - AtkObject *accessible; - const gchar *detail = NULL; - AtkObject *child; - gboolean child_needs_unref = FALSE; - - if (signal_hint->detail) - detail = g_quark_to_string (signal_hint->detail); - - accessible = g_value_get_object (¶m_values[0]); - if (!strcmp (detail, "add")) - { - gpointer child; - int index = g_value_get_uint (param_values + 1); - child = g_value_get_pointer (param_values + 2); - - if (ATK_IS_OBJECT (child)) - g_object_ref (child); - else - child = atk_object_ref_accessible_child (accessible, index); - - atk_dbus_register_subtree (child); - g_object_unref (child); - } - return TRUE; + return object_to_ref (gobj); } - + /* - * Initializes required global data. The update and removal lists - * and the reference lookup tables. - * - * Initializes all of the required D-Bus interfaces. + * Gets the path that indicates the accessible desktop object. + * This object is logically located on the registry daemon and not + * within any particular application. */ -void -atk_dbus_initialize (AtkObject *root) +gchar * +spi_register_root_object_path () { - if (!ref2ptr) - ref2ptr = g_hash_table_new(g_direct_hash, g_direct_equal); - - atk_dbus_register_accessible (root); - - atk_add_global_event_listener (tree_update_listener, "Gtk:AtkObject:property-change"); - atk_add_global_event_listener (tree_update_children_listener, "Gtk:AtkObject:children-changed"); + return g_strdup (SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT); } /*END------------------------------------------------------------------------*/ -