X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=atk-bridge%2Fbridge.c;h=ec6ddd074913a334afbf3088f7bd2ea24c8b9cd6;hb=cd5ac1cc95dd1dce2f8dc6aa617c9dd0d71a4291;hp=7ebc04624113a525345fad39b777788dae79f1b3;hpb=01182029872a450a5770ef9a8155763e44d5b1e8;p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git diff --git a/atk-bridge/bridge.c b/atk-bridge/bridge.c index 7ebc046..ec6ddd0 100644 --- a/atk-bridge/bridge.c +++ b/atk-bridge/bridge.c @@ -2,7 +2,8 @@ * AT-SPI - Assistive Technology Service Provider Interface * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) * - * Copyright 2001 Sun Microsystems Inc. + * Copyright 2001, 2002, 2003 Sun Microsystems Inc., + * Copyright 2001, 2002, 2003 Ximian, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,40 +21,95 @@ * Boston, MA 02111-1307, USA. */ +#include "config.h" +#include "dbus/dbus-glib.h" + +#include +#include +#include #include +#include #include #include -#include -#include #include #include #include -#include #include "accessible.h" -#include "application.h" - -#include #undef SPI_BRIDGE_DEBUG -static CORBA_Environment ev; -static Accessibility_Registry registry; -static SpiApplication *this_app = NULL; +#define DBG(a,b) if(_dbg>=(a))b + +#define bridge_threads_leave() \ + if (!during_init_shutdown && !g_main_context_is_owner (NULL)) atk_misc_threads_leave(misc); +#define bridge_threads_enter() \ + if (!during_init_shutdown && !g_main_context_is_owner (NULL)) atk_misc_threads_enter(misc); + +typedef struct _SpiAppData SpiAppData; +struct _SpiAppData +{ + AtkObject *root; + DRouteData droute; +}; + +int _dbg = 0; +static const char *registry = NULL; +static char *device_event_controller = NULL; +static SpiAppData *this_app = NULL; +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 gboolean spi_atk_bridge_idle_init (gpointer user_data); 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, @@ -64,15 +120,18 @@ static gboolean spi_atk_bridge_signal_listener (GSignalInvocationHint *s gpointer data); static gint spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data); +static void spi_atk_tidy_windows (void); +static void deregister_application (); +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 guint idle_init_id = 0; static GArray *listener_ids = NULL; /* @@ -82,84 +141,295 @@ static GArray *listener_ids = NULL; extern void gnome_accessibility_module_init (void); extern void gnome_accessibility_module_shutdown (void); +static void +spi_atk_bridge_init_event_type_consts () +{ + static gboolean done = FALSE; + + if (done) + return; + + 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; +} + +static gboolean +post_init (void) +{ + during_init_shutdown = FALSE; + return FALSE; +} + +static DBusObjectPathVTable droute_vtable = +{ + NULL, + &droute_message, + NULL, NULL, NULL, NULL +}; + +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) + { + g_warning("Couldn't connect to dbus: %s\n", error.message); + free(ad); + return NULL; + } + //dbus_connection_set_exit_on_disconnect(ad->droute.bus, FALSE); + //dbus_bus_register(ad->droute.bus, &error); + spi_dbus_initialize (&ad->droute); + /* Below line for testing -- it should be removed once at-spi-registryd is working */ + if (dbus_bus_request_name(ad->droute.bus, "test.atspi.tree", 0, &error)) printf("Got test name.\n"); + spi_register_tree_object(ad->droute.bus, "/org/freedesktop/atspi/tree"); + if (!dbus_connection_try_register_fallback (ad->droute.bus, "/org/freedesktop/atspi/accessible/", &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[]) { - CORBA_Environment ev; + const char *debug_env_string = g_getenv ("AT_SPI_DEBUG"); + gchar *fname; + gboolean success = FALSE; if (atk_bridge_initialized) { return 0; } atk_bridge_initialized = TRUE; + atk_bridge_pid = getpid (); - if (!bonobo_init (argc, argv ? *argv : NULL)) + misc = atk_misc_get_instance(); + + if (g_getenv ("ATK_BRIDGE_REDIRECT_LOG")) { - g_error ("Could not initialize Bonobo"); + 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); } + if (debug_env_string) + _dbg = (int) g_ascii_strtod (debug_env_string, NULL); + + /* Connect to dbus */ + this_app = spi_app_init (atk_get_root ()); + /* - * We only want to enable the bridge for top level + * 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 ()) - return 0; - - CORBA_exception_init(&ev); - - registry = bonobo_activation_activate_from_id ( - "OAFIID:Accessibility_Registry:proto0.1", 0, NULL, &ev); - - if (ev._major != CORBA_NO_EXCEPTION) +#else + if (0) +#endif { - g_error ("Accessibility app error: exception during " - "registry activation from id: %s\n", - CORBA_exception_id (&ev)); - CORBA_exception_free (&ev); + 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); } - - if (registry == CORBA_OBJECT_NIL) + else { - g_error ("Could not locate registry"); + 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) + { + spi_atk_register_event_listeners (); + spi_atk_bridge_init_event_type_consts (); + } + else + { + atk_bridge_initialized = FALSE; + } + g_idle_add (post_init, NULL); + + return 0; +} - bonobo_activate (); +static gboolean +spi_atk_bridge_do_registration (void) +{ + if (spi_atk_bridge_get_registry () == NULL) + { + g_warning ("Could not locate registry"); + return FALSE; + } /* Create the accessible application server object */ + if (this_app == NULL) + this_app = spi_app_init (atk_get_root ()); - this_app = spi_application_new (atk_get_root ()); + DBG (1, g_message ("About to register application\n")); - fprintf (stderr, "About to register application\n"); + spi_atk_bridge_register_application (spi_atk_bridge_get_registry ()); + + g_atexit (spi_atk_bridge_exit_func); - Accessibility_Registry_registerApplication (registry, - BONOBO_OBJREF (this_app), - &ev); + DBG (1, g_message ("Application registered & listening\n")); + return TRUE; +} - g_atexit (spi_atk_bridge_exit_func); +static void +spi_atk_bridge_toplevel_added (AtkObject *object, + guint index, + AtkObject *child) +{ + if (toplevels == 0) + { + spi_atk_bridge_do_registration (); + } + toplevels++; +} - idle_init_id = g_idle_add (spi_atk_bridge_idle_init, NULL); +static void +spi_atk_bridge_toplevel_removed (AtkObject *object, + guint index, + AtkObject *child) +{ + toplevels--; + if (toplevels == 0) + { + deregister_application (this_app); + reinit_register_vars (); + } + if (toplevels < 0) + { + g_warning ("More toplevels removed than added\n"); + toplevels = 0; + } +} - return 0; +static void +spi_atk_bridge_register_application (const char *registry) +{ + bridge_threads_leave (); + // TODO: register + bridge_threads_enter (); } -int -gtk_module_init (gint *argc, gchar **argv[]) +/* + * Returns a 'canonicalized' value for DISPLAY, + * with the screen number stripped off if present. + */ +static const gchar* +spi_display_name (void) { - return atk_bridge_init (argc, argv); + 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; } -static gboolean -spi_atk_bridge_idle_init (gpointer user_data) +static Display *bridge_display = NULL; + +static gchar * +spi_atk_bridge_get_registry_ior (void) { - idle_init_id = 0; + + 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; + +} - spi_atk_register_event_listeners (); - fprintf (stderr, "Application registered & listening\n"); +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"; +} - return FALSE; +static const char * +spi_atk_bridget_get_dec (void) +{ + return "/dec"; +} + +int +gtk_module_init (gint *argc, gchar **argv[]) +{ + return atk_bridge_init (argc, argv); } static void @@ -183,7 +453,17 @@ spi_atk_register_event_listeners (void) guint id; GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL); AtkObject *bo = atk_no_op_object_new (ao); - + + + if (atk_listeners_registered) + { + g_object_unref (G_OBJECT (bo)); + g_object_unref (ao); + return; + } + + atk_listeners_registered = TRUE; + /* Register for focus event notifications, and register app with central registry */ listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16); @@ -214,12 +494,23 @@ spi_atk_register_event_listeners (void) 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"); @@ -231,6 +522,7 @@ spi_atk_register_event_listeners (void) 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+ * @@ -245,49 +537,51 @@ spi_atk_register_event_listeners (void) } static void -deregister_application (BonoboObject *app) +deregister_application (SpiAppData *app) { - Accessibility_Registry_deregisterApplication ( - registry, BONOBO_OBJREF (app), &ev); - - registry = bonobo_object_release_unref (registry, &ev); - - app = bonobo_object_unref (app); + const char *registry = spi_atk_bridge_get_registry (); + bridge_threads_leave (); + // todo: deregister + bridge_threads_enter (); } static void spi_atk_bridge_exit_func (void) { - BonoboObject *app = (BonoboObject *) this_app; + SpiAppData *app = (SpiAppData *) this_app; - fprintf (stderr, "exiting bridge\n"); + DBG (1, g_message ("exiting bridge\n")); if (!app) { return; } - this_app = NULL; + if (atk_bridge_pid != getpid ()) + { + _exit (0); + } + 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 (!bonobo_is_initialized ()) - { - fprintf (stderr, "Re-initializing bonobo\n"); - g_assert (bonobo_init (0, NULL)); - g_assert (bonobo_activate ()); - } - - deregister_application (app); - - fprintf (stderr, "bridge exit func complete.\n"); + 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")) { - g_assert (!bonobo_debug_shutdown ()); } + if (bridge_display) + XCloseDisplay (bridge_display); } void @@ -295,121 +589,135 @@ gnome_accessibility_module_init (void) { atk_bridge_init (NULL, NULL); - g_print("Atk Accessibilty bridge initialized\n"); + if (g_getenv ("AT_BRIDGE_SHUTDOWN")) + { + g_print("Atk Accessibility bridge initialized\n"); + } } void gnome_accessibility_module_shutdown (void) { - BonoboObject *app = (BonoboObject *) this_app; - + int i; + GArray *ids = listener_ids; + if (!atk_bridge_initialized) { return; } + during_init_shutdown = TRUE; atk_bridge_initialized = FALSE; - this_app = NULL; - - g_print("Atk Accessibilty bridge shutdown\n"); - if (idle_init_id) + if (g_getenv ("AT_BRIDGE_SHUTDOWN")) { - g_source_remove (idle_init_id); - idle_init_id = 0; + g_print("Atk Accessibility bridge shutdown\n"); } - else - { - int i; - GArray *ids = listener_ids; - listener_ids = NULL; - atk_remove_focus_tracker (atk_bridge_focus_tracker_id); - - for (i = 0; ids && i < ids->len; i++) - { + 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)); - } - - atk_remove_key_event_listener (atk_bridge_key_event_listener_id); } + + if (atk_bridge_key_event_listener_id) + atk_remove_key_event_listener (atk_bridge_key_event_listener_id); + + deregister_application (this_app); + this_app = NULL; + + misc = NULL; +} - deregister_application (app); +static void emit(AtkObject *object, const char *name, int first_arg_type, ...) +{ + va_list args; + DBusMessage *sig; + char *path = spi_dbus_get_path(object); + + spi_dbus_update_cache(&this_app->droute); + sig = dbus_message_new_signal(path, "org.freedesktop.atspi.Accessible", name); + va_start(args, first_arg_type); + if (first_arg_type != DBUS_TYPE_INVALID) + { + dbus_message_append_args_valist(sig, first_arg_type, args); + } + va_end(args); + dbus_connection_send(this_app->droute.bus, sig, NULL); + g_free(path); + dbus_message_unref(sig); } static void spi_atk_bridge_focus_tracker (AtkObject *object) { - SpiAccessible *source; - Accessibility_Event e; - - source = spi_accessible_new (object); - - e.type = "focus:"; - e.source = BONOBO_OBJREF (source); - e.detail1 = 0; - e.detail2 = 0; - - Accessibility_Registry_notifyEvent (registry, &e, &ev); - - CORBA_exception_free (&ev); + emit(object, "focus", DBUS_TYPE_INVALID); } -static void -spi_atk_emit_eventv (GObject *gobject, - unsigned long detail1, - unsigned long detail2, - const char *format, ...) -{ - va_list args; - Accessibility_Event e; - SpiAccessible *source; - AtkObject *aobject; -#ifdef SPI_BRIDGE_DEBUG - CORBA_string s; -#endif - - va_start (args, format); - - if (ATK_IS_IMPLEMENTOR (gobject)) - { - aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject)); - source = spi_accessible_new (aobject); - g_object_unref (G_OBJECT (aobject)); - } - else if (ATK_IS_OBJECT (gobject)) - { - aobject = ATK_OBJECT (gobject); - source = spi_accessible_new (aobject); - } +static void emit_property_change(AtkObject *object, const char *name, int type, void *val) +{ + DBusMessage *sig; + char *path = spi_dbus_get_path(object); + DBusMessageIter iter, sub; + const char *type_as_string = NULL; + + 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 = "o"; else - { - aobject = NULL; - source = NULL; - g_error ("received property-change event from non-AtkImplementor"); - } + { + g_warning("Unknown type %d in property change signal", type); + } + sig = dbus_message_new_signal(path, "org.freedesktop.atspi.Accessible", "PropertyChanged"); + dbus_message_iter_init_append(sig, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); + dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type_as_string, &sub); + dbus_message_iter_append_basic(&sub, type, val); + dbus_message_iter_close_container(&iter, &sub); + dbus_connection_send(this_app->droute.bus, sig, NULL); + g_free(path); + dbus_message_unref(sig); +} - if (source != NULL) +static void emit_rect(AtkObject *object, const char *name, AtkRectangle *rect) +{ + DBusMessage *sig; + char *path = spi_dbus_get_path(object); + DBusMessageIter iter, sub; + dbus_uint32_t x, y, width, height; + + 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", "PropertyChanged"); + if (sig) { - e.type = g_strdup_vprintf (format, args); - e.source = BONOBO_OBJREF (source); - e.detail1 = detail1; - e.detail2 = detail2; - -#ifdef SPI_BRIDGE_DEBUG - s = Accessibility_Accessible__get_name (BONOBO_OBJREF (source), &ev); - g_warning ("Emitting event '%s' (%lu, %lu) on %s", - e.type, e.detail1, e.detail2, s); - CORBA_free (s); -#endif - - Accessibility_Registry_notifyEvent (registry, &e, &ev); - - CORBA_exception_free (&ev); - - g_free (e.type); + dbus_message_iter_init_append (sig, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); + if (!dbus_message_iter_open_container + (&iter, 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, &sub)) + goto oom; } - - va_end (args); + dbus_connection_send(this_app->droute.bus, sig, NULL); +oom: + g_free(path); + dbus_message_unref(sig); } static gboolean @@ -419,29 +727,91 @@ spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint, gpointer data) { AtkPropertyValues *values; - GObject *gobject; + 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 *name; + const gchar *signame; const gchar *s, *s2; g_signal_query (signal_hint->signal_id, &signal_query); - name = signal_query.signal_name; + 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); - fprintf (stderr, "Received (property) signal %s:%s:%s from object %s (gail %s)\n", - g_type_name (signal_query.itype), name, values->property_name, s, s2); + DBG (2, g_message ("Received (property) signal %s:%s:%s from object %s (gail %s)\n", + g_type_name (signal_query.itype), signame, values->property_name, s, s2)); #endif - gobject = g_value_get_object (param_values + 0); + obj = g_value_get_object (param_values + 0); + name = atk_object_get_name (obj); values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1); - spi_atk_emit_eventv (gobject, 0, 0, "object:property-change:%s", values->property_name); - + 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_property_change(obj, prop_name, 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_property_change(obj, prop_name, 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_property_change(obj, prop_name, 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_property_change(obj, prop_name, DBUS_TYPE_STRING, (void *)&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_property_change(obj, prop_name, DBUS_TYPE_STRING, (void *)&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_property_change(obj, prop_name, DBUS_TYPE_STRING, (void *)&sp); + } + else + { + long v = 0; + emit_property_change(obj, prop_name, DBUS_TYPE_INT32, &v); + } + if (s_ao) g_free(s_ao); return TRUE; } @@ -451,9 +821,8 @@ spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint, const GValue *param_values, gpointer data) { - GObject *gobject; + AtkObject *obj; gchar *property_name; - gchar *type; unsigned long detail1; #ifdef SPI_BRIDGE_DEBUG GSignalQuery signal_query; @@ -465,21 +834,25 @@ spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint, g_type_name (signal_query.itype), name); #endif - gobject = g_value_get_object (param_values + 0); + 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; + } detail1 = (g_value_get_boolean (param_values + 2)) ? 1 : 0; - type = g_strdup_printf ("object:state-change:%s", property_name); - spi_atk_emit_eventv (gobject, - detail1, - 0, - type); + emit(obj, "StateChanged", DBUS_TYPE_STRING, &property_name, DBUS_TYPE_UINT32, &detail1, DBUS_TYPE_INVALID); g_free (property_name); - g_free (type); return TRUE; } - +#if 0 static void spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke, AtkKeyEventStruct *event) @@ -492,8 +865,8 @@ spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke, else #endif if (!event) - { - g_print ("WARNING: NULL key event!"); + { /* this doesn't really need translating */ + g_print (_("WARNING: NULL key event reported.")); } keystroke->id = (CORBA_long) event->keyval; @@ -502,8 +875,14 @@ spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke, keystroke->modifiers = (CORBA_unsigned_short) (event->state & 0xFFFF); if (event->string) { + gunichar c; + keystroke->event_string = CORBA_string_dup (event->string); - keystroke->is_text = CORBA_TRUE; + c = g_utf8_get_char_validated (event->string, -1); + if (c > 0 && g_unichar_isprint (c)) + keystroke->is_text = CORBA_TRUE; + else + keystroke->is_text = CORBA_FALSE; } else { @@ -513,10 +892,10 @@ spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke, switch (event->type) { case (ATK_KEY_EVENT_PRESS): - keystroke->type = Accessibility_KEY_PRESSED; + keystroke->type = Accessibility_KEY_PRESSED_EVENT; break; case (ATK_KEY_EVENT_RELEASE): - keystroke->type = Accessibility_KEY_RELEASED; + keystroke->type = Accessibility_KEY_RELEASED_EVENT; break; default: keystroke->type = 0; @@ -529,32 +908,33 @@ spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke, keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp); #endif } +#endif static gint spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data) { - CORBA_boolean result; + gboolean result; +#if 0 Accessibility_DeviceEvent key_event; - Accessibility_DeviceEventController controller = - Accessibility_Registry_getDeviceEventController (registry, &ev); - if (BONOBO_EX (&ev)) - { - g_warning ("failure: no deviceeventcontroller found\n"); - CORBA_exception_free (&ev); - result = FALSE; - } - else - { + CORBA_exception_init (&ev); + + spi_init_keystroke_from_atk_key_event (&key_event, event); - spi_init_keystroke_from_atk_key_event (&key_event, event); + bridge_threads_leave (); + result = Accessibility_DeviceEventController_notifyListenersSync ( + spi_atk_bridget_get_dec (), &key_event, &ev); + bridge_threads_enter (); - result = Accessibility_DeviceEventController_notifyListenersSync ( - controller, &key_event, &ev); + if (key_event.event_string) CORBA_free (key_event.event_string); + if (BONOBO_EX(&ev)) + { + result = FALSE; CORBA_exception_free (&ev); } +#endif return result; } @@ -564,54 +944,135 @@ spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint, const GValue *param_values, gpointer data) { - GObject *gobject; + AtkObject *obj; GSignalQuery signal_query; const gchar *name; + const gchar *detail; + 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 +#endif g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; + if (signal_hint->detail) + { + detail = g_quark_to_string (signal_hint->detail); + sig_name = g_strdup_printf("object_%s_%s", name, detail); + } + else + { + detail = NULL; + 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 from object %s (gail %s)\n", - g_type_name (signal_query.itype), name, s ? s : "" , s2); + 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 - - gobject = g_value_get_object (param_values + 0); - if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT) - detail1 = g_value_get_int (param_values + 1); - if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT) - detail2 = g_value_get_int (param_values + 2); - spi_atk_emit_eventv (gobject, detail1, detail2, "object:%s", name); + obj = ATK_OBJECT(g_value_get_object (param_values + 0)); - return TRUE; -} + if (signal_query.signal_id == atk_signal_active_descendant_changed) + { + gpointer child = g_value_get_pointer (param_values + 1); + g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE); + ao = ATK_OBJECT (child); + + detail1 = atk_object_get_index_in_parent (ao); + s_ao = spi_dbus_get_path(child); + emit(obj, "name", DBUS_TYPE_OBJECT_PATH, s_ao, DBUS_TYPE_UINT32, &detail1, DBUS_TYPE_INVALID); + g_free(s_ao); + } + else if (signal_query.signal_id == atk_signal_link_selected) + { + if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT) + detail1 = g_value_get_int (param_values + 1); + emit(obj, "LinkSelected", DBUS_TYPE_UINT32, &detail1, DBUS_TYPE_INVALID); + } + 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, "BoundsChanged", atk_rect); + } + else if ((signal_query.signal_id == atk_signal_children_changed) && obj) + { + spi_dbus_notify_change(obj, FALSE, &this_app->droute); + } + else + { + if (n_param_values >= 2) + { + if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT) + detail1 = g_value_get_int (param_values + 1); + if (n_param_values >= 3) + { + if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT) + detail2 = g_value_get_int (param_values + 2); + } + } + + if (signal_query.signal_id == atk_signal_text_changed) + { + sp = atk_text_get_text (ATK_TEXT (obj), + detail1, + detail1+detail2); + emit(obj, sig_name, DBUS_TYPE_UINT32, &detail1, DBUS_TYPE_UINT32, &detail2, DBUS_TYPE_STRING, &sp, DBUS_TYPE_INVALID); + } + else if (signal_query.signal_id == atk_signal_text_selection_changed) + { + /* Return NULL as the selected string */ + // TODO + emit(obj, sig_name, DBUS_TYPE_INVALID); + } + else + { + emit(obj, sig_name, DBUS_TYPE_INVALID); + } + } + + if (sp) + g_free (sp); + + if (s_ao) + g_free (s_ao); + g_free(sig_name); + + return TRUE; +} static gboolean spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) + guint n_param_values, + const GValue *param_values, + gpointer data) { - GObject *gobject; + AtkObject *obj; GSignalQuery signal_query; - const gchar *name; + const gchar *name, *s; #ifdef SPI_BRIDGE_DEBUG - const gchar *s, *s2; + const gchar *s2; #endif g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; + gchar *sig_name; #ifdef SPI_BRIDGE_DEBUG s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0))); @@ -619,9 +1080,85 @@ spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint, 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)); - gobject = g_value_get_object (param_values + 0); - spi_atk_emit_eventv (gobject, 0, 0, "window:%s", name); + s = atk_object_get_name (obj); + sig_name = g_strdup_printf("window_%s", name); + emit(obj, sig_name, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID); + g_free(sig_name); return TRUE; } + +static gboolean +spi_atk_bridge_document_event_listener (GSignalInvocationHint *signal_hint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + AtkObject *obj; + GSignalQuery signal_query; + const gchar *name, *s; + gchar *sig_name; +#ifdef SPI_BRIDGE_DEBUG + const gchar *s2; +#endif + + g_signal_query (signal_hint->signal_id, &signal_query); + + name = signal_query.signal_name; + +#ifdef SPI_BRIDGE_DEBUG + s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0))); + s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0))); + fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n", + g_type_name (signal_query.itype), name, s ? s : "" , s2); +#endif + + obj = ATK_OBJECT(g_value_get_object (param_values + 0)); + + s = atk_object_get_name (obj); + sig_name = g_strdup_printf("document_%s", name); + emit(obj, sig_name, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID); + g_free(sig_name); + return TRUE; +} + +static void +spi_atk_tidy_windows (void) +{ + AtkObject *root; + gint n_children; + gint i; + + root = atk_get_root (); + n_children = atk_object_get_n_accessible_children (root); + for (i = 0; i < n_children; i++) + { + AtkObject *child; + AtkStateSet *stateset; + const gchar *name; + + child = atk_object_ref_accessible_child (root, i); + stateset = atk_object_ref_state_set (child); + + name = atk_object_get_name (child); + if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE)) + { + emit(child, "window:deactivate", DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); + } + g_object_unref (stateset); + + emit(child, "window:destroy", DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); + g_object_unref (child); + } +} + +static void +reinit_register_vars (void) +{ + registry = NULL; + device_event_controller = NULL; + this_app = NULL; +}