X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git;a=blobdiff_plain;f=atk-adaptor%2Fevent.c;h=586d19630e6e6271ddf282a5101d3a429366f845;hp=6e26b93ee14f259d087cdb0f0b9210c9c680fdf7;hb=51b5abcea9ee948d11aeb5d6062186f3d4f5ee59;hpb=0048e28f72f43986719392255a055fbb7d6224dd diff --git a/atk-adaptor/event.c b/atk-adaptor/event.c index 6e26b93..586d196 100644 --- a/atk-adaptor/event.c +++ b/atk-adaptor/event.c @@ -2,6 +2,7 @@ * AT-SPI - Assistive Technology Service Provider Interface * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) * + * Copyright 2011, F123 Consulting & Mais Diferenças * Copyright 2008, 2009, Codethink Ltd. * Copyright 2001, 2002, 2003 Sun Microsystems Inc., * Copyright 2001, 2002, 2003 Ximian, Inc. @@ -23,14 +24,18 @@ */ #include +#include #include #include +#include #include "bridge.h" #include "accessible-register.h" -#include "common/spi-dbus.h" +#include "spi-dbus.h" +#include "event.h" +#include "object.h" static GArray *listener_ids = NULL; @@ -39,74 +44,136 @@ static gint atk_bridge_focus_tracker_id; /*---------------------------------------------------------------------------*/ -#define ITF_EVENT_OBJECT "org.freedesktop.atspi.Event.Object" -#define ITF_EVENT_WINDOW "org.freedesktop.atspi.Event.Window" -#define ITF_EVENT_DOCUMENT "org.freedesktop.atspi.Event.Document" -#define ITF_EVENT_FOCUS "org.freedesktop.atspi.Event.Focus" +#define ITF_EVENT_OBJECT "org.a11y.atspi.Event.Object" +#define ITF_EVENT_WINDOW "org.a11y.atspi.Event.Window" +#define ITF_EVENT_DOCUMENT "org.a11y.atspi.Event.Document" +#define ITF_EVENT_FOCUS "org.a11y.atspi.Event.Focus" /*---------------------------------------------------------------------------*/ +typedef struct _SpiReentrantCallClosure +{ + DBusConnection *bus; + GMainLoop *loop; + DBusMessage *reply; + guint timeout; +} SpiReentrantCallClosure; + static void -set_reply (DBusPendingCall *pending, void *user_data) +switch_main_context (GMainContext *cnx) { - void **replyptr = (void **)user_data; + GList *list; - *replyptr = dbus_pending_call_steal_reply (pending); +#ifndef DISABLE_P2P + atspi_dbus_server_setup_with_g_main (spi_global_app_data->server, cnx); +#endif + atspi_dbus_connection_setup_with_g_main (spi_global_app_data->bus, cnx); + for (list = spi_global_app_data->direct_connections; list; list = list->next) + atspi_dbus_connection_setup_with_g_main (list->data, cnx); } -static DBusMessage * -send_and_allow_reentry (DBusConnection *bus, DBusMessage *message) +static void +set_reply (DBusPendingCall * pending, void *user_data) { - DBusPendingCall *pending; - DBusMessage *reply = NULL; + SpiReentrantCallClosure* closure = (SpiReentrantCallClosure *) user_data; - if (!dbus_connection_send_with_reply (bus, message, &pending, -1)) - { - return NULL; - } - dbus_pending_call_set_notify (pending, set_reply, (void *)&reply, NULL); - while (!reply) + closure->reply = dbus_pending_call_steal_reply (pending); + dbus_pending_call_unref (pending); + switch_main_context (NULL); + g_main_loop_quit (closure->loop); +} + +static gboolean +timeout_reply (void *data) +{ + SpiReentrantCallClosure *closure = data; + + if (!dbus_connection_get_is_connected (closure->bus)) + g_main_loop_quit (closure->loop); + closure->timeout = -1; + return FALSE; +} + +static DBusMessage * +send_and_allow_reentry (DBusConnection * bus, DBusMessage * message) +{ + DBusPendingCall *pending; + SpiReentrantCallClosure closure; + GMainContext *main_context; + GSource *source; + + main_context = (g_getenv ("AT_SPI_CLIENT") ? NULL : + spi_global_app_data->main_context); + closure.bus = bus; + closure.loop = g_main_loop_new (main_context, FALSE); + closure.reply = NULL; + switch_main_context (main_context); + + if (!dbus_connection_send_with_reply (bus, message, &pending, 9000) || !pending) { - if (!dbus_connection_read_write_dispatch (bus, -1)) return NULL; + switch_main_context (NULL); + return NULL; } - return reply; + dbus_pending_call_set_notify (pending, set_reply, (void *) &closure, NULL); + source = g_timeout_source_new (500); + g_source_set_callback (source, timeout_reply, &closure, NULL); + closure.timeout = g_source_attach (source, main_context); + g_source_unref (source); + g_main_loop_run (closure.loop); + if (closure.timeout != -1) + g_source_destroy (source); + + g_main_loop_unref (closure.loop); + return closure.reply; } +/*---------------------------------------------------------------------------*/ + +/* + * Functionality related to sending device events from the application. + * + * This is used for forwarding key events on to the registry daemon. + */ + static gboolean -Accessibility_DeviceEventController_NotifyListenersSync(const Accessibility_DeviceEvent *key_event) +Accessibility_DeviceEventController_NotifyListenersSync (const + AtspiDeviceEvent + * key_event) { DBusMessage *message; DBusError error; dbus_bool_t consumed = FALSE; message = - dbus_message_new_method_call(SPI_DBUS_NAME_REGISTRY, - SPI_DBUS_PATH_DEC, - SPI_DBUS_INTERFACE_DEC, - "NotifyListenersSync"); + dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_DEC, + ATSPI_DBUS_INTERFACE_DEC, + "NotifyListenersSync"); - dbus_error_init(&error); - if (spi_dbus_marshal_deviceEvent(message, key_event)) - { - DBusMessage *reply = send_and_allow_reentry (atk_adaptor_app_data->bus, message); - if (reply) + dbus_error_init (&error); + if (spi_dbus_marshal_deviceEvent (message, key_event)) { - DBusError error; - dbus_error_init(&error); - dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &consumed, DBUS_TYPE_INVALID); - dbus_message_unref(reply); + DBusMessage *reply = + send_and_allow_reentry (spi_global_app_data->bus, message); + if (reply) + { + DBusError error; + dbus_error_init (&error); + dbus_message_get_args (reply, &error, DBUS_TYPE_BOOLEAN, &consumed, + DBUS_TYPE_INVALID); + dbus_message_unref (reply); + } } - } - dbus_message_unref(message); + dbus_message_unref (message); return consumed; } static void -spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke, - AtkKeyEventStruct *event) +spi_init_keystroke_from_atk_key_event (AtspiDeviceEvent * keystroke, + AtkKeyEventStruct * event) { - keystroke->id = (dbus_int32_t) event->keyval; - keystroke->hw_code = (dbus_int16_t) event->keycode; + 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) @@ -128,55 +195,130 @@ spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke, switch (event->type) { case (ATK_KEY_EVENT_PRESS): - keystroke->type = Accessibility_KEY_PRESSED_EVENT; + keystroke->type = ATSPI_KEY_PRESSED; break; case (ATK_KEY_EVENT_RELEASE): - keystroke->type = Accessibility_KEY_RELEASED_EVENT; + keystroke->type = ATSPI_KEY_RELEASED; break; default: keystroke->type = 0; break; } -#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); +#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 } static gint -spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data) +spi_atk_bridge_key_listener (AtkKeyEventStruct * event, gpointer data) { - gboolean result; - Accessibility_DeviceEvent key_event; + gboolean result; + AtspiDeviceEvent key_event; spi_init_keystroke_from_atk_key_event (&key_event, event); - result = Accessibility_DeviceEventController_NotifyListenersSync (&key_event); + result = + Accessibility_DeviceEventController_NotifyListenersSync (&key_event); - if (key_event.event_string) g_free (key_event.event_string); + if (key_event.event_string) + g_free (key_event.event_string); return result; } - /*---------------------------------------------------------------------------*/ -/* - * Emits an AT-SPI event. - * AT-SPI events names are split into three parts: - * class:major:minor - * This is mapped onto D-Bus events as: - * D-Bus Interface:Signal Name:Detail argument - * - * Marshals a basic type into the 'any_data' attribute of - * the AT-SPI event. - */ +static gchar * +convert_signal_name (const gchar * s) +{ + gchar *ret = g_strdup (s); + gchar *t; + + if (!ret) + return NULL; + ret[0] = toupper (ret[0]); + while ((t = strchr (ret, '-')) != NULL) + { + memmove (t, t + 1, strlen (t)); + *t = toupper (*t); + } + return ret; +} + +static const void * +validate_for_dbus (const gint type, + const void *val) +{ + switch (type) + { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + if (!val) + return ""; + else if (!g_utf8_validate (val, -1, NULL)) + { + g_warning ("atk-bridge: Received bad UTF-8 string when emitting event"); + return ""; + } + else + return val; + default: + return val; + } +} + +static void +append_basic (DBusMessageIter *iter, + const char *type, + const void *val) +{ + DBusMessageIter sub; + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &sub); + + val = validate_for_dbus ((int) *type, val); + dbus_message_iter_append_basic(&sub, (int) *type, &val); + + dbus_message_iter_close_container(iter, &sub); +} + +static void +append_rect (DBusMessageIter *iter, + const char *type, + const void *val) +{ + DBusMessageIter variant, sub; + const AtkRectangle *rect = (const AtkRectangle *) val; + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &variant); + + dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub); + + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x)); + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y)); + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width)); + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height)); + + dbus_message_iter_close_container (&variant, &sub); + + dbus_message_iter_close_container(iter, &variant); +} + +static void +append_object (DBusMessageIter *iter, + const char *type, + const void *val) +{ + spi_object_append_v_reference (iter, ATK_OBJECT (val)); +} static gchar * -DBusSignalName (const gchar *s) +signal_name_to_dbus (const gchar *s) { gchar *ret = g_strdup (s); gchar *t; @@ -192,102 +334,155 @@ DBusSignalName (const gchar *s) return ret; } -static void -emit(AtkObject *accessible, - const char *klass, - const char *major, - const char *minor, - dbus_int32_t detail1, - dbus_int32_t detail2, - const char *type, - const void *val) -{ - gchar *path; - gchar *cname; - - /* TODO this is a hack, used becuase child-added events are not guaranteed. - * On recieving an event from a non-registered object we check if it can be safely - * registered before sending the event. - */ - path = atk_dbus_object_attempt_registration (accessible); +/* + * Converts names of the form "active-descendant-changed" to + * "ActiveDescendantChanged" + */ +static gchar * +ensure_proper_format (const char *name) +{ + gchar *ret = (gchar *) g_malloc (strlen (name) * 2 + 2); + gchar *p = ret; + gboolean need_upper = TRUE; - /* Tough decision here - * We won't send events from accessible - * objects that have not yet been added to the accessible tree. - */ - if (path == NULL) + if (!ret) + return NULL; + while (*name) + { + if (need_upper) + { + *p++ = toupper (*name); + need_upper = FALSE; + } + else if (*name == '-') + need_upper = TRUE; + else if (*name == ':') + { + need_upper = TRUE; + *p++ = *name; + } + else + *p++ = *name; + name++; + } + *p = '\0'; + return ret; +} + +static gboolean +signal_is_needed (const gchar *klass, const gchar *major, const gchar *minor) +{ + gchar *data [4]; + GList *iter; + event_data *evdata; + gboolean ret = FALSE; + GList *list; + + if (!spi_global_app_data->events_initialized) + return TRUE; + + data [0] = ensure_proper_format (klass + 21); + data [1] = ensure_proper_format (major); + data [2] = ensure_proper_format (minor); + data [3] = NULL; + + /* Hack: Always pass events that update the cache. + * TODO: FOr 2.2, have at-spi2-core define a special "cache listener" for + * this instead, so that we don't send these if no one is listening */ + if (!g_strcmp0 (data [1], "ChildrenChanged") || + !g_strcmp0 (data [1], "PropertyChange") || + !g_strcmp0 (data [1], "StateChanged")) { -#ifdef SPI_ATK_DEBUG - g_debug ("AT-SPI: Event recieved from non-registered object"); -#endif - return; + g_free (data [2]); + g_free (data [1]); + g_free (data [0]); + return TRUE; } - cname = DBusSignalName (major); - spi_dbus_emit_signal (atk_adaptor_app_data->bus, path, klass, cname, minor, detail1, detail2, type, val); - g_free (cname); - g_free(path); -} + /* Hack: events such as "object::text-changed::insert:system" as + generated by Gecko */ + data [2][strcspn (data [2], ":")] = '\0'; + for (list = spi_global_app_data->events; list; list = list->next) + { + evdata = list->data; + if (spi_event_is_subtype (data, evdata->data)) + { + ret = TRUE; + break; + } + } -/*---------------------------------------------------------------------------*/ +#if 0 + g_print("event: %s %s %s: %d\n", data[0], data[1], data[2], ret); +#endif + g_free (data [2]); + g_free (data [1]); + g_free (data [0]); + return ret; +} /* - * Emits an AT-SPI event, marshalling a BoundingBox structure into the - * 'any_data' variant of the event. + * Emits an AT-SPI event. + * AT-SPI events names are split into three parts: + * class:major:minor + * This is mapped onto D-Bus events as: + * D-Bus Interface:Signal Name:Detail argument + * + * Marshals a basic type into the 'any_data' attribute of + * the AT-SPI event. */ -static void -emit_rect(AtkObject *accessible, - const char *klass, - const char *major, - const char *minor, - AtkRectangle *rect) +static void +emit_event (AtkObject *obj, + const char *klass, + const char *major, + const char *minor, + dbus_int32_t detail1, + dbus_int32_t detail2, + const char *type, + const void *val, + void (*append_variant) (DBusMessageIter *, const char *, const void *)) { - DBusMessage *sig; - DBusMessageIter iter, variant, sub; - gchar *path, *cname; - dbus_int32_t dummy = 0; - - path = atk_dbus_object_to_path (accessible, FALSE); - - /* Tough decision here - * We won't send events from accessible - * objects that have not yet been added to the accessible tree. - */ - if (path == NULL) - return; + DBusConnection *bus = spi_global_app_data->bus; + const char *path; + gchar *cname, *t; + DBusMessage *sig; + DBusMessageIter iter, iter_struct; + if (!klass) klass = ""; if (!major) major = ""; if (!minor) minor = ""; + if (!type) type = "u"; + + if (!signal_is_needed (klass, major, minor)) + return; + + path = spi_register_object_to_path (spi_global_register, G_OBJECT (obj)); /* * This is very annoying, but as '-' isn't a legal signal * name in D-Bus (Why not??!?) The names need converting * on this side, and again on the client side. */ - cname = DBusSignalName (major); - + cname = signal_name_to_dbus (major); sig = dbus_message_new_signal(path, klass, cname); - g_free(path); - g_free(cname); - dbus_message_iter_init_append (sig, &iter); + dbus_message_iter_init_append(sig, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor); - dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy); - dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2); + append_variant (&iter, type, val); + spi_object_append_reference (&iter, spi_global_app_data->root); - dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(iiii)", &variant); - dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub); - dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x)); - dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y)); - dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width)); - dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height)); - dbus_message_iter_close_container (&variant, &sub); - dbus_message_iter_close_container (&iter, &variant); + dbus_connection_send(bus, sig, NULL); + dbus_message_unref(sig); - dbus_connection_send(atk_adaptor_app_data->bus, sig, NULL); + if (g_strcmp0 (cname, "ChildrenChanged") != 0) + spi_object_lease_if_needed (G_OBJECT (obj)); - dbus_message_unref (sig); + g_free(cname); + g_free (path); } /*---------------------------------------------------------------------------*/ @@ -297,14 +492,15 @@ emit_rect(AtkObject *accessible, * as the AT-SPI event, 'focus:' */ static void -focus_tracker (AtkObject *accessible) +focus_tracker (AtkObject * accessible) { - emit(accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0); + emit_event (accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); } /*---------------------------------------------------------------------------*/ -#define PCHANGE "property-change" +#define PCHANGE "PropertyChange" /* * This handler handles the following ATK signals and @@ -315,10 +511,9 @@ focus_tracker (AtkObject *accessible) * The property-name is part of the ATK property-change signal. */ static gboolean -property_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +property_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) { AtkObject *accessible; AtkPropertyValues *values; @@ -326,68 +521,91 @@ property_event_listener (GSignalInvocationHint *signal_hint, const gchar *pname = NULL; AtkObject *otemp; - const gchar *stemp; + const gchar *s1, s2; gint i; accessible = g_value_get_object (¶m_values[0]); - values = (AtkPropertyValues*) g_value_get_pointer (¶m_values[1]); + values = (AtkPropertyValues *) g_value_get_pointer (¶m_values[1]); pname = values[0].property_name; - if (strcmp (pname, "accessible-name") == 0 || - strcmp (pname, "accessible-description") == 0 || - strcmp (pname, "accessible-role") == 0 || - strcmp (pname, "accessible-parent") == 0) - { - return TRUE; - } /* TODO Could improve this control statement by matching * on only the end of the signal names, */ - if (strcmp (pname, "accessible-table-summary") == 0) + if (strcmp (pname, "accessible-name") == 0) + { + s1 = atk_object_get_name (accessible); + if (s1 != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_STRING_AS_STRING, s1, append_basic); + } + else if (strcmp (pname, "accessible-description") == 0) { - otemp = atk_table_get_summary(ATK_TABLE (accessible)); - stemp = atk_dbus_object_to_path (otemp, FALSE); - if (stemp != NULL) - emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp); + s1 = atk_object_get_description (accessible); + if (s1 != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_STRING_AS_STRING, s1, append_basic); + } + else if (strcmp (pname, "accessible-parent") == 0) + { + otemp = atk_object_get_parent (accessible); + if (otemp != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); + } + else if (strcmp (pname, "accessible-role") == 0) + { + i = atk_object_get_role (accessible); + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_UINT32_AS_STRING, GINT_TO_POINTER(i), append_basic); + } + else if (strcmp (pname, "accessible-table-summary") == 0) + { + otemp = atk_table_get_summary (ATK_TABLE (accessible)); + if (otemp != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); } else if (strcmp (pname, "accessible-table-column-header") == 0) { i = g_value_get_int (&(values->new_value)); - otemp = atk_table_get_column_header(ATK_TABLE (accessible), i); - stemp = atk_dbus_object_to_path (otemp, FALSE); - if (stemp != NULL) - emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp); + otemp = atk_table_get_column_header (ATK_TABLE (accessible), i); + if (otemp != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); } else if (strcmp (pname, "accessible-table-row-header") == 0) { i = g_value_get_int (&(values->new_value)); - otemp = atk_table_get_row_header(ATK_TABLE (accessible), i); - stemp = atk_dbus_object_to_path (otemp, FALSE); - if (stemp != NULL) - emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp); + otemp = atk_table_get_row_header (ATK_TABLE (accessible), i); + if (otemp != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); } else if (strcmp (pname, "accessible-table-row-description") == 0) { i = g_value_get_int (&(values->new_value)); - stemp = atk_table_get_row_description(ATK_TABLE (accessible), i); - emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp); + s1 = atk_table_get_row_description (ATK_TABLE (accessible), i); + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_STRING_AS_STRING, s1, append_basic); } else if (strcmp (pname, "accessible-table-column-description") == 0) { i = g_value_get_int (&(values->new_value)); - stemp = atk_table_get_column_description(ATK_TABLE (accessible), i); - emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp); + s1 = atk_table_get_column_description (ATK_TABLE (accessible), i); + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_STRING_AS_STRING, s1, append_basic); } else if (strcmp (pname, "accessible-table-caption-object") == 0) { - otemp = atk_table_get_caption(ATK_TABLE(accessible)); - stemp = atk_object_get_name(otemp); - emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp); + otemp = atk_table_get_caption (ATK_TABLE (accessible)); + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); } else { - emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_INT32_AS_STRING, 0); + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); } return TRUE; } @@ -402,24 +620,24 @@ property_event_listener (GSignalInvocationHint *signal_hint, * the param-name is part of the ATK state-change signal. */ static gboolean -state_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +state_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) { AtkObject *accessible; gchar *pname; guint detail1; - accessible = ATK_OBJECT(g_value_get_object (¶m_values[0])); - pname = g_strdup (g_value_get_string (¶m_values[1])); + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + pname = g_value_get_string (¶m_values[1]); - /* TODO - Possibly ignore a change to the 'defunct' state. - * This is because without reference counting defunct objects should be removed. - */ detail1 = (g_value_get_boolean (¶m_values[2])) ? 1 : 0; - emit(accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0); - g_free (pname); + emit_event (accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); + + if (!g_strcmp0 (pname, "defunct")) + spi_register_deregister_object (spi_global_register, G_OBJECT (accessible), + TRUE); return TRUE; } @@ -437,21 +655,21 @@ state_event_listener (GSignalInvocationHint *signal_hint, * window:deactivate -> window:deactivate */ static gboolean -window_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +window_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) { AtkObject *accessible; GSignalQuery signal_query; const gchar *name, *s; - + g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; - accessible = ATK_OBJECT(g_value_get_object(¶m_values[0])); + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); s = atk_object_get_name (accessible); - emit(accessible, ITF_EVENT_WINDOW, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s); + emit_event (accessible, ITF_EVENT_WINDOW, name, "", 0, 0, + DBUS_TYPE_STRING_AS_STRING, s, append_basic); return TRUE; } @@ -467,10 +685,9 @@ window_event_listener (GSignalInvocationHint *signal_hint, * Gtk:AtkDocument:reload -> document:reload */ static gboolean -document_event_listener (GSignalInvocationHint *signal_hint, +document_event_listener (GSignalInvocationHint * signal_hint, guint n_param_values, - const GValue *param_values, - gpointer data) + const GValue * param_values, gpointer data) { AtkObject *accessible; GSignalQuery signal_query; @@ -479,9 +696,10 @@ document_event_listener (GSignalInvocationHint *signal_hint, g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; - accessible = ATK_OBJECT(g_value_get_object(¶m_values[0])); + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); s = atk_object_get_name (accessible); - emit(accessible, ITF_EVENT_DOCUMENT, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s); + emit_event (accessible, ITF_EVENT_DOCUMENT, name, "", 0, 0, + DBUS_TYPE_STRING_AS_STRING, s, append_basic); return TRUE; } @@ -493,10 +711,9 @@ document_event_listener (GSignalInvocationHint *signal_hint, * this to an AT-SPI event - "object:bounds-changed". */ static gboolean -bounds_event_listener (GSignalInvocationHint *signal_hint, +bounds_event_listener (GSignalInvocationHint * signal_hint, guint n_param_values, - const GValue *param_values, - gpointer data) + const GValue * param_values, gpointer data) { AtkObject *accessible; AtkRectangle *atk_rect; @@ -506,12 +723,15 @@ bounds_event_listener (GSignalInvocationHint *signal_hint, g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; - accessible = ATK_OBJECT(g_value_get_object(¶m_values[0])); + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); if (G_VALUE_HOLDS_BOXED (param_values + 1)) + { atk_rect = g_value_get_boxed (param_values + 1); - emit_rect(accessible, ITF_EVENT_OBJECT, name, "", atk_rect); + emit_event (accessible, ITF_EVENT_OBJECT, name, "", 0, 0, + "(iiii)", atk_rect, append_rect); + } return TRUE; } @@ -523,36 +743,28 @@ bounds_event_listener (GSignalInvocationHint *signal_hint, * */ static gboolean -active_descendant_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +active_descendant_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) { AtkObject *accessible; AtkObject *child; GSignalQuery signal_query; const gchar *name, *minor; - gchar *s; gint detail1; g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; - accessible = ATK_OBJECT(g_value_get_object(¶m_values[0])); - child = ATK_OBJECT(g_value_get_pointer (¶m_values[1])); + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + child = ATK_OBJECT (g_value_get_pointer (¶m_values[1])); g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE); minor = g_quark_to_string (signal_hint->detail); detail1 = atk_object_get_index_in_parent (child); - s = atk_dbus_object_to_path (child, FALSE); - if (s == NULL) - { - g_free (s); - return TRUE; - } - emit(accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, s); - g_free(s); + emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, + "(so)", child, append_object); return TRUE; } @@ -564,26 +776,26 @@ active_descendant_event_listener (GSignalInvocationHint *signal_hint, * */ static gboolean -link_selected_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +link_selected_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) { AtkObject *accessible; GSignalQuery signal_query; const gchar *name, *minor; - gint detail1; + gint detail1 = 0; g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; - accessible = ATK_OBJECT(g_value_get_object(¶m_values[0])); + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); minor = g_quark_to_string (signal_hint->detail); if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) - detail1 = g_value_get_int (¶m_values[1]); + detail1 = g_value_get_int (¶m_values[1]); - emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0); + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); return TRUE; } @@ -592,38 +804,141 @@ link_selected_event_listener (GSignalInvocationHint *signal_hint, /* * Handles the ATK signal 'Gtk:AtkText:text-changed' and * converts it to the AT-SPI signal - 'object:text-changed' + * This signal is deprecated by Gtk:AtkText:text-insert + * and Gtk:AtkText:text-remove * */ static gboolean -text_changed_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +text_changed_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) { AtkObject *accessible; GSignalQuery signal_query; const gchar *name, *minor; gchar *selected; - gint detail1, detail2; + gint detail1 = 0, detail2 = 0; g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; - accessible = ATK_OBJECT(g_value_get_object(¶m_values[0])); + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + minor = g_quark_to_string (signal_hint->detail); + + if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) + detail2 = g_value_get_int (¶m_values[2]); + + selected = + atk_text_get_text (ATK_TEXT (accessible), detail1, detail1 + detail2); + + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + DBUS_TYPE_STRING_AS_STRING, selected, append_basic); + g_free (selected); + + return TRUE; +} + +/* + * Handles the ATK signal 'Gtk:AtkText:text-insert' and + * converts it to the AT-SPI signal - 'object:text-changed' + * + */ +static gboolean +text_insert_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + guint text_changed_signal_id; + GSignalQuery signal_query; + const gchar *name; + gchar *minor, *text; + gint detail1 = 0, detail2 = 0; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + /* Get signal name for 'Gtk:AtkText:text-changed' so + * we convert it to the AT-SPI signal - 'object:text-changed' + */ + text_changed_signal_id = g_signal_lookup ("text-changed", G_OBJECT_TYPE (accessible)); + g_signal_query (text_changed_signal_id, &signal_query); + name = signal_query.signal_name; + + + /* Add the insert and keep any detail coming from atk */ + minor = g_quark_to_string (signal_hint->detail); + if (minor) + minor = g_strconcat ("insert:", minor, NULL); + else + minor = g_strdup ("insert"); + + if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) + detail2 = g_value_get_int (¶m_values[2]); + + if (G_VALUE_TYPE (¶m_values[3]) == G_TYPE_STRING) + text = g_value_get_string (¶m_values[3]); + + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + DBUS_TYPE_STRING_AS_STRING, text, append_basic); + g_free (minor); + return TRUE; +} + +/* + * Handles the ATK signal 'Gtk:AtkText:text-remove' and + * converts it to the AT-SPI signal - 'object:text-changed' + * + */ +static gboolean +text_remove_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + guint text_changed_signal_id; + GSignalQuery signal_query; + const gchar *name; + gchar *minor, *text; + gint detail1 = 0, detail2 = 0; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + /* Get signal name for 'Gtk:AtkText:text-changed' so + * we convert it to the AT-SPI signal - 'object:text-changed' + */ + text_changed_signal_id = g_signal_lookup ("text-changed", G_OBJECT_TYPE (accessible)); + g_signal_query (text_changed_signal_id, &signal_query); + name = signal_query.signal_name; + minor = g_quark_to_string (signal_hint->detail); + /* Add the delete and keep any detail coming from atk */ + minor = g_quark_to_string (signal_hint->detail); + if (minor) + minor = g_strconcat ("delete:", minor, NULL); + else + minor = g_strdup ("delete"); + if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) - detail1 = g_value_get_int (¶m_values[1]); + detail1 = g_value_get_int (¶m_values[1]); if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) - detail2 = g_value_get_int (¶m_values[2]); + detail2 = g_value_get_int (¶m_values[2]); - selected = atk_text_get_text (ATK_TEXT (accessible), detail1, detail1+detail2); + if (G_VALUE_TYPE (¶m_values[3]) == G_TYPE_STRING) + text = g_value_get_string (¶m_values[3]); - emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, selected); + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + DBUS_TYPE_STRING_AS_STRING, text, append_basic); + g_free (minor); return TRUE; } + /*---------------------------------------------------------------------------*/ /* @@ -632,38 +947,112 @@ text_changed_event_listener (GSignalInvocationHint *signal_hint, * */ static gboolean -text_selection_changed_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +text_selection_changed_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, + gpointer data) { AtkObject *accessible; GSignalQuery signal_query; const gchar *name, *minor; - gint detail1, detail2; + gint detail1 = 0, detail2 = 0; g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; - accessible = ATK_OBJECT(g_value_get_object(¶m_values[0])); + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); minor = g_quark_to_string (signal_hint->detail); if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) - detail1 = g_value_get_int (¶m_values[1]); + detail1 = g_value_get_int (¶m_values[1]); if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) - detail2 = g_value_get_int (¶m_values[2]); + detail2 = g_value_get_int (¶m_values[2]); + + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + DBUS_TYPE_STRING_AS_STRING, "", append_basic); + return TRUE; +} - emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, ""); +/*---------------------------------------------------------------------------*/ + +/* + * Children changed signal converter and forwarder. + * + * Klass (Interface) org.a11y.atspi.Event.Object + * Major is the signal name. + * Minor is 'add' or 'remove' + * detail1 is the index. + * detail2 is 0. + * any_data is the child reference. + */ +static gboolean +children_changed_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + GSignalQuery signal_query; + const gchar *name, *minor; + gint detail1 = 0, detail2 = 0; + + AtkObject *accessible, *ao=NULL; + gpointer child; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + minor = g_quark_to_string (signal_hint->detail); + + detail1 = g_value_get_uint (param_values + 1); + child = g_value_get_pointer (param_values + 2); + + if (ATK_IS_OBJECT (child)) + { + ao = ATK_OBJECT (child); + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + "(so)", ao, append_object); + } + else if ((minor != NULL) && (strcmp (minor, "add") == 0)) + { + ao = atk_object_ref_accessible_child (accessible, + detail1); + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + "(so)", ao, append_object); + } + else + { + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + "(so)", ao, append_object); + } + return TRUE; } /*---------------------------------------------------------------------------*/ +static void +toplevel_added_event_listener (AtkObject * accessible, + guint index, AtkObject * child) +{ + emit_event (accessible, ITF_EVENT_OBJECT, "children-changed", "add", index, 0, + "(so)", child, append_object); +} + +static void +toplevel_removed_event_listener (AtkObject * accessible, + guint index, AtkObject * child) +{ + emit_event (accessible, ITF_EVENT_OBJECT, "children-changed", "remove", index, 0, + "(so)", child, append_object); +} + +/*---------------------------------------------------------------------------*/ + /* * Generic signal converter and forwarder. * - * Klass (Interface) org.freedesktop.atspi.Event.Object + * Klass (Interface) org.a11y.atspi.Event.Object * Major is the signal name. * Minor is NULL. * detail1 is 0. @@ -671,20 +1060,28 @@ text_selection_changed_event_listener (GSignalInvocationHint *signal_hint, * any_data is NULL. */ static gboolean -generic_event_listener (GSignalInvocationHint *signal_hint, - guint n_param_values, - const GValue *param_values, - gpointer data) +generic_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) { AtkObject *accessible; GSignalQuery signal_query; const gchar *name; + int detail1 = 0, detail2 = 0; g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; - accessible = ATK_OBJECT(g_value_get_object(¶m_values[0])); - emit(accessible, ITF_EVENT_OBJECT, name, "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0); + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + + if (n_param_values > 1 && G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + if (n_param_values > 2 && G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) + detail2 = g_value_get_int (¶m_values[2]); + + emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, detail2, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); return TRUE; } @@ -695,13 +1092,17 @@ generic_event_listener (GSignalInvocationHint *signal_hint, * and stores the signal id returned so that the function may be * de-registered later. */ -static void +static guint add_signal_listener (GSignalEmissionHook listener, const char *signal_name) { guint id; id = atk_add_global_event_listener (listener, signal_name); - g_array_append_val (listener_ids, id); + + if (id > 0) /* id == 0 is a failure */ + g_array_append_val (listener_ids, id); + + return id; } /* @@ -716,47 +1117,107 @@ 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 */ - GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL); + GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL); AtkObject *bo = atk_no_op_object_new (ao); + guint id = 0; g_object_unref (G_OBJECT (bo)); g_object_unref (ao); + if (listener_ids) + { + g_warning ("atk_bridge: spi_atk-register_event_listeners called multiple times"); + return; + } + /* 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 (focus_tracker); - add_signal_listener (property_event_listener, "Gtk:AtkObject:property-change"); - add_signal_listener (window_event_listener, "window:create"); - add_signal_listener (window_event_listener, "window:destroy"); - add_signal_listener (window_event_listener, "window:minimize"); - add_signal_listener (window_event_listener, "window:maximize"); - add_signal_listener (window_event_listener, "window:restore"); - add_signal_listener (window_event_listener, "window:activate"); - add_signal_listener (window_event_listener, "window:deactivate"); - add_signal_listener (document_event_listener, "Gtk:AtkDocument:load-complete"); - add_signal_listener (document_event_listener, "Gtk:AtkDocument:reload"); - add_signal_listener (document_event_listener, "Gtk:AtkDocument:load-stopped"); + add_signal_listener (property_event_listener, + "Gtk:AtkObject:property-change"); + + /* window events: we tentative try to register using the old format */ + id = add_signal_listener (window_event_listener, "window:create"); + + if (id != 0) + { + /* If we are able to register using the old format, we assume + * that the ATK implementor is managing window events without + * AtkWindow. We can't use the opposite test because after + * including AtkWindow on ATK you would be able to register to + * that event, although the ATK implementor could or not use it. + */ + + add_signal_listener (window_event_listener, "window:destroy"); + add_signal_listener (window_event_listener, "window:minimize"); + add_signal_listener (window_event_listener, "window:maximize"); + add_signal_listener (window_event_listener, "window:restore"); + add_signal_listener (window_event_listener, "window:activate"); + add_signal_listener (window_event_listener, "window:deactivate"); + } + else + { + add_signal_listener (window_event_listener, "Atk:AtkWindow:create"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:destroy"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:minimize"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:maximize"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:restore"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:activate"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:deactivate"); + } + + add_signal_listener (document_event_listener, + "Gtk:AtkDocument:load-complete"); + add_signal_listener (document_event_listener, "Gtk:AtkDocument:reload"); + add_signal_listener (document_event_listener, + "Gtk:AtkDocument:load-stopped"); /* TODO Fake this event on the client side */ - add_signal_listener (state_event_listener, "Gtk:AtkObject:state-change"); + add_signal_listener (state_event_listener, "Gtk:AtkObject:state-change"); /* TODO */ - add_signal_listener (active_descendant_event_listener, "Gtk:AtkObject:active-descendant-changed"); - add_signal_listener (bounds_event_listener, "Gtk:AtkComponent:bounds-changed"); - add_signal_listener (text_selection_changed_event_listener, "Gtk:AtkText:text-selection-changed"); - add_signal_listener (text_changed_event_listener, "Gtk:AtkText:text-changed"); - add_signal_listener (link_selected_event_listener, "Gtk:AtkHypertext:link-selected"); - add_signal_listener (generic_event_listener, "Gtk:AtkObject:visible-data-changed"); - add_signal_listener (generic_event_listener, "Gtk:AtkSelection:selection-changed"); - add_signal_listener (generic_event_listener, "Gtk:AtkText:text-attributes-changed"); - add_signal_listener (generic_event_listener, "Gtk:AtkText:text-caret-moved"); - add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-inserted"); - add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-reordered"); - add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-deleted"); - add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-inserted"); - add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-reordered"); - add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-deleted"); - add_signal_listener (generic_event_listener, "Gtk:AtkTable:model-changed"); + add_signal_listener (active_descendant_event_listener, + "Gtk:AtkObject:active-descendant-changed"); + add_signal_listener (bounds_event_listener, + "Gtk:AtkComponent:bounds-changed"); + add_signal_listener (text_selection_changed_event_listener, + "Gtk:AtkText:text-selection-changed"); + add_signal_listener (text_changed_event_listener, + "Gtk:AtkText:text-changed"); + add_signal_listener (text_insert_event_listener, + "Gtk:AtkText:text-insert"); + add_signal_listener (text_remove_event_listener, + "Gtk:AtkText:text-remove"); + add_signal_listener (link_selected_event_listener, + "Gtk:AtkHypertext:link-selected"); + add_signal_listener (generic_event_listener, + "Gtk:AtkObject:visible-data-changed"); + add_signal_listener (generic_event_listener, + "Gtk:AtkSelection:selection-changed"); + add_signal_listener (generic_event_listener, + "Gtk:AtkText:text-attributes-changed"); + add_signal_listener (generic_event_listener, + "Gtk:AtkText:text-caret-moved"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-inserted"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-reordered"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-deleted"); + add_signal_listener (generic_event_listener, + "Gtk:AtkTable:column-inserted"); + add_signal_listener (generic_event_listener, + "Gtk:AtkTable:column-reordered"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-deleted"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:model-changed"); + add_signal_listener (children_changed_event_listener, "Gtk:AtkObject:children-changed"); + +#if 0 + g_signal_connect (G_OBJECT (spi_global_app_data->root), + "children-changed::add", + (GCallback) toplevel_added_event_listener, NULL); + + g_signal_connect (G_OBJECT (spi_global_app_data->root), + "children-changed::remove", + (GCallback) toplevel_removed_event_listener, NULL); +#endif /* * May add the following listeners to implement preemptive key listening for GTK+ @@ -764,7 +1225,8 @@ spi_atk_register_event_listeners (void) * 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); + atk_bridge_key_event_listener_id = + atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL); } /*---------------------------------------------------------------------------*/ @@ -780,15 +1242,25 @@ spi_atk_deregister_event_listeners (void) 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_focus_tracker (atk_bridge_focus_tracker_id); + atk_bridge_focus_tracker_id = 0; + } + + if (ids) { + for (i = 0; i < ids->len; i++) + { atk_remove_global_event_listener (g_array_index (ids, guint, i)); + } + g_array_free (ids, TRUE); } - + if (atk_bridge_key_event_listener_id) - atk_remove_key_event_listener (atk_bridge_key_event_listener_id); + { + atk_remove_key_event_listener (atk_bridge_key_event_listener_id); + atk_bridge_key_event_listener_id = 0; + } } /*---------------------------------------------------------------------------*/ @@ -814,20 +1286,35 @@ spi_atk_tidy_windows (void) 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, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name); + emit_event (child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0, + DBUS_TYPE_STRING_AS_STRING, name, append_basic); } g_object_unref (stateset); - emit(child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name); + emit_event (child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0, + DBUS_TYPE_STRING_AS_STRING, name, append_basic); g_object_unref (child); } } +gboolean +spi_event_is_subtype (gchar **needle, gchar **haystack) +{ + while (*haystack && **haystack) + { + if (g_strcmp0 (*needle, *haystack)) + return FALSE; + needle++; + haystack++; + } + return TRUE; +} + /*END------------------------------------------------------------------------*/