X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git;a=blobdiff_plain;f=atk-adaptor%2Fbridge.c;h=3e5fba4d22dea3b9b0fe18a3b66503cfeaca3b6e;hp=ff0514081296855c9dc2e14b8870a8e831a11687;hb=7fc3c0ed67c52371855c5db456f26f27dfd22126;hpb=c844663e2e13adff0e9a60fbab1c9d57307c2fbb diff --git a/atk-adaptor/bridge.c b/atk-adaptor/bridge.c index ff05140..9880639 100644 --- a/atk-adaptor/bridge.c +++ b/atk-adaptor/bridge.c @@ -2,6 +2,7 @@ * AT-SPI - Assistive Technology Service Provider Interface * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) * + * Copyright 2008, 2009 Codethink Ltd. * Copyright 2001, 2002, 2003 Sun Microsystems Inc., * Copyright 2001, 2002, 2003 Ximian, Inc. * @@ -21,1152 +22,988 @@ * Boston, MA 02111-1307, USA. */ +#define _GNU_SOURCE #include "config.h" -#include "dbus/dbus-glib-lowlevel.h" -#include -#include -#include -#include #include #include +#include #include +#include +#include #include -#include -#include -#include "accessible.h" -#undef SPI_BRIDGE_DEBUG +#include +#include +#include -#define DBG(a,b) if(_dbg>=(a))b +#include "bridge.h" +#include "event.h" +#include "adaptors.h" +#include "object.h" +#include "accessible-stateset.h" -#define bridge_threads_leave() \ - if (!during_init_shutdown && !g_main_context_is_owner (NULL)) atk_misc_threads_leave(misc); -#define bridge_threads_enter() \ - if (!during_init_shutdown && !g_main_context_is_owner (NULL)) atk_misc_threads_enter(misc); +#include "accessible-register.h" +#include "accessible-leasing.h" +#include "accessible-cache.h" -typedef struct _SpiAppData SpiAppData; -struct _SpiAppData -{ - AtkObject *root; - DRouteData droute; -}; +#include "spi-dbus.h" -int _dbg = 0; -static const char *registry = NULL; -static char *device_event_controller = NULL; -static SpiAppData *this_app = NULL; -static gboolean registry_died = FALSE; -static gboolean atk_listeners_registered = FALSE; -static gint toplevels = 0; -static gboolean exiting = FALSE; -static AtkMisc *misc = NULL; -static gboolean during_init_shutdown = TRUE; - -static guint atk_signal_text_changed; -static guint atk_signal_children_changed; -static guint atk_signal_active_descendant_changed; -static guint atk_signal_text_selection_changed; - -/* NOT YET USED - static guint atk_signal_row_reordered; - static guint atk_signal_row_inserted; - static guint atk_signal_row_deleted; - static guint atk_signal_column_reordered; - static guint atk_signal_column_inserted; - static guint atk_signal_column_deleted; -*/ - -static guint atk_signal_link_selected; -static guint atk_signal_bounds_changed; - -static const char *spi_atk_bridge_get_registry (void); -static gboolean spi_atk_bridge_do_registration (void); -static void spi_atk_bridge_toplevel_added (AtkObject *object, - guint index, - AtkObject *child); -static void spi_atk_bridge_toplevel_removed (AtkObject *object, - guint index, - AtkObject *child); - -static void spi_atk_bridge_exit_func (void); -static void spi_atk_register_event_listeners (void); -static void spi_atk_bridge_focus_tracker (AtkObject *object); -static gchar *spi_atk_bridge_get_registry_ior (void); -static void spi_atk_bridge_register_application (const char *registry); -static gboolean spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data); +/*---------------------------------------------------------------------------*/ -static gboolean -spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data); -static gboolean -spi_atk_bridge_document_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data); -static gboolean -spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data); -static gboolean spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data); -static gint spi_atk_bridge_key_listener (AtkKeyEventStruct *event, - gpointer data); -static void spi_atk_tidy_windows (void); -static void deregister_application (SpiAppData *app); -static void reinit_register_vars (void); - -/* For automatic libgnome init */ -extern void gnome_accessibility_module_init (void); -extern void gnome_accessibility_module_shutdown (void); - -static int atk_bridge_initialized = FALSE; -static pid_t atk_bridge_pid = 0; -static guint atk_bridge_focus_tracker_id = 0; -static guint atk_bridge_key_event_listener_id = 0; -static GArray *listener_ids = NULL; +static DBusHandlerResult +signal_filter (DBusConnection *bus, DBusMessage *message, void *user_data); -/* - * These exported symbols are hooked by gnome-program - * to provide automatic module initialization and shutdown. - */ -extern void gnome_accessibility_module_init (void); -extern void gnome_accessibility_module_shutdown (void); +SpiBridge *spi_global_app_data = NULL; + +static gboolean inited = FALSE; + +/*---------------------------------------------------------------------------*/ static void -spi_atk_bridge_init_event_type_consts () +add_event (const char *bus_name, const char *event) { - static gboolean done = FALSE; + event_data *evdata; + gchar **data; - if (done) + spi_atk_add_client (bus_name); + evdata = (event_data *) g_malloc (sizeof (*evdata)); + if (!evdata) return; - - atk_signal_children_changed = g_signal_lookup ("children_changed", - ATK_TYPE_OBJECT); - atk_signal_text_changed = g_signal_lookup ("text_changed", - ATK_TYPE_TEXT); - atk_signal_bounds_changed = g_signal_lookup ("bounds_changed", - ATK_TYPE_COMPONENT); - atk_signal_active_descendant_changed = - g_signal_lookup ("active_descendant_changed", - ATK_TYPE_OBJECT); - atk_signal_link_selected = g_signal_lookup ("link_selected", - ATK_TYPE_HYPERTEXT); - atk_signal_text_selection_changed = g_signal_lookup ("text_selection_changed", - ATK_TYPE_TEXT); - done = TRUE; + data = g_strsplit (event, ":", 3); + if (!data) + { + g_free (evdata); + return; + } + evdata->bus_name = g_strdup (bus_name); + evdata->data = data; + spi_global_app_data->events = g_list_append (spi_global_app_data->events, evdata); } -static gboolean -post_init (gpointer data) -{ - during_init_shutdown = FALSE; - return FALSE; -} +static GSList *clients = NULL; -static DBusObjectPathVTable droute_vtable = +static void +tally_event_reply () { - NULL, - &droute_message, - NULL, NULL, NULL, NULL -}; + static int replies_received = 0; -static SpiAppData * -spi_app_init (AtkObject *root) -{ - DBusError error; - dbus_error_init(&error); - SpiAppData *ad = (SpiAppData *)calloc(sizeof(SpiAppData), 1); - if (!ad) return NULL; - ad->root = root; - ad->droute.bus = dbus_bus_get(DBUS_BUS_SESSION, &error); - if (!ad->droute.bus) + replies_received++; + if (replies_received == 3) { - g_warning("Couldn't connect to dbus: %s\n", error.message); - free(ad); - return NULL; + if (!clients) + spi_atk_deregister_event_listeners (); + spi_global_app_data->events_initialized = TRUE; } - //dbus_connection_set_exit_on_disconnect(ad->droute.bus, FALSE); - dbus_bus_register(ad->droute.bus, &error); - spi_dbus_initialize (&ad->droute); - if (!dbus_connection_try_register_fallback (ad->droute.bus, "/org/freedesktop/atspi", &droute_vtable, &ad->droute, &error)) - { - g_warning("Couldn't register droute.\n"); - } - dbus_connection_setup_with_g_main(ad->droute.bus, g_main_context_default()); - return ad; } -static int -atk_bridge_init (gint *argc, gchar **argv[]) +static void +get_events_reply (DBusPendingCall *pending, void *user_data) { - const char *debug_env_string = g_getenv ("AT_SPI_DEBUG"); - gchar *fname; - gboolean success = FALSE; + DBusMessage *reply = dbus_pending_call_steal_reply (pending); + DBusMessageIter iter, iter_array, iter_struct; + + if (!reply) + goto done; - if (atk_bridge_initialized) + if (strcmp (dbus_message_get_signature (reply), "a(ss)") != 0) { - return 0; + g_warning ("atk-bridge: GetRegisteredEvents returned message with unknown signature"); + goto done; } - atk_bridge_initialized = TRUE; - atk_bridge_pid = getpid (); - - misc = atk_misc_get_instance(); - if (g_getenv ("ATK_BRIDGE_REDIRECT_LOG")) + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_array); + while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) { - fname = g_strconcat ("/tmp/", g_get_prgname (), ".at-spi-log", NULL); - /* make sure we're not being redirected - security issue */ - if (!g_file_test (fname, G_FILE_TEST_IS_SYMLINK)) - freopen (fname, "w", stderr); - g_free (fname); + char *bus_name, *event; + dbus_message_iter_recurse (&iter_array, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &bus_name); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &event); + add_event (bus_name, event); + dbus_message_iter_next (&iter_array); } - if (debug_env_string) - _dbg = (int) g_ascii_strtod (debug_env_string, NULL); +done: + if (reply) + dbus_message_unref (reply); + if (pending) + dbus_pending_call_unref (pending); - /* Connect to dbus */ - this_app = spi_app_init (atk_get_root ()); + tally_event_reply (); +} - /* - * We only want to enable the bridge for top level - * applications, we detect bonobo components by seeing - * if they were activated with the intention of extracting - * an impl. by IID - very solid. - */ -#ifdef WITH_BONOBO - // TODO: Figure out if this is still needed - if (bonobo_activation_iid_get ()) -#else - if (0) -#endif - { - DBG (1, g_message ("Found Bonobo component\n")); - g_signal_connect (atk_get_root (), - "children-changed::add", - (GCallback) spi_atk_bridge_toplevel_added, - NULL); - g_signal_connect (atk_get_root (), - "children-changed::remove", - (GCallback) spi_atk_bridge_toplevel_removed, - NULL); - /* in this case we redefine 'success' to mean 'registry is present' */ - success = (spi_atk_bridge_get_registry () != NULL); - } - else - { - success = spi_atk_bridge_do_registration (); - } - /* - * we must emit events even if we are not registered as a - * full-fledged app; See bugzilla #400709. - */ - if (success) +static void +get_device_events_reply (DBusPendingCall *pending, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply (pending); + DBusMessageIter iter, iter_array, iter_struct; + + if (!reply) + goto done; + + if (strncmp (dbus_message_get_signature (reply), "a(s", 3) != 0) { - spi_atk_register_event_listeners (); - spi_atk_bridge_init_event_type_consts (); + g_warning ("atk-bridge: get_device_events_reply: unknown signature"); + goto done; } - else + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_array); + while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) { - atk_bridge_initialized = FALSE; + char *bus_name; + dbus_message_iter_recurse (&iter_array, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &bus_name); + spi_atk_add_client (bus_name); + dbus_message_iter_next (&iter_array); } - g_idle_add (post_init, NULL); - return 0; +done: + if (reply) + dbus_message_unref (reply); + if (pending) + dbus_pending_call_unref (pending); + + tally_event_reply (); } -static gboolean -spi_atk_bridge_do_registration (void) +static void +get_registered_event_listeners (SpiBridge *app) { - if (spi_atk_bridge_get_registry () == NULL) - { - g_warning ("Could not locate registry"); - return FALSE; - } + DBusMessage *message; + DBusPendingCall *pending = NULL; - /* Create the accessible application server object */ - if (this_app == NULL) - this_app = spi_app_init (atk_get_root ()); - - DBG (1, g_message ("About to register application\n")); + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_REGISTRY, + ATSPI_DBUS_INTERFACE_REGISTRY, + "GetRegisteredEvents"); + if (!message) + return; - spi_atk_bridge_register_application (spi_atk_bridge_get_registry ()); - - g_atexit (spi_atk_bridge_exit_func); + dbus_connection_send_with_reply (app->bus, message, &pending, -1); + dbus_message_unref (message); + if (!pending) + { + spi_global_app_data->events_initialized = TRUE; + return; + } + dbus_pending_call_set_notify (pending, get_events_reply, NULL, NULL); - DBG (1, g_message ("Application registered & listening\n")); - return TRUE; -} + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_DEC, + ATSPI_DBUS_INTERFACE_DEC, + "GetKeystrokeListeners"); + if (!message) + return; + pending = NULL; + dbus_connection_send_with_reply (app->bus, message, &pending, -1); + dbus_message_unref (message); + if (!pending) + { + spi_global_app_data->events_initialized = TRUE; + return; + } + dbus_pending_call_set_notify (pending, get_device_events_reply, NULL, NULL); -static void -spi_atk_bridge_toplevel_added (AtkObject *object, - guint index, - AtkObject *child) -{ - if (toplevels == 0) + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_DEC, + ATSPI_DBUS_INTERFACE_DEC, + "GetDeviceEventListeners"); + if (!message) + return; + pending = NULL; + dbus_connection_send_with_reply (app->bus, message, &pending, -1); + dbus_message_unref (message); + if (!pending) { - spi_atk_bridge_do_registration (); + spi_global_app_data->events_initialized = TRUE; + return; } - toplevels++; + dbus_pending_call_set_notify (pending, get_device_events_reply, NULL, NULL); } static void -spi_atk_bridge_toplevel_removed (AtkObject *object, - guint index, - AtkObject *child) +register_reply (DBusPendingCall *pending, void *user_data) { - toplevels--; - if (toplevels == 0) + DBusMessage *reply; + SpiBridge *app = user_data; + + reply = dbus_pending_call_steal_reply (pending); + dbus_pending_call_unref (pending); + if (reply) { - deregister_application (this_app); - reinit_register_vars (); + gchar *app_name, *obj_path; + + if (strcmp (dbus_message_get_signature (reply), "(so)") != 0) + { + g_warning ("AT-SPI: Could not obtain desktop path or name\n"); + } + else + { + DBusMessageIter iter, iter_struct; + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &app_name); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &obj_path); + + app->desktop_name = g_strdup (app_name); + app->desktop_path = g_strdup (obj_path); + } } - if (toplevels < 0) + else { - g_warning ("More toplevels removed than added\n"); - toplevels = 0; + g_warning ("AT-SPI: Could not embed inside desktop"); + return; } + dbus_message_unref (reply); + + get_registered_event_listeners (spi_global_app_data); } -static void -spi_atk_bridge_register_application (const char *registry) +static gboolean +register_application (SpiBridge * app) { - DBusMessage *message, *reply; + DBusMessage *message; + DBusMessageIter iter; DBusError error; + DBusPendingCall *pending; - bridge_threads_leave (); - message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, SPI_DBUS_PATH_REGISTRY, SPI_DBUS_INTERFACE_REGISTRY, "registerApplication"); dbus_error_init (&error); - reply = dbus_connection_send_with_reply_and_block(this_app->droute.bus, message, 1000, &error); - if (error.message) g_warning (error.message); - if (reply) dbus_message_unref (reply); - if (message) dbus_message_unref (message); - bridge_threads_enter (); -} -/* - * Returns a 'canonicalized' value for DISPLAY, - * with the screen number stripped off if present. - */ -static const gchar* -spi_display_name (void) -{ - static const char *canonical_display_name = NULL; - if (!canonical_display_name) - { - const gchar *display_env = g_getenv ("AT_SPI_DISPLAY"); - if (!display_env) - { - 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 = strrchr (canonical_display_name, ':'); - screen_p = strrchr (canonical_display_name, '.'); - if (screen_p && display_p && (screen_p > display_p)) - { - *screen_p = '\0'; - } - } - } - else - { - canonical_display_name = display_env; - } - } - return canonical_display_name; -} + /* These will be overridden when we get a reply, but in practice these + defaults should always be correct */ + app->desktop_name = ATSPI_DBUS_NAME_REGISTRY; + app->desktop_path = ATSPI_DBUS_PATH_ROOT; -static Display *bridge_display = NULL; + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_ROOT, + ATSPI_DBUS_INTERFACE_SOCKET, + "Embed"); -static gchar * -spi_atk_bridge_get_registry_ior (void) -{ - - Atom AT_SPI_IOR; - Atom actual_type; - int actual_format; - unsigned char *data = NULL; - unsigned long nitems; - unsigned long leftover; - if (!bridge_display) - bridge_display = XOpenDisplay (spi_display_name ()); - - AT_SPI_IOR = XInternAtom (bridge_display, "AT_SPI_IOR", False); - XGetWindowProperty(bridge_display, - XDefaultRootWindow (bridge_display), - AT_SPI_IOR, 0L, - (long)BUFSIZ, False, - (Atom) 31, &actual_type, &actual_format, - &nitems, &leftover, &data); - if (data == NULL) - g_warning (_("AT_SPI_REGISTRY was not started at session startup.")); - - return (gchar *) data; - -} + dbus_message_iter_init_append (message, &iter); + spi_object_append_reference (&iter, app->root); + + if (!dbus_connection_send_with_reply (app->bus, message, &pending, -1) + || !pending) + { + return FALSE; + } + dbus_pending_call_set_notify (pending, register_reply, app, NULL); -static const char * -spi_atk_bridge_get_registry (void) -{ - // TODO: check for registry dying, as the old code attempted to do - return "org.freedesktop.atspi.registry"; -} + if (message) + dbus_message_unref (message); -static const char * -spi_atk_bridge_get_dec (void) -{ - return "/dec"; -} +#ifndef DISABLE_P2P + if (getuid () != 0) + { + app->app_tmp_dir = g_build_filename (g_get_user_runtime_dir (), + "at-spi2-XXXXXX", NULL); + if (!g_mkdtemp (app->app_tmp_dir)) + { + g_free (app->app_tmp_dir); + app->app_tmp_dir = NULL; + return FALSE; + } + } -int -gtk_module_init (gint *argc, gchar **argv[]) -{ - return atk_bridge_init (argc, argv); + if (app->app_tmp_dir) + app->app_bus_addr = g_strdup_printf ("unix:path=%s/socket", app->app_tmp_dir); + else + app->app_bus_addr = g_strdup_printf ("unix:path=%s/at-spi2-socket-%d", + g_get_user_runtime_dir (), getpid ()); +#endif + + return TRUE; } +/*---------------------------------------------------------------------------*/ + static void -add_signal_listener (const char *signal_name) +deregister_application (SpiBridge * app) { - guint id; + DBusMessage *message; + DBusMessageIter iter; + DBusError error; + const char *uname; - id = atk_add_global_event_listener ( - spi_atk_bridge_signal_listener, signal_name); + dbus_error_init (&error); - g_array_append_val (listener_ids, id); -} + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_REGISTRY, + ATSPI_DBUS_INTERFACE_REGISTRY, + "DeregisterApplication"); + dbus_message_set_no_reply (message, TRUE); -static void -spi_atk_register_event_listeners (void) -{ - /* - * kludge to make sure the Atk interface types are registered, otherwise - * the AtkText signal handlers below won't get registered - */ - guint id; - GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL); - AtkObject *bo = atk_no_op_object_new (ao); + uname = dbus_bus_get_unique_name (app->bus); + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &uname); + dbus_connection_send (app->bus, message, NULL); + if (message) + dbus_message_unref (message); - if (atk_listeners_registered) - { - g_object_unref (G_OBJECT (bo)); - g_object_unref (ao); - return; - } + if (app->app_bus_addr) + { + unlink (app->app_bus_addr); + g_free (app->app_bus_addr); + app->app_bus_addr = NULL; + } - atk_listeners_registered = TRUE; - - /* Register for focus event notifications, and register app with central registry */ - - listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16); - - atk_bridge_focus_tracker_id = atk_add_focus_tracker (spi_atk_bridge_focus_tracker); - - id = atk_add_global_event_listener (spi_atk_bridge_property_event_listener, - "Gtk:AtkObject:property-change"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener, - "window:create"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener, - "window:destroy"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener, - "window:minimize"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener, - "window:maximize"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener, - "window:restore"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener, - "window:activate"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener, - "window:deactivate"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_document_event_listener, - "Gtk:AtkDocument:load-complete"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_document_event_listener, - "Gtk:AtkDocument:reload"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_document_event_listener, - "Gtk:AtkDocument:load-stopped"); - g_array_append_val (listener_ids, id); - id = atk_add_global_event_listener (spi_atk_bridge_state_event_listener, - "Gtk:AtkObject:state-change"); - g_array_append_val (listener_ids, id); - - add_signal_listener ("Gtk:AtkObject:children-changed"); - add_signal_listener ("Gtk:AtkObject:visible-data-changed"); - add_signal_listener ("Gtk:AtkObject:active-descendant-changed"); - add_signal_listener ("Gtk:AtkComponent:bounds-changed"); - add_signal_listener ("Gtk:AtkSelection:selection-changed"); - add_signal_listener ("Gtk:AtkText:text-selection-changed"); - add_signal_listener ("Gtk:AtkText:text-changed"); - add_signal_listener ("Gtk:AtkText:text-caret-moved"); - add_signal_listener ("Gtk:AtkTable:row-inserted"); - add_signal_listener ("Gtk:AtkTable:row-reordered"); - add_signal_listener ("Gtk:AtkTable:row-deleted"); - add_signal_listener ("Gtk:AtkTable:column-inserted"); - add_signal_listener ("Gtk:AtkTable:column-reordered"); - add_signal_listener ("Gtk:AtkTable:column-deleted"); - add_signal_listener ("Gtk:AtkTable:model-changed"); - add_signal_listener ("Gtk:AtkHypertext:link-selected"); -/* - * May add the following listeners to implement preemptive key listening for GTK+ - * - * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event"); - * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event"); - */ - atk_bridge_key_event_listener_id = atk_add_key_event_listener ( - spi_atk_bridge_key_listener, NULL); - - g_object_unref (G_OBJECT (bo)); - g_object_unref (ao); + if (app->app_tmp_dir) + { + rmdir (app->app_tmp_dir); + g_free (app->app_tmp_dir); + app->app_tmp_dir = NULL; + } } -static void -deregister_application (SpiAppData *app) +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ + +static AtkPlugClass *plug_class; +static AtkSocketClass *socket_class; + +static gchar * +get_plug_id (AtkPlug * plug) { - const char *registry = spi_atk_bridge_get_registry (); - bridge_threads_leave (); - // todo: deregister - bridge_threads_enter (); + const char *uname = dbus_bus_get_unique_name (spi_global_app_data->bus); + gchar *path; + GString *str = g_string_new (NULL); + + path = spi_register_object_to_path (spi_global_register, G_OBJECT (plug)); + g_string_printf (str, "%s:%s", uname, path); + g_free (path); + return g_string_free (str, FALSE); } -static void -spi_atk_bridge_exit_func (void) +AtkStateSet * +socket_ref_state_set (AtkObject *accessible) { - SpiAppData *app = (SpiAppData *) this_app; + char *child_name, *child_path; + AtkSocket *socket = ATK_SOCKET (accessible); + int count = 0; + int j; + int v; + DBusMessage *message, *reply; + DBusMessageIter iter, iter_array; + AtkStateSet *set; + + set = atk_state_set_new (); - DBG (1, g_message ("exiting bridge\n")); + if (!socket->embedded_plug_id) + return set; - if (!app) + child_name = g_strdup (socket->embedded_plug_id); + if (!child_name) + return set; + child_path = g_utf8_strchr (child_name + 1, -1, ':'); + if (!child_path) { - return; + g_free (child_name); + return set; } - if (atk_bridge_pid != getpid ()) + *(child_path++) = '\0'; + message = dbus_message_new_method_call (child_name, child_path, ATSPI_DBUS_INTERFACE_ACCESSIBLE, "GetState"); + g_free (child_name); + reply = dbus_connection_send_with_reply_and_block (spi_global_app_data->bus, message, 1, NULL); + dbus_message_unref (message); + if (reply == NULL) + return set; + if (strcmp (dbus_message_get_signature (reply), "au") != 0) { - _exit (0); + dbus_message_unref (reply); + return set; } - during_init_shutdown = TRUE; - exiting = TRUE; - /* - * Check whether we still have windows which have not been deleted. - */ - spi_atk_tidy_windows (); - /* - * FIXME: this may be incorrect for apps that do their own bonobo - * shutdown, until we can explicitly shutdown to get the ordering - * right. - */ - if (!registry_died) - deregister_application (this_app); - this_app = NULL; - DBG (1, g_message ("bridge exit func complete.\n")); - - if (g_getenv ("AT_BRIDGE_SHUTDOWN")) + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_array); + do { + dbus_message_iter_get_basic (&iter_array, &v); + for (j = 0; j < 32; j++) + { + if (v & (1 << j)) + { + AtkState state = spi_atk_state_from_spi_state ((count << 5) + j); + atk_state_set_add_state (set, state); + } + } + count++; } - if (bridge_display) - XCloseDisplay (bridge_display); + while (dbus_message_iter_next (&iter_array)); + dbus_message_unref (reply); + return set; } -void -gnome_accessibility_module_init (void) +static void +socket_embed_hook (AtkSocket * socket, gchar * plug_id) { - atk_bridge_init (NULL, NULL); + AtkObject *accessible = ATK_OBJECT(socket); + gchar *plug_name, *plug_path; + AtkObjectClass *klass; - if (g_getenv ("AT_BRIDGE_SHUTDOWN")) - { - g_print("Atk Accessibility bridge initialized\n"); - } -} - -void -gnome_accessibility_module_shutdown (void) -{ - int i; - GArray *ids = listener_ids; - - if (!atk_bridge_initialized) + /* Force registration */ + gchar *path = spi_register_object_to_path (spi_global_register, G_OBJECT (accessible)); + /* Let the plug know that it has been embedded */ + plug_name = g_strdup (plug_id); + if (!plug_name) { + g_free (path); return; } - during_init_shutdown = TRUE; - atk_bridge_initialized = FALSE; - - if (g_getenv ("AT_BRIDGE_SHUTDOWN")) + plug_path = g_utf8_strchr (plug_name + 1, -1, ':'); + if (plug_path) { - g_print("Atk Accessibility bridge shutdown\n"); + DBusMessage *message; + *(plug_path++) = '\0'; + message = dbus_message_new_method_call (plug_name, plug_path, ATSPI_DBUS_INTERFACE_SOCKET, "Embedded"); + dbus_message_append_args (message, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); + dbus_connection_send (spi_global_app_data->bus, message, NULL); } + g_free (plug_name); + g_free (path); - listener_ids = NULL; - if (atk_bridge_focus_tracker_id) - atk_remove_focus_tracker (atk_bridge_focus_tracker_id); - - for (i = 0; ids && i < ids->len; i++) - { - atk_remove_global_event_listener (g_array_index (ids, guint, i)); - } - - if (atk_bridge_key_event_listener_id) - atk_remove_key_event_listener (atk_bridge_key_event_listener_id); + klass = ATK_OBJECT_GET_CLASS (accessible); + klass->ref_state_set = socket_ref_state_set; +} - deregister_application (this_app); - this_app = NULL; +static void +install_plug_hooks () +{ + gpointer data; - misc = NULL; + data = g_type_class_ref (ATK_TYPE_PLUG); + plug_class = ATK_PLUG_CLASS (data); + data = g_type_class_ref (ATK_TYPE_SOCKET); + socket_class = ATK_SOCKET_CLASS (data); + plug_class->get_object_id = get_plug_id; + socket_class->embed = socket_embed_hook; } -static void emit(AtkObject *object, const char *name, const char *detail, dbus_int32_t detail1, dbus_int32_t detail2, int type, const void *val) +static guint +get_ancestral_uid (guint pid) { - DBusMessage *sig; - char *path = spi_dbus_get_path(object); - DBusMessageIter iter, sub; - const char *type_as_string = NULL; - dbus_int32_t dummy = 0; - - spi_dbus_update_cache(&this_app->droute); - if (type == DBUS_TYPE_OBJECT_PATH) - { - type_as_string = "o"; - if (!val) val = ""; - } - else if (type == DBUS_TYPE_STRING) type_as_string = "s"; - else if (type == DBUS_TYPE_INT32) type_as_string = "i"; - else if (type == DBUS_TYPE_UINT32) type_as_string = "u"; - else if (type == DBUS_TYPE_INVALID) - { - type = DBUS_TYPE_UINT32; - type_as_string = "u"; - if (!val) val = &dummy; - } - else + FILE *fp; + char buf [80]; + int ppid = 0; + int uid = 0; + gboolean got_ppid = 0; + gboolean got_uid = 0; + + sprintf (buf, "/proc/%d/status", pid); + fp = fopen (buf, "r"); + if (!fp) + return 0; + while ((!got_ppid || !got_uid) && fgets (buf, sizeof (buf), fp)) { - g_warning("Unknown type %d in property change signal", type); + if (sscanf (buf, "PPid:\t%d", &ppid) == 1) + got_ppid = TRUE; + else if (sscanf (buf, "Uid:\t%d", &uid) == 1) + got_uid = TRUE; } - sig = dbus_message_new_signal(path, "org.freedesktop.atspi.Accessible", name); - dbus_message_iter_init_append(sig, &iter); - if (!detail) detail = ""; - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &detail); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2); - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type_as_string, &sub); - dbus_message_iter_append_basic(&sub, type, &val); - dbus_message_iter_close_container(&iter, &sub); -printf("emit: %s %s\n", name, detail); - dbus_connection_send(this_app->droute.bus, sig, NULL); - g_free(path); - dbus_message_unref(sig); + fclose (fp); + + if (!got_ppid || !got_uid) + return 0; + if (uid != 0) + return uid; + if (ppid == 0 || ppid == 1) + return 0; + return get_ancestral_uid (ppid); } -static void -spi_atk_bridge_focus_tracker (AtkObject *object) +static dbus_bool_t +user_check (DBusConnection *bus, unsigned long uid, void *data) { - emit(object, "focus", NULL, 0, 0, DBUS_TYPE_INVALID, NULL); + if (uid == getuid () || uid == geteuid ()) + return TRUE; + if (getuid () == 0) + return get_ancestral_uid (getpid ()) == uid; + return FALSE; } -static void emit_rect(AtkObject *object, const char *name, const char *detail, AtkRectangle *rect) +static void +new_connection_cb (DBusServer *server, DBusConnection *con, void *data) { - DBusMessage *sig; - char *path = spi_dbus_get_path(object); - DBusMessageIter iter, iter_variant, sub; - dbus_uint32_t x, y, width, height; - dbus_int32_t dummy = 0; - - spi_dbus_update_cache(&this_app->droute); - x = rect->x; - y = rect->y; - width = rect->width; - height = rect->height; - sig = dbus_message_new_signal(path, "org.freedesktop.atspi.Accessible", name); - if (!detail) detail = ""; - if (sig) - { - dbus_message_iter_init_append (sig, &iter); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &detail); - dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy); - dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy); - if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(iiii)", &iter_variant)) - goto oom; - if (!dbus_message_iter_open_container (&iter_variant, DBUS_TYPE_STRUCT, NULL, &sub)) - goto oom; - dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &x); - dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &y); - dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &width); - dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &height); - if (!dbus_message_iter_close_container (&iter_variant, &sub)) - goto oom; - if (!dbus_message_iter_close_container (&iter, &iter_variant)) - goto oom; - } - dbus_connection_send(this_app->droute.bus, sig, NULL); -oom: - g_free(path); - dbus_message_unref(sig); -} + dbus_connection_set_unix_user_function (con, user_check, NULL, NULL); + dbus_connection_ref(con); + atspi_dbus_connection_setup_with_g_main(con, NULL); + droute_intercept_dbus (con); + droute_context_register (spi_global_app_data->droute, con); -static const char *PropertyChange = "object_property_change"; + spi_global_app_data->direct_connections = g_list_append (spi_global_app_data->direct_connections, con); +} -static gboolean -spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +static int +setup_bus (void) { - AtkPropertyValues *values; - AtkObject *obj; - const gchar *prop_name; - const gchar *sp = NULL; - AtkObject *ao; - char *s_ao = NULL; - gint i; - const gchar *name = NULL; - -#ifdef SPI_BRIDGE_DEBUG - GSignalQuery signal_query; - const gchar *signame; - const gchar *s, *s2; - - g_signal_query (signal_hint->signal_id, &signal_query); - signame = signal_query.signal_name; - - s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0))); - s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0))); - values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1); - DBG (2, g_message ("Received (property) signal %s:%s:%s from object %s (gail %s)\n", - g_type_name (signal_query.itype), signame, values->property_name, s, s2)); - +#ifndef DISABLE_P2P + DBusServer *server; + DBusError err; + + if (!spi_global_app_data->app_bus_addr) + return -1; + + dbus_error_init(&err); + server = dbus_server_listen(spi_global_app_data->app_bus_addr, &err); + if (server == NULL) + { + g_warning ("atk-bridge: Couldn't listen on dbus server: %s", err.message); + dbus_error_init (&err); + spi_global_app_data->app_bus_addr [0] = '\0'; + g_main_context_unref (spi_global_app_data->main_context); + spi_global_app_data->main_context = NULL; + return -1; + } + + atspi_dbus_server_setup_with_g_main(server, NULL); + dbus_server_set_new_connection_function(server, new_connection_cb, NULL, NULL); + + spi_global_app_data->server = server; #endif - obj = g_value_get_object (param_values + 0); - name = atk_object_get_name (obj); - values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1); + return 0; +} - prop_name = values->property_name; - if (strcmp (prop_name, "accessible-name") == 0) - { - spi_dbus_notify_change(obj, FALSE, &this_app->droute); - } - else if (strcmp (prop_name, "accessible-description") == 0) - { - spi_dbus_notify_change(obj, FALSE, &this_app->droute); - } - else if (strcmp (prop_name, "accessible-parent") == 0) - { - spi_dbus_notify_change(obj, FALSE, &this_app->droute); - } - else if (strcmp (prop_name, "accessible-table-summary") == 0) - { - ao = atk_table_get_summary (ATK_TABLE (obj)); - s_ao = spi_dbus_get_path(ao); - emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_OBJECT_PATH, s_ao); - } - else if (strcmp (prop_name, "accessible-table-column-header") == 0) - { - i = g_value_get_int (&(values->new_value)); - ao = atk_table_get_column_header (ATK_TABLE (obj), i); - s_ao = spi_dbus_get_path(ao); - emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_OBJECT_PATH, s_ao); - } - else if (strcmp (prop_name, "accessible-table-row-header") == 0) - { - i = g_value_get_int (&(values->new_value)); - ao = atk_table_get_row_header (ATK_TABLE (obj), i); - s_ao = spi_dbus_get_path(ao); - emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_OBJECT_PATH, s_ao); - } - else if (strcmp (prop_name, "accessible-table-row-description") == 0) - { - i = g_value_get_int (&(values->new_value)); - sp = atk_table_get_row_description (ATK_TABLE (obj), i); - emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_STRING, sp); - } - else if (strcmp (prop_name, "accessible-table-column-description") == 0) - { - i = g_value_get_int (&(values->new_value)); - sp = atk_table_get_column_description (ATK_TABLE(obj), i); - emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_STRING, sp); - } - else if (strcmp (prop_name, "accessible-table-caption-object") == 0) - { - ao = atk_table_get_caption (ATK_TABLE(obj)); - sp = atk_object_get_name (ao); - emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_STRING, sp); - } - else + +gchar *atspi_dbus_name = NULL; +static gboolean atspi_no_register = FALSE; + +static GOptionEntry atspi_option_entries[] = { + {"atspi-dbus-name", 0, 0, G_OPTION_ARG_STRING, &atspi_dbus_name, + "D-Bus bus name to register as", NULL}, + {"atspi-no-register", 0, 0, G_OPTION_ARG_NONE, &atspi_no_register, + "Do not register with Registry Daemon", NULL}, + {NULL} +}; + +static gchar * +introspect_children_cb (const char *path, void *data) +{ + if (!strcmp (path, "/org/a11y/atspi/accessible")) { - emit(obj, PropertyChange, prop_name, 0, 0, DBUS_TYPE_INVALID, NULL); + return g_strdup ("\n"); + /* TODO: Should we place the whole hierarchy here? */ } - if (s_ao) g_free(s_ao); - return TRUE; + return NULL; } -static gboolean -spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +static void +handle_event_listener_registered (DBusConnection *bus, DBusMessage *message, + void *user_data) { - AtkObject *obj; - gchar *property_name; - unsigned long detail1; -#ifdef SPI_BRIDGE_DEBUG - GSignalQuery signal_query; - const gchar *name; - - g_signal_query (signal_hint->signal_id, &signal_query); - name = signal_query.signal_name; - fprintf (stderr, "Received (state) signal %s:%s\n", - g_type_name (signal_query.itype), name); -#endif + const char *name; + char *sender; - obj = ATK_OBJECT(g_value_get_object (param_values + 0)); - property_name = g_strdup (g_value_get_string (param_values + 1)); - /* Ignore defunct for now; we'll send a tree update to remove it when - the object goes away */ - /* Also ignore state changes for objects not yet broadcast */ - if ((property_name && !strcmp(property_name, "defunct")) || - !spi_dbus_object_is_known(obj)) - { - g_free(property_name); - return TRUE; - } - detail1 = (g_value_get_boolean (param_values + 2)) - ? 1 : 0; - emit(obj, "object_state_changed", property_name, detail1, 0, DBUS_TYPE_INVALID, NULL); - g_free (property_name); - return TRUE; + if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &sender, + DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) + return; + + add_event (sender, name); } static void -spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke, - AtkKeyEventStruct *event) +remove_events (const char *bus_name, const char *event) { -#ifdef SPI_DEBUG - if (event) - { - g_print ("event %c (%d)\n", (int) event->keyval, (int) event->keycode); - } - else -#endif - if (!event) - { /* this doesn't really need translating */ - g_print (_("WARNING: NULL key event reported.")); - } - - keystroke->id = (dbus_int32_t) event->keyval; - keystroke->hw_code = (dbus_int16_t) event->keycode; - keystroke->timestamp = (dbus_uint32_t) event->timestamp; - keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF); - if (event->string) - { - gunichar c; + gchar **remove_data; + GList *list; - keystroke->event_string = g_strdup (event->string); - c = g_utf8_get_char_validated (event->string, -1); - if (c > 0 && g_unichar_isprint (c)) - keystroke->is_text = TRUE; - else - keystroke->is_text = FALSE; - } - else + remove_data = g_strsplit (event, ":", 3); + if (!remove_data) { - keystroke->event_string = g_strdup (""); - keystroke->is_text = FALSE; + return; } - switch (event->type) + + for (list = spi_global_app_data->events; list;) { - case (ATK_KEY_EVENT_PRESS): - keystroke->type = Accessibility_KEY_PRESSED_EVENT; - break; - case (ATK_KEY_EVENT_RELEASE): - keystroke->type = Accessibility_KEY_RELEASED_EVENT; - break; - default: - keystroke->type = 0; - break; + event_data *evdata = list->data; + if (!g_strcmp0 (evdata->bus_name, bus_name) && + spi_event_is_subtype (evdata->data, remove_data)) + { + GList *events = spi_global_app_data->events; + list = list->next; + g_strfreev (evdata->data); + g_free (evdata->bus_name); + g_free (evdata); + spi_global_app_data->events = g_list_remove (events, evdata); + } + else + { + list = list->next; + } } -#if 0 - g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n", - (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code, - (int) keystroke->modifiers, - keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp); -#endif + + g_strfreev (remove_data); } -static gboolean Accessibility_DeviceEventController_notifyListenersSync(const Accessibility_DeviceEvent *key_event) +static void +handle_event_listener_deregistered (DBusConnection *bus, DBusMessage *message, + void *user_data) { - DBusMessage *message = dbus_message_new_method_call(SPI_DBUS_NAME_REGISTRY, SPI_DBUS_PATH_REGISTRY, "org.freedesktop.atspi.DeviceEventController", "notifyListenersSync"); - DBusError error; - dbus_bool_t consumed = FALSE; + gchar *name; + char *sender; - dbus_error_init(&error); - if (spi_dbus_marshal_deviceEvent(message, key_event)) - { - DBusMessage *reply = dbus_connection_send_with_reply_and_block(this_app->droute.bus, message, 1000, &error); - if (reply) + if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &sender, + DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) + return; + + remove_events (sender, name); +} + +static void +handle_device_listener_registered (DBusConnection *bus, DBusMessage *message, + void *user_data) +{ + char *sender; + DBusMessageIter iter, iter_struct; + + if (strncmp (dbus_message_get_signature (message), "(s", 2) != 0) { - DBusError error; - dbus_error_init(&error); - dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &consumed, DBUS_TYPE_INVALID); - dbus_message_unref(reply); + g_warning ("atk-bridge: handle_device_listener_register: unknown signature"); + return; } - } - dbus_message_unref(message); - return consumed; + + dbus_message_iter_init (message, &iter); + dbus_message_iter_recurse (&iter, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &sender); + spi_atk_add_client (sender); } -static gint -spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data) +static DBusHandlerResult +signal_filter (DBusConnection *bus, DBusMessage *message, void *user_data) { - gboolean result; - Accessibility_DeviceEvent key_event; + const char *interface = dbus_message_get_interface (message); + const char *member = dbus_message_get_member (message); + DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - spi_init_keystroke_from_atk_key_event (&key_event, event); + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - bridge_threads_leave (); - result = Accessibility_DeviceEventController_notifyListenersSync (&key_event); - bridge_threads_enter (); + if (!strcmp (interface, ATSPI_DBUS_INTERFACE_REGISTRY)) + { + result = DBUS_HANDLER_RESULT_HANDLED; + if (!strcmp (member, "EventListenerRegistered")) + handle_event_listener_registered (bus, message, user_data); + else if (!strcmp (member, "EventListenerDeregistered")) + handle_event_listener_deregistered (bus, message, user_data); + else + result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else if (!strcmp (interface, ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER)) + { + result = DBUS_HANDLER_RESULT_HANDLED; + if (!strcmp (member, "KeystrokeListenerRegistered")) + handle_device_listener_registered (bus, message, user_data); + else if (!strcmp (member, "DeviceListenerRegistered")) + handle_device_listener_registered (bus, message, user_data); + else + result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } - if (key_event.event_string) g_free (key_event.event_string); + if (!g_strcmp0(interface, DBUS_INTERFACE_DBUS) && + !g_strcmp0(member, "NameOwnerChanged")) + { + char *name, *old, *new; + result = DBUS_HANDLER_RESULT_HANDLED; + if (dbus_message_get_args (message, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) + { + if (*old != '\0' && *new == '\0') + spi_atk_remove_client (old); + } + } return result; } +/* + * Checks the status of the environment variables + * + * At this moment it only checks NO_AT_BRIDGE + * + * Returns TRUE if there isn't anything on the environment preventing + * you to load the bridge, FALSE otherwise + */ static gboolean -spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +check_envvar (void) { - AtkObject *obj; - GSignalQuery signal_query; - const gchar *name; - const gchar *detail = NULL; - char *sp = NULL; - AtkObject *ao; - gint detail1 = 0, detail2 = 0; - char *s_ao = NULL; - gchar *sig_name; - char *p; -#ifdef SPI_BRIDGE_DEBUG - const gchar *s, *s2; -#endif - - g_signal_query (signal_hint->signal_id, &signal_query); + const gchar *envvar; - name = signal_query.signal_name; - if (signal_hint->detail) - { - detail = g_quark_to_string (signal_hint->detail); - } - sig_name = g_strdup_printf("object_%s", name); - while ((p = strchr(sig_name, '-')) != NULL) *p = '_'; - -#ifdef SPI_BRIDGE_DEBUG - s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0))); - s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0))); - fprintf (stderr, "Received signal %s:%s detail: %s from object %s (gail %s)\n", - g_type_name (signal_query.itype), name, - detail ? detail : "", s ? s : "" , s2); -#endif - - obj = ATK_OBJECT(g_value_get_object (param_values + 0)); + envvar = g_getenv ("NO_AT_BRIDGE"); - if (signal_query.signal_id == atk_signal_active_descendant_changed) - { - gpointer child = g_value_get_pointer (param_values + 1); + if (envvar && atoi (envvar) == 1) + return FALSE; + else + return TRUE; +} - g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE); +/* + * spi_app_init + * + * The following needs to be initialized. + * + * - DRoute for routing message to their accessible objects. + * - Event handlers for emmitting signals on specific ATK events. + * - setup the bus for p2p communication + * - Application registration with the AT-SPI registry. + * + */ +int +atk_bridge_adaptor_init (gint * argc, gchar ** argv[]) +{ + GOptionContext *opt; + GError *err = NULL; + DBusError error; + AtkObject *root; + gboolean load_bridge; + DRoutePath *treepath, *accpath; - ao = ATK_OBJECT (child); + load_bridge = check_envvar (); + if (inited && !load_bridge) + g_warning ("ATK Bridge is disabled but a11y has already been enabled."); - detail1 = atk_object_get_index_in_parent (ao); - s_ao = spi_dbus_get_path(child); - emit(obj, sig_name, detail, detail1, 0, DBUS_TYPE_OBJECT_PATH, s_ao); - g_free(s_ao); - } - else if (signal_query.signal_id == atk_signal_link_selected) + if (inited || !load_bridge) + return 0; + + inited = TRUE; + + root = atk_get_root (); + g_warn_if_fail (root); + if (!root) { - if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT) - detail1 = g_value_get_int (param_values + 1); - emit(obj, sig_name, detail, detail1, 0, DBUS_TYPE_INVALID, NULL); + inited = FALSE; + return -1; } - else if (signal_query.signal_id == atk_signal_bounds_changed) - { - AtkRectangle *atk_rect = NULL; - if (G_VALUE_HOLDS_BOXED (param_values + 1)) - atk_rect = g_value_get_boxed (param_values + 1); - emit_rect(obj, sig_name, detail, atk_rect); - } - else if ((signal_query.signal_id == atk_signal_children_changed) && obj) + /* Parse command line options */ + opt = g_option_context_new (NULL); + g_option_context_add_main_entries (opt, atspi_option_entries, NULL); + g_option_context_set_ignore_unknown_options (opt, TRUE); + if (!g_option_context_parse (opt, argc, argv, &err)) + g_warning ("AT-SPI Option parsing failed: %s\n", err->message); + g_option_context_free (opt); + + /* Allocate global data and do ATK initializations */ + spi_global_app_data = g_new0 (SpiBridge, 1); + spi_global_app_data->root = g_object_ref (root); + + /* Set up D-Bus connection and register bus name */ + dbus_error_init (&error); + spi_global_app_data->bus = atspi_get_a11y_bus (); + if (!spi_global_app_data->bus) { - spi_dbus_notify_change(obj, FALSE, &this_app->droute); + g_free (spi_global_app_data); + spi_global_app_data = NULL; + inited = FALSE; + return -1; } - else - { - if (n_param_values >= 2) - { - if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT) - detail1 = g_value_get_int (param_values + 1); - if (n_param_values >= 3) - { - if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT) - detail2 = g_value_get_int (param_values + 2); - } - } - if (signal_query.signal_id == atk_signal_text_changed) - { - sp = atk_text_get_text (ATK_TEXT (obj), - detail1, - detail1+detail2); - emit(obj, sig_name, detail, detail1, detail2, DBUS_TYPE_STRING, sp); - } - else if (signal_query.signal_id == atk_signal_text_selection_changed) + if (atspi_dbus_name != NULL) + { + if (dbus_bus_request_name + (spi_global_app_data->bus, atspi_dbus_name, 0, &error)) { - /* Return NULL as the selected string */ - emit(obj, sig_name, detail, detail1, detail2, DBUS_TYPE_STRING, ""); + g_print ("AT-SPI Recieved D-Bus name - %s\n", atspi_dbus_name); } else { - emit(obj, sig_name, detail, 0, 0, DBUS_TYPE_INVALID, NULL); + g_print + ("AT-SPI D-Bus name requested but could not be allocated - %s\n", + atspi_dbus_name); } } - if (sp) - g_free (sp); + spi_global_app_data->main_context = g_main_context_new (); - if (s_ao) - g_free (s_ao); - g_free(sig_name); + atspi_dbus_connection_setup_with_g_main (spi_global_app_data->bus, NULL); - return TRUE; + /* Hook our plug-and socket functions */ + install_plug_hooks (); + + /* + * Create the leasing, register and cache objects. + * The order is important here, the cache depends on the + * register object. + */ + spi_global_register = g_object_new (SPI_REGISTER_TYPE, NULL); + spi_global_leasing = g_object_new (SPI_LEASING_TYPE, NULL); + spi_global_cache = g_object_new (SPI_CACHE_TYPE, NULL); + + /* Register droute for routing AT-SPI messages */ + spi_global_app_data->droute = + droute_new (); + + treepath = droute_add_one (spi_global_app_data->droute, + "/org/a11y/atspi/cache", spi_global_cache); + + if (!treepath) + { + g_warning ("atk-bridge: Error in droute_add_one(). Already running?"); + return -1; + } + + accpath = droute_add_many (spi_global_app_data->droute, + "/org/a11y/atspi/accessible", + NULL, + introspect_children_cb, + NULL, + (DRouteGetDatumFunction) + spi_global_register_path_to_object); + + + /* Register all interfaces with droute and set up application accessible db */ + spi_initialize_cache (treepath); + spi_initialize_accessible (accpath); + spi_initialize_application (accpath); + spi_initialize_action (accpath); + spi_initialize_collection (accpath); + spi_initialize_component (accpath); + spi_initialize_document (accpath); + spi_initialize_editabletext (accpath); + spi_initialize_hyperlink (accpath); + spi_initialize_hypertext (accpath); + spi_initialize_image (accpath); + spi_initialize_selection (accpath); + spi_initialize_socket (accpath); + spi_initialize_table (accpath); + spi_initialize_text (accpath); + spi_initialize_value (accpath); + + droute_context_register (spi_global_app_data->droute, + spi_global_app_data->bus); + + /* Register methods to send D-Bus signals on certain ATK events */ + if (clients) + spi_atk_register_event_listeners (); + + /* Set up filter and match rules to catch signals */ + dbus_bus_add_match (spi_global_app_data->bus, "type='signal', interface='org.a11y.atspi.Registry', sender='org.a11y.atspi.Registry'", NULL); + dbus_bus_add_match (spi_global_app_data->bus, "type='signal', interface='org.a11y.atspi.DeviceEventListener', sender='org.a11y.atspi.Registry'", NULL); + dbus_connection_add_filter (spi_global_app_data->bus, signal_filter, NULL, + NULL); + + /* Register this app by sending a signal out to AT-SPI registry daemon */ + if (!atspi_no_register && (!root || !ATK_IS_PLUG (root))) + register_application (spi_global_app_data); + else + get_registered_event_listeners (spi_global_app_data); + + setup_bus(); + + return 0; } -static gboolean -spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +void +atk_bridge_adaptor_cleanup (void) { - AtkObject *obj; - GSignalQuery signal_query; - const gchar *name, *s; -#ifdef SPI_BRIDGE_DEBUG - const gchar *s2; -#endif - - g_signal_query (signal_hint->signal_id, &signal_query); + GList *l; + GSList *ls; - name = signal_query.signal_name; - gchar *sig_name; + g_return_if_fail (inited); -#ifdef SPI_BRIDGE_DEBUG - s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0))); - s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0))); - fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n", - g_type_name (signal_query.itype), name, s ? s : "" , s2); -#endif - - obj = ATK_OBJECT(g_value_get_object (param_values + 0)); + if (!spi_global_app_data) + return; - s = atk_object_get_name (obj); - sig_name = g_strdup_printf("window_%s", name); - emit(obj, sig_name, NULL, 0, 0, DBUS_TYPE_STRING, s); - g_free(sig_name); + spi_atk_tidy_windows (); + spi_atk_deregister_event_listeners (); - return TRUE; -} + deregister_application (spi_global_app_data); -static gboolean -spi_atk_bridge_document_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) -{ - AtkObject *obj; - GSignalQuery signal_query; - const gchar *name, *s; - gchar *sig_name; -#ifdef SPI_BRIDGE_DEBUG - const gchar *s2; -#endif + if (spi_global_app_data->bus) + { + dbus_connection_remove_filter (spi_global_app_data->bus, signal_filter, NULL); + droute_context_unregister (spi_global_app_data->droute, spi_global_app_data->bus); + dbus_connection_unref (spi_global_app_data->bus); + } - g_signal_query (signal_hint->signal_id, &signal_query); + for (l = spi_global_app_data->direct_connections; l; l = l->next) + { + DBusConnection *connection; - name = signal_query.signal_name; + connection = l->data; -#ifdef SPI_BRIDGE_DEBUG - s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0))); - s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0))); - fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n", - g_type_name (signal_query.itype), name, s ? s : "" , s2); -#endif + droute_context_unregister (spi_global_app_data->droute, connection); + droute_unintercept_dbus (connection); + dbus_connection_unref (connection); + } + g_list_free (spi_global_app_data->direct_connections); - obj = ATK_OBJECT(g_value_get_object (param_values + 0)); + for (ls = clients; ls; ls = ls->next) + g_free (l->data); + g_slist_free (clients); + clients = NULL; - s = atk_object_get_name (obj); - sig_name = g_strdup_printf("document_%s", name); - emit(obj, sig_name, NULL, 0, 0, DBUS_TYPE_STRING, s); - g_free(sig_name); - return TRUE; + g_object_unref (spi_global_cache); + g_object_unref (spi_global_leasing); + g_object_unref (spi_global_register); + + if (spi_global_app_data->main_context) + g_main_context_unref (spi_global_app_data->main_context); + + droute_free (spi_global_app_data->droute); + + g_free (spi_global_app_data); + spi_global_app_data = NULL; + + inited = FALSE; } -static void -spi_atk_tidy_windows (void) -{ - AtkObject *root; - gint n_children; - gint i; +/*---------------------------------------------------------------------------*/ - root = atk_get_root (); - n_children = atk_object_get_n_accessible_children (root); - for (i = 0; i < n_children; i++) - { - AtkObject *child; - AtkStateSet *stateset; - const gchar *name; - - child = atk_object_ref_accessible_child (root, i); - stateset = atk_object_ref_state_set (child); - - name = atk_object_get_name (child); - if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE)) - { - emit(child, "window:deactivate", NULL, 0, 0, DBUS_TYPE_STRING, name); - } - g_object_unref (stateset); +static gchar *name_match_tmpl = + "type='signal', interface='org.freedesktop.DBus', member='NameOwnerChanged', arg0='%s'"; - emit(child, "window:destroy", NULL, 0, 0, DBUS_TYPE_STRING, name); - g_object_unref (child); - } +void +spi_atk_add_client (const char *bus_name) +{ + GSList *l; + gchar *match; + + for (l = clients; l; l = l->next) + { + if (!g_strcmp0 (l->data, bus_name)) + return; + } + if (!clients) + spi_atk_register_event_listeners (); + clients = g_slist_append (clients, g_strdup (bus_name)); + match = g_strdup_printf (name_match_tmpl, bus_name); + dbus_bus_add_match (spi_global_app_data->bus, match, NULL); + g_free (match); } -static void -reinit_register_vars (void) +void +spi_atk_remove_client (const char *bus_name) { - registry = NULL; - device_event_controller = NULL; - this_app = NULL; + GSList *l; + GSList *next_node; + + l = clients; + while (l) + { + next_node = l->next; + + if (!g_strcmp0 (l->data, bus_name)) + { + gchar *match = g_strdup_printf (name_match_tmpl, l->data); + dbus_bus_remove_match (spi_global_app_data->bus, match, NULL); + g_free (match); + g_free (l->data); + clients = g_slist_delete_link (clients, l); + if (!clients) + spi_atk_deregister_event_listeners (); + } + + l = next_node; + } } + +/*END------------------------------------------------------------------------*/