X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=atspi%2Fatspi-misc.c;h=02eab8cfd137df9d7f02e8c8eb8d7de20aca8df6;hb=9dc6aaf0f801e52d145783e15125148efec16bae;hp=9c2471a0e485e89b0ea848299c3224cd5475e830;hpb=e587b52e725d27443ee94c5d932441aaf302bc00;p=platform%2Fupstream%2Fat-spi2-core.git diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index 9c2471a..02eab8c 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -29,7 +29,9 @@ */ #include "atspi-private.h" +#ifdef HAVE_X11 #include "X11/Xlib.h" +#endif #include "atspi-gmain.h" #include #include @@ -38,6 +40,13 @@ static void handle_get_items (DBusPendingCall *pending, void *user_data); 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; const char *atspi_path_registry = ATSPI_DBUS_PATH_REGISTRY; @@ -59,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; @@ -78,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 @@ -96,10 +107,10 @@ _atspi_get_iface_num (const char *iface) return -1; } -static GHashTable * -get_live_refs (void) +GHashTable * +_atspi_get_live_refs (void) { - if (!live_refs) + if (!live_refs) { live_refs = g_hash_table_new (g_direct_hash, g_direct_equal); } @@ -112,15 +123,20 @@ _atspi_bus () { if (!bus) atspi_init (); + if (!bus) + 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; + GList *l; refs = live_refs; live_refs = NULL; @@ -128,6 +144,27 @@ cleanup () { g_hash_table_destroy (refs); } + + if (bus) + { + dbus_connection_close (bus); + dbus_connection_unref (bus); + bus = NULL; + } + + if (!desktop) + return; + for (l = desktop->children; l;) + { + GList *next = l->next; + AtspiAccessible *child = l->data; + g_object_run_dispose (G_OBJECT (child->parent.app)); + g_object_run_dispose (G_OBJECT (child)); + l = next; + } + g_object_run_dispose (G_OBJECT (desktop->parent.app)); + g_object_unref (desktop); + desktop = NULL; } static gboolean atspi_inited = FALSE; @@ -146,29 +183,43 @@ handle_get_bus_address (DBusPendingCall *pending, void *user_data) if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN) { if (dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) + DBUS_TYPE_INVALID) && address [0]) { DBusError error; + DBusConnection *bus; + dbus_error_init (&error); - DBusConnection *bus = dbus_connection_open (address, &error); + bus = dbus_connection_open_private (address, &error); if (bus) { if (app->bus) - dbus_connection_unref (app->bus); + { + dbus_connection_unref (app->bus); + } app->bus = bus; } + else + { + g_warning ("Unable to open bus connection: %s", error.message); + dbus_error_free (&error); + } } } dbus_message_unref (reply); dbus_pending_call_unref (pending); + if (!app->bus) + return; /* application has gone away / been disposed */ + message = dbus_message_new_method_call (app->bus_name, "/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 * @@ -177,7 +228,6 @@ get_application (const char *bus_name) AtspiApplication *app = NULL; char *bus_name_dup; DBusMessage *message; - DBusError error; DBusPendingCall *pending = NULL; if (!app_hash) @@ -191,22 +241,26 @@ get_application (const char *bus_name) if (!bus_name_dup) return NULL; // TODO: change below to something that will send state-change:defunct notification if necessary */ app = _atspi_application_new (bus_name); - if (!app) return NULL; app->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); app->bus = dbus_connection_ref (_atspi_bus ()); + gettimeofday (&app->time_added, NULL); app->cache = ATSPI_CACHE_UNDEFINED; g_hash_table_insert (app_hash, bus_name_dup, app); - dbus_error_init (&error); 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; } -static AtspiAccessible * +AtspiAccessible * ref_accessible (const char *app_name, const char *path) { AtspiApplication *app; @@ -221,8 +275,9 @@ ref_accessible (const char *app_name, const char *path) { if (!app->root) { - app->root = atspi_accessible_new (app, atspi_path_root); + app->root = _atspi_accessible_new (app, atspi_path_root); app->root->accessible_parent = atspi_get_desktop (0); + app->root->accessible_parent->children = g_list_append (app->root->accessible_parent->children, g_object_ref (app->root)); } return g_object_ref (app->root); } @@ -232,11 +287,10 @@ ref_accessible (const char *app_name, const char *path) { return g_object_ref (a); } - a = atspi_accessible_new (app, 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; } @@ -254,9 +308,7 @@ ref_hyperlink (const char *app_name, const char *path) { return g_object_ref (hyperlink); } - hyperlink = atspi_hyperlink_new (app, path); - if (!hyperlink) - return NULL; + hyperlink = _atspi_hyperlink_new (app, path); g_hash_table_insert (app->hash, g_strdup (hyperlink->parent.path), hyperlink); /* TODO: This should be a weak ref */ g_object_ref (hyperlink); /* for the hash */ @@ -287,7 +339,7 @@ handle_remove_accessible (DBusConnection *bus, DBusMessage *message, void *user_ if (strcmp (signature, "(so)") != 0) { - g_warning (_("AT-SPI: Unknown signature %s for RemoveAccessible"), signature); + g_warning ("AT-SPI: Unknown signature %s for RemoveAccessible", signature); return DBUS_HANDLER_RESULT_HANDLED; } @@ -306,77 +358,51 @@ handle_remove_accessible (DBusConnection *bus, DBusMessage *message, void *user_ return DBUS_HANDLER_RESULT_HANDLED; } -static gboolean -add_app_to_desktop (AtspiAccessible *a, const char *bus_name) +static DBusHandlerResult +handle_name_owner_changed (DBusConnection *bus, DBusMessage *message, void *user_data) { - DBusError error; + const char *name, *new, *old; + static gboolean registry_lost = FALSE; + + if (!dbus_message_get_args (message, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) + { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } - dbus_error_init (&error); - AtspiAccessible *obj = ref_accessible (bus_name, atspi_path_root); - if (obj) + if (!strcmp (name, "org.a11y.atspi.Registry")) { - GList *new_list = g_list_append (a->children, obj); - if (new_list) + if (registry_lost && !old[0]) { - a->children = new_list; - return TRUE; + _atspi_reregister_event_listeners (); + _atspi_reregister_device_listeners (); + registry_lost = FALSE; } + else if (!new[0]) + registry_lost = TRUE; } - else - { - g_warning (_("AT-SPI: Error calling getRoot for %s: %s"), bus_name, error.message); - } - 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) + else if (app_hash) { - unref_object_and_descendants (l->data); + AtspiApplication *app = g_hash_table_lookup (app_hash, old); + if (app && app->bus_name && !strcmp(app->bus_name, name)) + g_object_run_dispose (G_OBJECT (app)); } - g_object_unref (obj); + return DBUS_HANDLER_RESULT_HANDLED; } static gboolean -remove_app_from_desktop (AtspiAccessible *a, const char *bus_name) +add_app_to_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; + AtspiAccessible *obj = ref_accessible (bus_name, atspi_path_root); + /* The app will be added to the desktop as a side-effect of calling + * ref_accessible */ + g_object_unref (obj); + return (obj != NULL); } -static AtspiAccessible *desktop; - void get_reference_from_iter (DBusMessageIter *iter, const char **app_name, const char **path) { @@ -392,7 +418,6 @@ get_reference_from_iter (DBusMessageIter *iter, const char **app_name, const cha static void add_accessible_from_iter (DBusMessageIter *iter) { - GList *new_list; DBusMessageIter iter_struct, iter_array; const char *app_name, *path; AtspiAccessible *accessible; @@ -428,8 +453,7 @@ add_accessible_from_iter (DBusMessageIter *iter) AtspiAccessible *child; get_reference_from_iter (&iter_array, &app_name, &path); child = ref_accessible (app_name, path); - new_list = g_list_append (accessible->children, child); - if (new_list) accessible->children = new_list; + accessible->children = g_list_append (accessible->children, child); } /* interfaces */ @@ -482,7 +506,7 @@ handle_get_items (DBusPendingCall *pending, void *user_data) 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); + g_warning ("AT-SPI: Error in GetItems, sender=%s, error=%s", sender, error); dbus_message_unref (reply); dbus_pending_call_unref (pending); return; @@ -503,7 +527,7 @@ handle_get_items (DBusPendingCall *pending, void *user_data) static AtspiAccessible * ref_accessible_desktop (AtspiApplication *app) { - DBusError error; + GError *error; DBusMessage *message, *reply; DBusMessageIter iter, iter_array; gchar *bus_name_dup; @@ -513,25 +537,30 @@ ref_accessible_desktop (AtspiApplication *app) g_object_ref (desktop); return desktop; } - desktop = atspi_accessible_new (app, atspi_path_root); + desktop = _atspi_accessible_new (app, atspi_path_root); if (!desktop) { 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"); - dbus_error_init (&error); message = dbus_message_new_method_call (atspi_bus_registry, atspi_path_root, atspi_interface_accessible, "GetChildren"); if (!message) return NULL; - reply = _atspi_dbus_send_with_reply_and_block (message, NULL); + error = NULL; + reply = _atspi_dbus_send_with_reply_and_block (message, &error); if (!reply || strcmp (dbus_message_get_signature (reply), "a(so)") != 0) { - g_warning ("Couldn't get application list: %s", error.message); + if (error != NULL) + { + g_warning ("Couldn't get application list: %s", error->message); + g_clear_error (&error); + } if (reply) dbus_message_unref (reply); return NULL; @@ -551,17 +580,20 @@ ref_accessible_desktop (AtspiApplication *app) if (bus_name_dup) g_hash_table_insert (app_hash, bus_name_dup, app); - return desktop; + 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); } @@ -584,7 +616,7 @@ _atspi_dbus_return_accessible_from_message (DBusMessage *message) } else { - g_warning (_("AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange signature %s"), signature); + g_warning ("AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange signature %s", signature); } dbus_message_unref (message); return retval; @@ -604,8 +636,12 @@ _atspi_dbus_return_hyperlink_from_message (DBusMessage *message) { DBusMessageIter iter; AtspiHyperlink *retval = NULL; - const char *signature = dbus_message_get_signature (message); - + const char *signature; + + if (!message) + return NULL; + + signature = dbus_message_get_signature (message); if (!strcmp (signature, "(so)")) { dbus_message_iter_init (message, &iter); @@ -613,7 +649,7 @@ _atspi_dbus_return_hyperlink_from_message (DBusMessage *message) } else { - g_warning (_("AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange signature %s"), signature); + g_warning ("AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange signature %s", signature); } dbus_message_unref (message); return retval; @@ -634,11 +670,10 @@ static DBusHandlerResult handle_add_accessible (DBusConnection *bus, DBusMessage *message, void *user_data) { DBusMessageIter iter; - const char *sender = dbus_message_get_sender (message); if (strcmp (dbus_message_get_signature (message), cache_signal_type) != 0) { - g_warning (_("AT-SPI: AddAccessible with unknown signature %s\n"), + g_warning ("AT-SPI: AddAccessible with unknown signature %s\n", dbus_message_get_signature (message)); return DBUS_HANDLER_RESULT_HANDLED; } @@ -655,7 +690,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) @@ -666,11 +701,11 @@ process_deferred_message (BusDataClosure *closure) if (type == DBUS_MESSAGE_TYPE_SIGNAL && !strncmp (interface, "org.a11y.atspi.Event.", 21)) { - atspi_dbus_handle_event (closure->bus, closure->message, closure->data); + _atspi_dbus_handle_event (closure->bus, closure->message, closure->data); } if (dbus_message_is_method_call (closure->message, atspi_interface_device_event_listener, "NotifyEvent")) { - atspi_dbus_handle_DeviceEvent (closure->bus, + _atspi_dbus_handle_DeviceEvent (closure->bus, closure->message, closure->data); } if (dbus_message_is_signal (closure->message, atspi_interface_cache, "AddAccessible")) @@ -681,52 +716,64 @@ process_deferred_message (BusDataClosure *closure) { handle_remove_accessible (closure->bus, closure->message, closure->data); } + if (dbus_message_is_signal (closure->message, "org.freedesktop.DBus", "NameOwnerChanged")) + { + handle_name_owner_changed (closure->bus, closure->message, closure->data); + } } -static GList *deferred_messages = NULL; +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; if (in_process_deferred_messages) return TRUE; in_process_deferred_messages = 1; - while (deferred_messages != NULL) + while ((closure = g_queue_pop_head (deferred_messages))) { - BusDataClosure *closure = deferred_messages->data; process_deferred_message (closure); - deferred_messages = g_list_remove (deferred_messages, 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) { BusDataClosure *closure = g_new (BusDataClosure, 1); - GList *new_list; - if (!closure) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; closure->bus = dbus_connection_ref (bus); closure->message = dbus_message_ref (message); closure->data = user_data; - new_list = g_list_append (deferred_messages, closure); - if (new_list) - deferred_messages = new_list; + g_queue_push_tail (deferred_messages, closure); + + 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); + } - if (process_deferred_messages_id == -1) - process_deferred_messages_id = g_idle_add (_atspi_process_deferred_messages, NULL); return DBUS_HANDLER_RESULT_HANDLED; } @@ -753,55 +800,48 @@ atspi_dbus_filter (DBusConnection *bus, DBusMessage *message, void *data) { return defer_message (bus, message, data); } + if (dbus_message_is_signal (message, "org.freedesktop.DBus", "NameOwnerChanged")) + { + defer_message (bus, message, data); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } 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. * * TODO: Avoid having duplicate functions for this here and in at-spi2-atk */ -static const gchar * +static gchar * spi_display_name (void) { - static const char *canonical_display_name = NULL; - if (!canonical_display_name) + char *canonical_display_name = NULL; + const gchar *display_env = g_getenv ("AT_SPI_DISPLAY"); + + if (!display_env) { - const gchar *display_env = g_getenv ("AT_SPI_DISPLAY"); - if (!display_env) + display_env = g_getenv ("DISPLAY"); + if (!display_env || !display_env[0]) + return NULL; + else { - display_env = g_getenv ("DISPLAY"); - if (!display_env || !display_env[0]) - canonical_display_name = ":0"; - else + gchar *display_p, *screen_p; + canonical_display_name = g_strdup (display_env); + display_p = g_utf8_strrchr (canonical_display_name, -1, ':'); + screen_p = g_utf8_strrchr (canonical_display_name, -1, '.'); + if (screen_p && display_p && (screen_p > display_p)) { - gchar *display_p, *screen_p; - canonical_display_name = g_strdup (display_env); - display_p = g_utf8_strrchr (canonical_display_name, -1, ':'); - screen_p = g_utf8_strrchr (canonical_display_name, -1, '.'); - if (screen_p && display_p && (screen_p > display_p)) - { - *screen_p = '\0'; - } + *screen_p = '\0'; } } - else - { - canonical_display_name = display_env; - } } + else + { + canonical_display_name = g_strdup (display_env); + } + return canonical_display_name; } @@ -810,13 +850,13 @@ 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) { - DBusError error; char *match; + const gchar *no_cache; if (atspi_inited) { @@ -825,74 +865,92 @@ atspi_init (void) atspi_inited = TRUE; - g_type_init (); - - get_live_refs(); + _atspi_get_live_refs(); - dbus_error_init (&error); bus = atspi_get_a11y_bus (); if (!bus) return 2; - dbus_bus_register (bus, &error); + dbus_bus_register (bus, NULL); atspi_dbus_connection_setup_with_g_main(bus, g_main_context_default()); dbus_connection_add_filter (bus, atspi_dbus_filter, NULL, NULL); - dbind_set_timeout (1000); match = g_strdup_printf ("type='signal',interface='%s',member='AddAccessible'", atspi_interface_cache); - dbus_error_init (&error); - dbus_bus_add_match (bus, match, &error); + dbus_bus_add_match (bus, match, NULL); g_free (match); match = g_strdup_printf ("type='signal',interface='%s',member='RemoveAccessible'", atspi_interface_cache); - dbus_bus_add_match (bus, match, &error); + dbus_bus_add_match (bus, match, NULL); g_free (match); match = g_strdup_printf ("type='signal',interface='%s',member='ChildrenChanged'", atspi_interface_event_object); - dbus_bus_add_match (bus, match, &error); + dbus_bus_add_match (bus, match, NULL); g_free (match); match = g_strdup_printf ("type='signal',interface='%s',member='PropertyChange'", atspi_interface_event_object); - dbus_bus_add_match (bus, match, &error); + dbus_bus_add_match (bus, match, NULL); g_free (match); match = g_strdup_printf ("type='signal',interface='%s',member='StateChanged'", atspi_interface_event_object); - dbus_bus_add_match (bus, match, &error); + dbus_bus_add_match (bus, match, NULL); g_free (match); + + dbus_bus_add_match (bus, + "type='signal', interface='org.freedesktop.DBus', member='NameOwnerChanged'", + NULL); + + no_cache = g_getenv ("ATSPI_NO_CACHE"); + if (no_cache && g_strcmp0 (no_cache, "0") != 0) + atspi_no_cache = TRUE; + + deferred_messages = g_queue_new (); + return 0; } - static GMainLoop *mainloop; +/** + * 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. * - * (NOTE: This method does not return control, it is exited via a call to - * atspi_event_quit () from within an event handler). + * NOTE: This method does not return control; it is exited via a call to + * #atspi_event_quit from within an event handler. * **/ void atspi_event_main (void) { - mainloop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (mainloop); + atspi_main_loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (atspi_main_loop); + atspi_main_loop = NULL; } /** * atspi_event_quit: * - * Quits the last main event loop for the SPI services, - * see atspi_event_main + * Quits the last main event loop for the AT-SPI services, + * See: #atspi_event_main **/ void atspi_event_quit (void) { - g_main_loop_quit (mainloop); + g_main_loop_quit (atspi_main_loop); } /** * atspi_exit: * - * Disconnects from the Accessibility Registry and releases + * Disconnects from #AtspiRegistry instances and releases * any floating resources. Call only once at exit. * - * Returns: 0 if there were no leaks, otherwise non zero. + * Returns: 0 if there were no leaks, otherwise other integer values. **/ int atspi_exit (void) @@ -920,6 +978,92 @@ atspi_exit (void) return leaked; } +static GSList *hung_processes; + +static void +remove_hung_process (DBusPendingCall *pending, void *data) +{ + + hung_processes = g_slist_remove (hung_processes, data); + g_free (data); + dbus_pending_call_unref (pending); +} + +static void +check_for_hang (DBusMessage *message, DBusError *error, DBusConnection *bus, const char *bus_name) +{ + if (!message && error->name && + !strcmp (error->name, "org.freedesktop.DBus.Error.NoReply")) + { + GSList *l; + DBusMessage *message; + gchar *bus_name_dup; + DBusPendingCall *pending = NULL; + for (l = hung_processes; l; l = l->next) + if (!strcmp (l->data, bus_name)) + return; + message = dbus_message_new_method_call (bus_name, "/", + "org.freedesktop.DBus.Peer", + "Ping"); + if (!message) + return; + dbus_connection_send_with_reply (bus, message, &pending, -1); + dbus_message_unref (message); + if (!pending) + return; + bus_name_dup = g_strdup (bus_name); + hung_processes = g_slist_append (hung_processes, bus_name_dup); + dbus_pending_call_set_notify (pending, remove_hung_process, bus_name_dup, NULL); + } +} + +static gboolean +connection_is_hung (const char *bus_name) +{ + GSList *l; + + for (l = hung_processes; l; l = l->next) + if (!strcmp (l->data, bus_name)) + return TRUE; + return FALSE; +} + +static gboolean +check_app (AtspiApplication *app, GError **error) +{ + if (!app || !app->bus) + { + g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_APPLICATION_GONE, + _("The application no longer exists")); + return FALSE; + } + + if (atspi_main_loop && connection_is_hung (app->bus_name)) + { + g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, + "The process appears to be hung."); + return FALSE; + } + + return TRUE; +} + +static void +set_timeout (AtspiApplication *app) +{ + struct timeval tv; + int diff; + + if (app && app_startup_time > 0) + { + gettimeofday (&tv, NULL); + diff = (tv.tv_sec - app->time_added.tv_sec) * 1000 + (tv.tv_usec - app->time_added.tv_usec) / 1000; + dbind_set_timeout (MAX(method_call_timeout, app_startup_time - diff)); + } + else + dbind_set_timeout (method_call_timeout); +} + dbus_bool_t _atspi_dbus_call (gpointer obj, const char *interface, const char *method, GError **error, const char *type, ...) { @@ -928,23 +1072,27 @@ _atspi_dbus_call (gpointer obj, const char *interface, const char *method, GErro DBusError err; AtspiObject *aobj = ATSPI_OBJECT (obj); - if (!aobj->app || !aobj->app->bus) + if (!check_app (aobj->app, error)) + return FALSE; + + if (!allow_sync) { - g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_APPLICATION_GONE, - _("The application no longer exists")); + _atspi_set_error_no_sync (error); return FALSE; } va_start (args, type); dbus_error_init (&err); + set_timeout (aobj->app); retval = dbind_method_call_reentrant_va (aobj->app->bus, aobj->app->bus_name, aobj->path, interface, method, &err, type, args); va_end (args); - _atspi_process_deferred_messages ((gpointer)TRUE); + check_for_hang (NULL, &err, aobj->app->bus, aobj->app->bus_name); + process_deferred_messages (); if (dbus_error_is_set (&err)) { - /* TODO: Set gerror */ + g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "%s", err.message); dbus_error_free (&err); } return retval; @@ -963,6 +1111,7 @@ _atspi_dbus_call_partial (gpointer obj, return _atspi_dbus_call_partial_va (obj, interface, method, error, type, args); } + DBusMessage * _atspi_dbus_call_partial_va (gpointer obj, const char *interface, @@ -973,37 +1122,46 @@ _atspi_dbus_call_partial_va (gpointer obj, { AtspiObject *aobj = ATSPI_OBJECT (obj); DBusError err; - DBusMessage *msg = NULL, *reply = NULL; - DBusMessageIter iter; - const char *p; + DBusMessage *msg = NULL, *reply = NULL; + DBusMessageIter iter; + const char *p; dbus_error_init (&err); - if (!aobj->app || !aobj->app->bus) - { - g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_APPLICATION_GONE, - _("The application no longer exists")); + if (!check_app (aobj->app, error)) goto out; - } - msg = dbus_message_new_method_call (aobj->app->bus_name, aobj->path, interface, method); - if (!msg) - goto out; + msg = dbus_message_new_method_call (aobj->app->bus_name, aobj->path, interface, method); + if (!msg) + goto out; - p = type; - dbus_message_iter_init_append (msg, &iter); - dbind_any_marshal_va (&iter, &p, args); + p = type; + dbus_message_iter_init_append (msg, &iter); + dbind_any_marshal_va (&iter, &p, args); - reply = dbind_send_and_allow_reentry (aobj->app->bus, msg, &err); + set_timeout (aobj->app); + reply = dbind_send_and_allow_reentry (aobj->app->bus, msg, &err); + check_for_hang (reply, &err, aobj->app->bus, aobj->app->bus_name); 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 */ + g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, err.message); dbus_error_free (&err); + if (reply) + dbus_message_unref(reply); + } + else 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; } @@ -1016,14 +1174,17 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, DBusError err; dbus_bool_t retval = FALSE; AtspiObject *aobj = ATSPI_OBJECT (obj); + char expected_type = (type [0] == '(' ? 'r' : type [0]); if (!aobj) return FALSE; - if (!aobj->app || !aobj->app->bus) + if (!check_app (aobj->app, error)) + return FALSE; + + if (!allow_sync) { - g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_APPLICATION_GONE, - _("The application no longer exists")); + _atspi_set_error_no_sync (error); return FALSE; } @@ -1038,9 +1199,11 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, } dbus_message_append_args (message, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); dbus_error_init (&err); + set_timeout (aobj->app); 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 @@ -1049,23 +1212,23 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR) { - const char *err; - dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &err, DBUS_TYPE_INVALID); - if (err) - g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err); + 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); goto done; } 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 ("AT-SPI: 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); - if (dbus_message_iter_get_arg_type (&iter_variant) != type[0]) + if (dbus_message_iter_get_arg_type (&iter_variant) != expected_type) { - g_warning (_("atspi_dbus_get_property: Wrong type: expected %s, got %c\n"), type, dbus_message_iter_get_arg_type (&iter_variant)); + g_warning ("atspi_dbus_get_property: Wrong type: expected %s, got %c\n", type, dbus_message_iter_get_arg_type (&iter_variant)); goto done; } if (!strcmp (type, "(so)")) @@ -1080,6 +1243,7 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, } retval = TRUE; done: + dbus_error_free (&err); if (reply) dbus_message_unref (reply); return retval; @@ -1100,10 +1264,11 @@ _atspi_dbus_send_with_reply_and_block (DBusMessage *message, GError **error) bus = (app ? app->bus : _atspi_bus()); 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 (err.message) + if (dbus_error_is_set (&err)) { if (error) g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err.message); @@ -1132,7 +1297,9 @@ _atspi_dbus_return_hash_from_message (DBusMessage *message) GHashTable * _atspi_dbus_hash_from_iter (DBusMessageIter *iter) { - GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal); + GHashTable *hash = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); DBusMessageIter iter_array, iter_dict; dbus_message_iter_recurse (iter, &iter_array); @@ -1178,15 +1345,12 @@ _atspi_dbus_attribute_array_from_iter (DBusMessageIter *iter) { const char *name, *value; gchar *str; - GArray *new_array; dbus_message_iter_recurse (&iter_array, &iter_dict); dbus_message_iter_get_basic (&iter_dict, &name); dbus_message_iter_next (&iter_dict); dbus_message_iter_get_basic (&iter_dict, &value); str = g_strdup_printf ("%s:%s", name, value); - new_array = g_array_append_val (array, str); - if (new_array) - array = new_array; + array = g_array_append_val (array, str); dbus_message_iter_next (&iter_array);; } return array; @@ -1196,8 +1360,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) { @@ -1208,7 +1380,7 @@ _atspi_dbus_set_interfaces (AtspiAccessible *accessible, DBusMessageIter *iter) n = _atspi_get_iface_num (iface); if (n == -1) { - g_warning (_("AT-SPI: Unknown interface %s"), iface); + g_warning ("AT-SPI: Unknown interface %s", iface); } else accessible->interfaces |= (1 << n); @@ -1228,7 +1400,7 @@ _atspi_dbus_set_state (AtspiAccessible *accessible, DBusMessageIter *iter) dbus_message_iter_get_fixed_array (&iter_array, &states, &count); if (count != 2) { - g_warning (_("AT-SPI: expected 2 values in states array; got %d\n"), count); + g_warning ("AT-SPI: expected 2 values in states array; got %d\n", count); if (!accessible->states) accessible->states = _atspi_state_set_new_internal (accessible, 0); } @@ -1245,7 +1417,7 @@ _atspi_dbus_set_state (AtspiAccessible *accessible, DBusMessageIter *iter) } GQuark -atspi_error_quark (void) +_atspi_error_quark (void) { return g_quark_from_static_string ("atspi_error"); } @@ -1253,35 +1425,47 @@ atspi_error_quark (void) /* * Gets the IOR from the XDisplay. */ +#ifdef HAVE_X11 static char * get_accessibility_bus_address_x11 (void) { Atom AT_SPI_BUS; Atom actual_type; - Display *bridge_display; + Display *bridge_display = NULL; int actual_format; - unsigned char *data = NULL; + char *data; + unsigned char *data_x11 = NULL; unsigned long nitems; unsigned long leftover; + char *display_name; + + display_name = spi_display_name (); + if (!display_name) + return NULL; + + bridge_display = XOpenDisplay (display_name); + g_free (display_name); - bridge_display = XOpenDisplay (spi_display_name ()); if (!bridge_display) { g_warning ("Could not open X display"); return NULL; } - + AT_SPI_BUS = XInternAtom (bridge_display, "AT_SPI_BUS", False); XGetWindowProperty (bridge_display, XDefaultRootWindow (bridge_display), AT_SPI_BUS, 0L, (long) BUFSIZ, False, (Atom) 31, &actual_type, &actual_format, - &nitems, &leftover, &data); + &nitems, &leftover, &data_x11); XCloseDisplay (bridge_display); - return g_strdup (data); + data = g_strdup ((gchar *)data_x11); + XFree (data_x11); + return data; } +#endif static char * get_accessibility_bus_address_dbus (void) @@ -1289,6 +1473,7 @@ get_accessibility_bus_address_dbus (void) DBusConnection *session_bus = NULL; DBusMessage *message; DBusMessage *reply; + DBusError error; char *address = NULL; session_bus = dbus_bus_get (DBUS_BUS_SESSION, NULL); @@ -1300,15 +1485,21 @@ get_accessibility_bus_address_dbus (void) "org.a11y.Bus", "GetAddress"); + dbus_error_init (&error); reply = dbus_connection_send_with_reply_and_block (session_bus, message, -1, - NULL); + &error); dbus_message_unref (message); if (!reply) - return NULL; - + { + g_warning ("Error retrieving accessibility bus address: %s: %s", + error.name, error.message); + dbus_error_free (&error); + goto out; + } + { const char *tmp_address; if (!dbus_message_get_args (reply, @@ -1318,43 +1509,290 @@ 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; } +static DBusConnection *a11y_bus; +static dbus_int32_t a11y_dbus_slot = -1; + +static void +a11y_bus_free (void *data) +{ + if (data == a11y_bus) + { + a11y_bus = NULL; + dbus_connection_free_data_slot (&a11y_dbus_slot); + } +} + +/** + * atspi_get_a11y_bus: (skip) + */ DBusConnection * atspi_get_a11y_bus (void) { - DBusConnection *bus = NULL; DBusError error; - char *address; + char *address = 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"); +#ifdef HAVE_X11 address = get_accessibility_bus_address_x11 (); +#endif if (!address) address = get_accessibility_bus_address_dbus (); if (!address) return NULL; dbus_error_init (&error); - bus = dbus_connection_open (address, &error); - if (!bus) + a11y_bus = dbus_connection_open_private (address, &error); + g_free (address); + + if (!a11y_bus) { g_warning ("Couldn't connect to accessibility bus: %s", error.message); + dbus_error_free (&error); return NULL; } else { - if (!dbus_bus_register (bus, &error)) + if (!dbus_bus_register (a11y_bus, &error)) { g_warning ("Couldn't register with accessibility bus: %s", error.message); + dbus_error_free (&error); + dbus_connection_close (a11y_bus); + dbus_connection_unref (a11y_bus); + a11y_bus = NULL; return NULL; } } - - return bus; + + /* Simulate a weak ref on the bus */ + dbus_connection_set_data (a11y_bus, a11y_dbus_slot, a11y_bus, a11y_bus_free); + + return a11y_bus; +} + +/** + * 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. + * + * 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. + */ +void +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); +} + +#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")); }