X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=atspi%2Fatspi-misc.c;h=9e8595ad5e273573a285f3701bb30a24cf6be760;hb=refs%2Ftags%2Fupstream%2F2.34.0;hp=548cfdeb844697f90b0c7a0c93ffe5124889fd98;hpb=8846499e0aaae728fb460a8b27f6645dac55e91d;p=platform%2Fupstream%2Fat-spi2-core.git diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index 548cfde..9e8595a 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -7,19 +7,19 @@ * Copyright 2010, 2011 Novell, Inc. * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public + * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 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. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * You should have received a copy of the GNU Lesser 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /* @@ -29,7 +29,9 @@ */ #include "atspi-private.h" +#ifdef HAVE_X11 #include "X11/Xlib.h" +#endif #include "atspi-gmain.h" #include #include @@ -40,8 +42,10 @@ static DBusConnection *bus = NULL; static GHashTable *live_refs = NULL; static gint method_call_timeout = 800; static gint app_startup_time = 15000; +static gboolean allow_sync = TRUE; GMainLoop *atspi_main_loop; +GMainContext *atspi_main_context; gboolean atspi_no_cache; const char *atspi_path_dec = ATSPI_DBUS_PATH_DEC; @@ -64,6 +68,7 @@ const char *atspi_interface_image = ATSPI_DBUS_INTERFACE_IMAGE; const char *atspi_interface_registry = ATSPI_DBUS_INTERFACE_REGISTRY; const char *atspi_interface_selection = ATSPI_DBUS_INTERFACE_SELECTION; const char *atspi_interface_table = ATSPI_DBUS_INTERFACE_TABLE; +const char *atspi_interface_table_cell = ATSPI_DBUS_INTERFACE_TABLE_CELL; const char *atspi_interface_text = ATSPI_DBUS_INTERFACE_TEXT; const char *atspi_interface_cache = ATSPI_DBUS_INTERFACE_CACHE; const char *atspi_interface_value = ATSPI_DBUS_INTERFACE_VALUE; @@ -83,6 +88,7 @@ static const char *interfaces[] = "org.a11y.atspi.LoginHelper", ATSPI_DBUS_INTERFACE_SELECTION, ATSPI_DBUS_INTERFACE_TABLE, + ATSPI_DBUS_INTERFACE_TABLE_CELL, ATSPI_DBUS_INTERFACE_TEXT, ATSPI_DBUS_INTERFACE_VALUE, NULL @@ -101,8 +107,8 @@ _atspi_get_iface_num (const char *iface) return -1; } -static GHashTable * -get_live_refs (void) +GHashTable * +_atspi_get_live_refs (void) { if (!live_refs) { @@ -118,16 +124,19 @@ _atspi_bus () if (!bus) atspi_init (); if (!bus) - g_error ("AT-SPI: COuldn't connect to accessibility bus. Is at-spi-bus-launcher running?"); + g_error ("AT-SPI: Couldn't connect to accessibility bus. Is at-spi-bus-launcher running?"); return bus; } #define APP_IS_REGISTRY(app) (!strcmp (app->bus_name, atspi_bus_registry)) +static AtspiAccessible *desktop; + static void cleanup () { GHashTable *refs; + gint i; refs = live_refs; live_refs = NULL; @@ -142,6 +151,22 @@ cleanup () dbus_connection_unref (bus); bus = NULL; } + + if (!desktop) + return; + + /* TODO: Do we need this code, or should we just dispose the desktop? */ + for (i = desktop->children->len - 1; i >= 0; i--) + { + AtspiAccessible *child = g_ptr_array_index (desktop->children, i); + if (child->parent.app) + g_object_run_dispose (G_OBJECT (child->parent.app)); + g_object_run_dispose (G_OBJECT (child)); + } + + g_object_run_dispose (G_OBJECT (desktop->parent.app)); + g_object_unref (desktop); + desktop = NULL; } static gboolean atspi_inited = FALSE; @@ -174,10 +199,12 @@ handle_get_bus_address (DBusPendingCall *pending, void *user_data) dbus_connection_unref (app->bus); } app->bus = bus; + atspi_dbus_connection_setup_with_g_main(bus, g_main_context_default()); } else { - g_warning ("Unable to open bus connection: %s", error.message); + if (!strcmp (error.name, DBUS_ERROR_FILE_NOT_FOUND)) + g_warning ("AT-SPI: Unable to open bus connection: %s", error.message); dbus_error_free (&error); } } @@ -192,9 +219,11 @@ handle_get_bus_address (DBusPendingCall *pending, void *user_data) "/org/a11y/atspi/cache", atspi_interface_cache, "GetItems"); - dbus_connection_send_with_reply (app->bus, message, &new_pending, 2000); - dbus_pending_call_set_notify (new_pending, handle_get_items, app, NULL); + dbus_connection_send_with_reply (app->bus, message, &new_pending, 2000); dbus_message_unref (message); + if (!new_pending) + return; + dbus_pending_call_set_notify (new_pending, handle_get_items, app, NULL); } static AtspiApplication * @@ -224,9 +253,14 @@ get_application (const char *bus_name) message = dbus_message_new_method_call (bus_name, atspi_path_root, atspi_interface_application, "GetApplicationBusAddress"); - dbus_connection_send_with_reply (app->bus, message, &pending, 2000); - dbus_pending_call_set_notify (pending, handle_get_bus_address, app, NULL); + dbus_connection_send_with_reply (app->bus, message, &pending, 2000); dbus_message_unref (message); + if (!pending) + { + g_hash_table_remove (app_hash, bus_name_dup); + return NULL; + } + dbus_pending_call_set_notify (pending, handle_get_bus_address, app, NULL); return app; } @@ -247,6 +281,7 @@ ref_accessible (const char *app_name, const char *path) { app->root = _atspi_accessible_new (app, atspi_path_root); app->root->accessible_parent = atspi_get_desktop (0); + g_ptr_array_add (app->root->accessible_parent->children, g_object_ref (app->root)); } return g_object_ref (app->root); } @@ -259,8 +294,7 @@ ref_accessible (const char *app_name, const char *path) a = _atspi_accessible_new (app, path); if (!a) return NULL; - g_hash_table_insert (app->hash, g_strdup (a->parent.path), a); - g_object_ref (a); /* for the hash */ + g_hash_table_insert (app->hash, g_strdup (a->parent.path), g_object_ref (a)); return a; } @@ -340,7 +374,7 @@ handle_name_owner_changed (DBusConnection *bus, DBusMessage *message, void *user DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) { - return; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } if (!strcmp (name, "org.a11y.atspi.Registry")) @@ -354,6 +388,12 @@ handle_name_owner_changed (DBusConnection *bus, DBusMessage *message, void *user else if (!new[0]) registry_lost = TRUE; } + else if (app_hash) + { + AtspiApplication *app = g_hash_table_lookup (app_hash, old); + if (app && !strcmp (app->bus_name, old)) + g_object_run_dispose (G_OBJECT (app)); + } return DBUS_HANDLER_RESULT_HANDLED; } @@ -361,66 +401,12 @@ static gboolean add_app_to_desktop (AtspiAccessible *a, const char *bus_name) { AtspiAccessible *obj = ref_accessible (bus_name, atspi_path_root); - if (obj) - { - a->children = g_list_append (a->children, obj); - return TRUE; - } - else - { - g_warning ("AT-SPI: Error calling getRoot for %s", bus_name); - } - return FALSE; -} - -static void -send_children_changed (AtspiAccessible *parent, AtspiAccessible *child, gboolean add) -{ - AtspiEvent e; - - memset (&e, 0, sizeof (e)); - e.type = (add? "object:children-changed:add": "object:children-changed:remove"); - e.source = parent; - e.detail1 = g_list_index (parent->children, child); - e.detail2 = 0; - _atspi_send_event (&e); -} - -static void -unref_object_and_descendants (AtspiAccessible *obj) -{ - GList *l; - - for (l = obj->children; l; l = l->next) - { - unref_object_and_descendants (l->data); - } + /* The app will be added to the desktop as a side-effect of calling + * ref_accessible */ g_object_unref (obj); + return (obj != NULL); } -static gboolean -remove_app_from_desktop (AtspiAccessible *a, const char *bus_name) -{ - GList *l; - AtspiAccessible *child; - - for (l = a->children; l; l = l->next) - { - child = l->data; - if (!strcmp (bus_name, child->parent.app->bus_name)) break; - } - if (!l) - { - return FALSE; - } - send_children_changed (a, child, FALSE); - a->children = g_list_remove (a->children, child); - unref_object_and_descendants (child); - return TRUE; -} - -static AtspiAccessible *desktop; - void get_reference_from_iter (DBusMessageIter *iter, const char **app_name, const char **path) { @@ -441,6 +427,8 @@ add_accessible_from_iter (DBusMessageIter *iter) AtspiAccessible *accessible; const char *name, *description; dbus_uint32_t role; + gboolean children_cached = FALSE; + dbus_int32_t count, index; dbus_message_iter_recurse (iter, &iter_struct); @@ -459,19 +447,40 @@ add_accessible_from_iter (DBusMessageIter *iter) g_object_unref (accessible->accessible_parent); accessible->accessible_parent = ref_accessible (app_name, path); - /* Get children */ - while (accessible->children) + if (dbus_message_iter_get_arg_type (&iter_struct) == 'i') { - g_object_unref (accessible->children->data); - accessible->children = g_list_remove (accessible->children, accessible->children->data); + /* Get index in parent */ + dbus_message_iter_get_basic (&iter_struct, &index); + if (index >= 0 && accessible->accessible_parent) + { + if (index >= accessible->accessible_parent->children->len) + g_ptr_array_set_size (accessible->accessible_parent->children, index + 1); + g_ptr_array_index (accessible->accessible_parent->children, index) = g_object_ref (accessible); + } + + /* get child count */ + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &count); + if (count >= 0) + { + g_ptr_array_set_size (accessible->children, count); + children_cached = TRUE; + } } - dbus_message_iter_recurse (&iter_struct, &iter_array); - while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) + else if (dbus_message_iter_get_arg_type (&iter_struct) == 'a') { - AtspiAccessible *child; - get_reference_from_iter (&iter_array, &app_name, &path); - child = ref_accessible (app_name, path); - accessible->children = g_list_append (accessible->children, child); + /* It's the old API with a list of children */ + /* TODO: Perhaps remove this code eventually */ + dbus_message_iter_recurse (&iter_struct, &iter_array); + while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) + { + AtspiAccessible *child; + get_reference_from_iter (&iter_array, &app_name, &path); + child = ref_accessible (app_name, path); + g_ptr_array_remove (accessible->children, child); + g_ptr_array_add (accessible->children, child); + } + children_cached = TRUE; } /* interfaces */ @@ -504,7 +513,8 @@ add_accessible_from_iter (DBusMessageIter *iter) _atspi_accessible_add_cache (accessible, ATSPI_CACHE_NAME | ATSPI_CACHE_ROLE | ATSPI_CACHE_PARENT | ATSPI_CACHE_DESCRIPTION); if (!atspi_state_set_contains (accessible->states, - ATSPI_STATE_MANAGES_DESCENDANTS)) + ATSPI_STATE_MANAGES_DESCENDANTS) && + children_cached) _atspi_accessible_add_cache (accessible, ATSPI_CACHE_CHILDREN); /* This is a bit of a hack since the cache holds a ref, so we don't need @@ -522,9 +532,17 @@ handle_get_items (DBusPendingCall *pending, void *user_data) { const char *sender = dbus_message_get_sender (reply); const char *error = NULL; - dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &error, - DBUS_TYPE_INVALID); - g_warning ("AT-SPI: Error in GetItems, sender=%s, error=%s", sender, error); + const char *error_name = dbus_message_get_error_name (reply); + if (!strcmp (error_name, DBUS_ERROR_SERVICE_UNKNOWN) + || !strcmp (error_name, DBUS_ERROR_NO_REPLY)) + { + } + else + { + dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &error, + DBUS_TYPE_INVALID); + g_warning ("AT-SPI: Error in GetItems, sender=%s, error=%s", sender, error); + } dbus_message_unref (reply); dbus_pending_call_unref (pending); return; @@ -560,8 +578,9 @@ ref_accessible_desktop (AtspiApplication *app) { return NULL; } - g_hash_table_insert (app->hash, desktop->parent.path, desktop); - g_object_ref (desktop); /* for the hash */ + g_hash_table_insert (app->hash, g_strdup (desktop->parent.path), + g_object_ref (desktop)); + app->root = g_object_ref (desktop); desktop->name = g_strdup ("main"); message = dbus_message_new_method_call (atspi_bus_registry, atspi_path_root, @@ -590,24 +609,28 @@ ref_accessible_desktop (AtspiApplication *app) get_reference_from_iter (&iter_array, &app_name, &path); add_app_to_desktop (desktop, app_name); } - dbus_message_unref (reply); /* Record the alternate name as an alias for org.a11y.atspi.Registry */ bus_name_dup = g_strdup (dbus_message_get_sender (reply)); if (bus_name_dup) g_hash_table_insert (app_hash, bus_name_dup, app); - return desktop; + dbus_message_unref (reply); + + return g_object_ref (desktop); } AtspiAccessible * _atspi_ref_accessible (const char *app, const char *path) { AtspiApplication *a = get_application (app); - if (!a) return NULL; + if (!a) + return NULL; if ( APP_IS_REGISTRY(a)) { - return a->root = ref_accessible_desktop (a); + if (!a->root) + g_object_unref (ref_accessible_desktop (a)); /* sets a->root */ + return g_object_ref (a->root); } return ref_accessible (app, path); } @@ -678,18 +701,19 @@ _atspi_dbus_return_hyperlink_from_iter (DBusMessageIter *iter) return ref_hyperlink (app_name, path); } -const char *cache_signal_type = "((so)(so)(so)a(so)assusau)"; +const char *cache_signal_type = "((so)(so)(so)iiassusau)"; +const char *old_cache_signal_type = "((so)(so)(so)a(so)assusau)"; static DBusHandlerResult handle_add_accessible (DBusConnection *bus, DBusMessage *message, void *user_data) { DBusMessageIter iter; - const char *sender = dbus_message_get_sender (message); + const char *signature = dbus_message_get_signature (message); - if (strcmp (dbus_message_get_signature (message), cache_signal_type) != 0) + if (strcmp (signature, cache_signal_type) != 0 && + strcmp (signature, old_cache_signal_type) != 0) { - g_warning ("AT-SPI: AddAccessible with unknown signature %s\n", - dbus_message_get_signature (message)); + g_warning ("AT-SPI: AddAccessible with unknown signature %s\n", signature); return DBUS_HANDLER_RESULT_HANDLED; } @@ -705,7 +729,7 @@ typedef struct void *data; } BusDataClosure; -static guint process_deferred_messages_id = -1; +static GSource *process_deferred_messages_source = NULL; static void process_deferred_message (BusDataClosure *closure) @@ -739,8 +763,8 @@ process_deferred_message (BusDataClosure *closure) static GQueue *deferred_messages = NULL; -gboolean -_atspi_process_deferred_messages (gpointer data) +static gboolean +process_deferred_messages (void) { static int in_process_deferred_messages = 0; BusDataClosure *closure; @@ -748,20 +772,27 @@ _atspi_process_deferred_messages (gpointer data) if (in_process_deferred_messages) return TRUE; in_process_deferred_messages = 1; - while (closure = g_queue_pop_head (deferred_messages)) + while ((closure = g_queue_pop_head (deferred_messages))) { process_deferred_message (closure); dbus_message_unref (closure->message); dbus_connection_unref (closure->bus); g_free (closure); } - /* If data is NULL, assume that we were called from GLib */ - if (!data) - process_deferred_messages_id = -1; in_process_deferred_messages = 0; return FALSE; } +static gboolean +process_deferred_messages_callback (gpointer data) +{ + if (process_deferred_messages ()) + return G_SOURCE_CONTINUE; + + process_deferred_messages_source = NULL; + return G_SOURCE_REMOVE; +} + static DBusHandlerResult defer_message (DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -773,8 +804,15 @@ defer_message (DBusConnection *connection, DBusMessage *message, void *user_data g_queue_push_tail (deferred_messages, closure); - if (process_deferred_messages_id == -1) - process_deferred_messages_id = g_idle_add (_atspi_process_deferred_messages, NULL); + if (process_deferred_messages_source == NULL) + { + process_deferred_messages_source = g_idle_source_new (); + g_source_set_callback (process_deferred_messages_source, + process_deferred_messages_callback, NULL, NULL); + g_source_attach (process_deferred_messages_source, atspi_main_context); + g_source_unref (process_deferred_messages_source); + } + return DBUS_HANDLER_RESULT_HANDLED; } @@ -809,17 +847,6 @@ atspi_dbus_filter (DBusConnection *bus, DBusMessage *message, void *data) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -static const char *signal_interfaces[] = -{ - "org.a11y.atspi.Event.Object", - "org.a11y.atspi.Event.Window", - "org.a11y.atspi.Event.Mouse", - "org.a11y.atspi.Event.Terminal", - "org.a11y.atspi.Event.Document", - "org.a11y.atspi.Event.Focus", - NULL -}; - /* * Returns a 'canonicalized' value for DISPLAY, * with the screen number stripped off if present. @@ -836,7 +863,7 @@ spi_display_name (void) { display_env = g_getenv ("DISPLAY"); if (!display_env || !display_env[0]) - canonical_display_name = g_strdup (":0"); + return NULL; else { gchar *display_p, *screen_p; @@ -862,7 +889,7 @@ spi_display_name (void) * * Connects to the accessibility registry and initializes the SPI. * - * Returns: 0 on success, otherwise an integer error code. + * Returns: 0 on success, 1 if already initialized, or an integer error code. **/ int atspi_init (void) @@ -877,9 +904,7 @@ atspi_init (void) atspi_inited = TRUE; - g_type_init (); - - get_live_refs(); + _atspi_get_live_refs(); bus = atspi_get_a11y_bus (); if (!bus) @@ -917,6 +942,19 @@ atspi_init (void) } /** + * atspi_is_initialized: + * + * Indicates whether AT-SPI has been initialized. + * + * Returns: %True if initialized; %False otherwise. + */ +gboolean +atspi_is_initialized () +{ + return atspi_inited; +} + +/** * atspi_event_main: * * Starts/enters the main event loop for the AT-SPI services. @@ -984,8 +1022,6 @@ static GSList *hung_processes; static void remove_hung_process (DBusPendingCall *pending, void *data) { - gchar *bus_name = data; - hung_processes = g_slist_remove (hung_processes, data); g_free (data); dbus_pending_call_unref (pending); @@ -1077,6 +1113,12 @@ _atspi_dbus_call (gpointer obj, const char *interface, const char *method, GErro if (!check_app (aobj->app, error)) return FALSE; + if (!allow_sync) + { + _atspi_set_error_no_sync (error); + return FALSE; + } + va_start (args, type); dbus_error_init (&err); set_timeout (aobj->app); @@ -1085,7 +1127,7 @@ _atspi_dbus_call (gpointer obj, const char *interface, const char *method, GErro type, args); va_end (args); check_for_hang (NULL, &err, aobj->app->bus, aobj->app->bus_name); - _atspi_process_deferred_messages ((gpointer)TRUE); + process_deferred_messages (); if (dbus_error_is_set (&err)) { g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "%s", err.message); @@ -1142,12 +1184,23 @@ out: va_end (args); if (msg) dbus_message_unref (msg); - _atspi_process_deferred_messages ((gpointer)TRUE); + process_deferred_messages (); if (dbus_error_is_set (&err)) { /* TODO: Set gerror */ dbus_error_free (&err); } + + if (reply && dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR) + { + const char *err_str = NULL; + dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &err_str, DBUS_TYPE_INVALID); + if (err_str) + g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err_str); + dbus_message_unref (reply); + return NULL; + } + return reply; } @@ -1167,6 +1220,12 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, if (!check_app (aobj->app, error)) return FALSE; + if (!allow_sync) + { + _atspi_set_error_no_sync (error); + return FALSE; + } + message = dbus_message_new_method_call (aobj->app->bus_name, aobj->path, "org.freedesktop.DBus.Properties", @@ -1182,7 +1241,7 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, reply = dbind_send_and_allow_reentry (aobj->app->bus, message, &err); check_for_hang (reply, &err, aobj->app->bus, aobj->app->bus_name); dbus_message_unref (message); - _atspi_process_deferred_messages ((gpointer)TRUE); + process_deferred_messages (); if (!reply) { // TODO: throw exception @@ -1201,7 +1260,7 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, dbus_message_iter_init (reply, &iter); if (dbus_message_iter_get_arg_type (&iter) != 'v') { - g_warning ("AT-SPI: expected a variant when fetching %s from interface %s; got %s\n", name, interface, dbus_message_get_signature (reply)); + g_warning ("atspi_dbus_get_property: expected a variant when fetching %s from interface %s; got %s\n", name, interface, dbus_message_get_signature (reply)); goto done; } dbus_message_iter_recurse (&iter, &iter_variant); @@ -1245,7 +1304,7 @@ _atspi_dbus_send_with_reply_and_block (DBusMessage *message, GError **error) dbus_error_init (&err); set_timeout (app); reply = dbind_send_and_allow_reentry (bus, message, &err); - _atspi_process_deferred_messages ((gpointer)TRUE); + process_deferred_messages (); dbus_message_unref (message); if (dbus_error_is_set (&err)) { @@ -1339,8 +1398,16 @@ void _atspi_dbus_set_interfaces (AtspiAccessible *accessible, DBusMessageIter *iter) { DBusMessageIter iter_array; + char *iter_sig = dbus_message_iter_get_signature (iter); accessible->interfaces = 0; + if (strcmp (iter_sig, "as") != 0) + { + g_warning ("_atspi_dbus_set_interfaces: Passed iterator with invalid signature %s", dbus_message_iter_get_signature (iter)); + dbus_free (iter_sig); + return; + } + dbus_free (iter_sig); dbus_message_iter_recurse (iter, &iter_array); while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) { @@ -1396,6 +1463,7 @@ _atspi_error_quark (void) /* * Gets the IOR from the XDisplay. */ +#ifdef HAVE_X11 static char * get_accessibility_bus_address_x11 (void) { @@ -1410,11 +1478,12 @@ get_accessibility_bus_address_x11 (void) char *display_name; display_name = spi_display_name (); - if (display_name != NULL) - { - bridge_display = XOpenDisplay (display_name); - g_free (display_name); - } + if (!display_name) + return NULL; + + bridge_display = XOpenDisplay (display_name); + g_free (display_name); + if (!bridge_display) { g_warning ("Could not open X display"); @@ -1430,10 +1499,11 @@ get_accessibility_bus_address_x11 (void) &nitems, &leftover, &data_x11); XCloseDisplay (bridge_display); - data = g_strdup (data_x11); + data = g_strdup ((gchar *)data_x11); XFree (data_x11); return data; } +#endif static char * get_accessibility_bus_address_dbus (void) @@ -1462,10 +1532,10 @@ get_accessibility_bus_address_dbus (void) if (!reply) { - g_warning ("Error retrieving accessibility bus address: %s: %s", + g_warning ("AT-SPI: Error retrieving accessibility bus address: %s: %s", error.name, error.message); dbus_error_free (&error); - return NULL; + goto out; } { @@ -1477,12 +1547,14 @@ get_accessibility_bus_address_dbus (void) DBUS_TYPE_INVALID)) { dbus_message_unref (reply); - return NULL; + goto out; } address = g_strdup (tmp_address); dbus_message_unref (reply); } +out: + dbus_connection_unref (session_bus); return address; } @@ -1499,21 +1571,31 @@ a11y_bus_free (void *data) } } +/** + * atspi_get_a11y_bus: (skip) + */ DBusConnection * atspi_get_a11y_bus (void) { - DBusConnection *bus = NULL; DBusError error; - char *address; + char *address = NULL; + const char *address_env = NULL; if (a11y_bus && dbus_connection_get_is_connected (a11y_bus)) return a11y_bus; if (a11y_dbus_slot == -1) if (!dbus_connection_allocate_data_slot (&a11y_dbus_slot)) - g_warning ("at-spi: Unable to allocate D-Bus slot"); - - address = get_accessibility_bus_address_x11 (); + g_warning ("AT-SPI: Unable to allocate D-Bus slot"); + + address_env = g_getenv ("AT_SPI_BUS_ADDRESS"); + if (address_env != NULL && *address_env != 0) + address = g_strdup (address_env); +#ifdef HAVE_X11 + if (!address && g_getenv ("DISPLAY") != NULL && + g_getenv ("WAYLAND_DISPLAY") == NULL) + address = get_accessibility_bus_address_x11 (); +#endif if (!address) address = get_accessibility_bus_address_dbus (); if (!address) @@ -1525,7 +1607,8 @@ atspi_get_a11y_bus (void) if (!a11y_bus) { - g_warning ("Couldn't connect to accessibility bus: %s", error.message); + if (!g_getenv("SSH_CONNECTION")) + g_warning ("Couldn't connect to accessibility bus: %s", error.message); dbus_error_free (&error); return NULL; } @@ -1549,23 +1632,24 @@ atspi_get_a11y_bus (void) } /** - * Set the timeout used for method calls. If this is not set explicitly, - * a default of 0.8 ms is used. - * Note that at-spi2-registryd currently uses a timeout of 3 seconds when - * sending a keyboard event notification. This means that, if an AT makes - * a call in response to the keyboard notification and the application - * being called does not respond before the timeout is reached, - * at-spi2-registryd will time out on the keyboard event notification and - * pass the key onto the application (ie, reply to indicate that the key - * was not consumed), so this may make it undesirable to set a timeout - * larger than 3 seconds. + * atspi_set_timeout: + * @val: The timeout value, in milliseconds, or -1 to disable the timeout. + * @startup_time: The amount of time, in milliseconds, to allow to pass + * before enforcing timeouts on an application. Can be used to prevent + * timeout exceptions if an application is likely to block for an extended + * period of time on initialization. -1 can be passed to disable this + * behavior. * - * @val: The timeout value, in milliseconds, or -1 to disable the timeout. - * @startup_time: The amount of time, in milliseconds, to allow to pass - * before enforcing timeouts on an application. Can be used to prevent - * timeout exceptions if an application is likely to block for an extended - * period of time on initialization. -1 can be passed to disable this - * behavior. + * Set the timeout used for method calls. If this is not set explicitly, + * a default of 0.8 ms is used. + * Note that at-spi2-registryd currently uses a timeout of 3 seconds when + * sending a keyboard event notification. This means that, if an AT makes + * a call in response to the keyboard notification and the application + * being called does not respond before the timeout is reached, + * at-spi2-registryd will time out on the keyboard event notification and + * pass the key onto the application (ie, reply to indicate that the key + * was not consumed), so this may make it undesirable to set a timeout + * larger than 3 seconds. * * By default, the normal timeout is set to 800 ms, and the application startup * timeout is set to 15 seconds. @@ -1576,3 +1660,242 @@ atspi_set_timeout (gint val, gint startup_time) method_call_timeout = val; app_startup_time = startup_time; } + +/** + * atspi_set_main_context: + * @cnx: The #GMainContext to use. + * + * Sets the main loop context that AT-SPI should assume is in use when + * setting an idle callback. + * This function should be called by application-side implementors (ie, + * at-spi2-atk) when it is desirable to re-enter the main loop. + */ +void +atspi_set_main_context (GMainContext *cnx) +{ + if (atspi_main_context == cnx) + return; + if (process_deferred_messages_source != NULL) + { + g_source_destroy (process_deferred_messages_source); + process_deferred_messages_source = g_idle_source_new (); + g_source_set_callback (process_deferred_messages_source, + process_deferred_messages_callback, NULL, NULL); + g_source_attach (process_deferred_messages_source, cnx); + g_source_unref (process_deferred_messages_source); + } + atspi_main_context = cnx; + atspi_dbus_connection_setup_with_g_main (atspi_get_a11y_bus (), cnx); + + if (desktop) + { + gint i; + for (i = desktop->children->len - 1; i >= 0; i--) + { + AtspiAccessible *child = g_ptr_array_index (desktop->children, i); + if (child->parent.app && child->parent.app->bus) + atspi_dbus_connection_setup_with_g_main (child->parent.app->bus, cnx); + } + } +} + +#ifdef DEBUG_REF_COUNTS +static void +print_disposed (gpointer key, gpointer value, gpointer data) +{ + AtspiAccessible *accessible = key; + if (accessible->parent.app) + return; + g_print ("disposed: %s %d\n", accessible->name, accessible->role); +} + +void +debug_disposed () +{ + g_hash_table_foreach (live_refs, print_disposed, NULL); +} +#endif + +gchar * +_atspi_name_compat (gchar *name) +{ + gchar *p = name; + + while (*p) + { + if (*p == '-') + *p = ' '; + p++; + } + return name; +} + +/** + * atspi_role_get_name: + * @role: an #AtspiRole object to query. + * + * Gets a localizable string that indicates the name of an #AtspiRole. + * DEPRECATED. + * + * Returns: a localizable string name for an #AtspiRole enumerated type. + **/ +gchar * +atspi_role_get_name (AtspiRole role) +{ + gchar *retval = NULL; + GTypeClass *type_class; + GEnumValue *value; + + type_class = g_type_class_ref (ATSPI_TYPE_ROLE); + g_return_val_if_fail (G_IS_ENUM_CLASS (type_class), NULL); + + value = g_enum_get_value (G_ENUM_CLASS (type_class), role); + + if (value) + { + retval = g_strdup (value->value_nick); + } + + g_type_class_unref (type_class); + + if (retval) + return _atspi_name_compat (retval); + + return NULL; +} + +GHashTable * +_atspi_dbus_update_cache_from_dict (AtspiAccessible *accessible, DBusMessageIter *iter) +{ + GHashTable *cache = _atspi_accessible_ref_cache (accessible); + DBusMessageIter iter_dict, iter_dict_entry, iter_struct, iter_variant; + + dbus_message_iter_recurse (iter, &iter_dict); + while (dbus_message_iter_get_arg_type (&iter_dict) != DBUS_TYPE_INVALID) + { + const char *key; + GValue *val = NULL; + dbus_message_iter_recurse (&iter_dict, &iter_dict_entry); + dbus_message_iter_get_basic (&iter_dict_entry, &key); + dbus_message_iter_next (&iter_dict_entry); + dbus_message_iter_recurse (&iter_dict_entry, &iter_variant); + if (!strcmp (key, "interfaces")) + { + _atspi_dbus_set_interfaces (accessible, &iter_variant); + } + else if (!strcmp (key, "Attributes")) + { + char *iter_sig = dbus_message_iter_get_signature (&iter_variant); + val = g_new0 (GValue, 1);; + g_value_init (val, G_TYPE_HASH_TABLE); + if (strcmp (iter_sig, "a{ss}") != 0) + { + dbus_free (iter_sig); + break; + } + dbus_free (iter_sig); + g_value_take_boxed (val, _atspi_dbus_hash_from_iter (&iter_variant)); + } + else if (!strcmp (key, "Component.ScreenExtents")) + { + dbus_int32_t d_int; + AtspiRect extents; + char *iter_sig = dbus_message_iter_get_signature (&iter_variant); + val = g_new0 (GValue, 1);; + g_value_init (val, ATSPI_TYPE_RECT); + if (strcmp (iter_sig, "(iiii)") != 0) + { + dbus_free (iter_sig); + break; + } + dbus_free (iter_sig); + dbus_message_iter_recurse (&iter_variant, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &d_int); + extents.x = d_int; + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &d_int); + extents.y = d_int; + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &d_int); + extents.width = d_int; + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &d_int); + extents.height = d_int; + g_value_set_boxed (val, &extents); + } + if (val) + g_hash_table_insert (cache, g_strdup (key), val); + dbus_message_iter_next (&iter_dict); + } + + return cache; +} + +gboolean +_atspi_get_allow_sync () +{ + return allow_sync; +} + +gboolean +_atspi_set_allow_sync (gboolean val) +{ + gboolean ret = allow_sync; + + allow_sync = val; + return ret; +} + +void +_atspi_set_error_no_sync (GError **error) +{ + g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_SYNC_NOT_ALLOWED, + _("Attempted synchronous call where prohibited")); +} + +static const char *sr_introspection = "\n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +""; + +static DBusHandlerResult +screen_reader_filter (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + if (dbus_message_is_method_call (message, DBUS_INTERFACE_INTROSPECTABLE, + "Introspect")) + { + DBusMessage *reply = dbus_message_new_method_return (message); + dbus_message_append_args (reply, DBUS_TYPE_STRING, &sr_introspection, + DBUS_TYPE_INVALID); + dbus_connection_send (bus, reply, NULL); + dbus_message_unref (reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +gboolean +_atspi_prepare_screen_reader_interface () +{ + static gint initialized = 0; + DBusConnection *a11y_bus = _atspi_bus (); + + if (initialized) + return (initialized > 0); + + if (dbus_bus_request_name (a11y_bus, "org.a11y.Atspi.ScreenReader", 0, NULL) < 0) + { + initialized = -1; + return FALSE; + } + + initialized = 1; + dbus_connection_add_filter (a11y_bus, screen_reader_filter, NULL, NULL); + return TRUE; +}