2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2008, 2009, Codethink Ltd.
6 * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
7 * Copyright 2001, 2002, 2003 Ximian, Inc.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
28 #include <droute/droute.h>
31 #include "accessible-register.h"
33 #include "spi-common/spi-dbus.h"
35 static GArray *listener_ids = NULL;
37 static gint atk_bridge_key_event_listener_id;
38 static gint atk_bridge_focus_tracker_id;
40 /*---------------------------------------------------------------------------*/
42 #define ITF_EVENT_OBJECT "org.freedesktop.atspi.Event.Object"
43 #define ITF_EVENT_WINDOW "org.freedesktop.atspi.Event.Window"
44 #define ITF_EVENT_DOCUMENT "org.freedekstop.atspi.Event.Document"
45 #define ITF_EVENT_FOCUS "org.freedesktop.atspi.Event.Focus"
47 /*---------------------------------------------------------------------------*/
50 Accessibility_DeviceEventController_notifyListenersSync(const Accessibility_DeviceEvent *key_event)
54 dbus_bool_t consumed = FALSE;
57 dbus_message_new_method_call(SPI_DBUS_NAME_REGISTRY,
58 SPI_DBUS_PATH_REGISTRY,
59 SPI_DBUS_INTERFACE_DEC,
60 "notifyListenersSync");
62 dbus_error_init(&error);
63 if (spi_dbus_marshal_deviceEvent(message, key_event))
65 DBusMessage *reply = dbus_connection_send_with_reply_and_block(atk_adaptor_app_data->bus, message, 1000, &error);
69 dbus_error_init(&error);
70 dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &consumed, DBUS_TYPE_INVALID);
71 dbus_message_unref(reply);
74 dbus_message_unref(message);
79 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke,
80 AtkKeyEventStruct *event)
82 keystroke->id = (dbus_int32_t) event->keyval;
83 keystroke->hw_code = (dbus_int16_t) event->keycode;
84 keystroke->timestamp = (dbus_uint32_t) event->timestamp;
85 keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF);
90 keystroke->event_string = g_strdup (event->string);
91 c = g_utf8_get_char_validated (event->string, -1);
92 if (c > 0 && g_unichar_isprint (c))
93 keystroke->is_text = TRUE;
95 keystroke->is_text = FALSE;
99 keystroke->event_string = g_strdup ("");
100 keystroke->is_text = FALSE;
104 case (ATK_KEY_EVENT_PRESS):
105 keystroke->type = Accessibility_KEY_PRESSED_EVENT;
107 case (ATK_KEY_EVENT_RELEASE):
108 keystroke->type = Accessibility_KEY_RELEASED_EVENT;
115 g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
116 (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
117 (int) keystroke->modifiers,
118 keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
124 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
127 Accessibility_DeviceEvent key_event;
129 spi_init_keystroke_from_atk_key_event (&key_event, event);
131 result = Accessibility_DeviceEventController_notifyListenersSync (&key_event);
133 if (key_event.event_string) g_free (key_event.event_string);
139 /*---------------------------------------------------------------------------*/
142 * Emits an AT-SPI event.
143 * AT-SPI events names are split into three parts:
145 * This is mapped onto D-Bus events as:
146 * D-Bus Interface:Signal Name:Detail argument
148 * Marshals a basic type into the 'any_data' attribute of
153 * This is a rather annoying function needed to replace
154 * NULL values of strings with the empty string. Null string
155 * values can be created by the atk_object_get_name or text selection
158 provide_defaults(const gint type,
163 case DBUS_TYPE_STRING:
164 case DBUS_TYPE_OBJECT_PATH:
175 emit(AtkObject *accessible,
179 dbus_int32_t detail1,
180 dbus_int32_t detail2,
185 DBusMessageIter iter, sub;
186 gchar *path, *cname, *t;
188 path = atk_dbus_object_to_path (accessible);
190 /* Tough decision here
191 * We won't send events from accessible
192 * objects that have not yet been added to the accessible tree.
197 if (!klass) klass = "";
198 if (!major) major = "";
199 if (!minor) minor = "";
202 * This is very annoying, but as '-' isn't a legal signal
203 * name in D-Bus (Why not??!?) The names need converting
204 * on this side, and again on the client side.
206 cname = g_strdup(major);
207 while ((t = strchr(cname, '-')) != NULL) *t = '_';
209 sig = dbus_message_new_signal(path, klass, cname);
213 dbus_message_iter_init_append(sig, &iter);
215 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
216 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1);
217 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2);
219 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type, &sub);
221 * I need to convert the string signature to an integer type signature.
222 * DBUS_TYPE_INT32 is defined as 'i' whereas the string is "i".
223 * I should just be able to cast the first character of the string to an
226 val = provide_defaults((int) *type, val);
227 dbus_message_iter_append_basic(&sub, (int) *type, &val);
228 dbus_message_iter_close_container(&iter, &sub);
230 dbus_connection_send(atk_adaptor_app_data->bus, sig, NULL);
231 dbus_message_unref(sig);
234 /*---------------------------------------------------------------------------*/
237 * Emits an AT-SPI event, marshalling a BoundingBox structure into the
238 * 'any_data' variant of the event.
241 emit_rect(AtkObject *accessible,
248 DBusMessageIter iter, variant, sub;
249 gchar *path, *cname, *t;
250 dbus_int32_t dummy = 0;
252 path = atk_dbus_object_to_path (accessible);
254 /* Tough decision here
255 * We won't send events from accessible
256 * objects that have not yet been added to the accessible tree.
261 if (!klass) klass = "";
262 if (!major) major = "";
263 if (!minor) minor = "";
266 * This is very annoying, but as '-' isn't a legal signal
267 * name in D-Bus (Why not??!?) The names need converting
268 * on this side, and again on the client side.
270 cname = g_strdup(major);
271 while ((t = strchr(cname, '-')) != NULL) *t = '_';
273 sig = dbus_message_new_signal(path, klass, cname);
277 dbus_message_iter_init_append (sig, &iter);
278 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
279 dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
280 dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
282 dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(iiii)", &variant);
283 dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub);
284 dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x));
285 dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y));
286 dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width));
287 dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height));
288 dbus_message_iter_close_container (&variant, &sub);
289 dbus_message_iter_close_container (&iter, &variant);
291 dbus_connection_send(atk_adaptor_app_data->bus, sig, NULL);
294 /*---------------------------------------------------------------------------*/
297 * The focus listener handles the ATK 'focus' signal and forwards it
298 * as the AT-SPI event, 'focus:'
301 focus_tracker (AtkObject *accessible)
303 emit(accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
306 /*---------------------------------------------------------------------------*/
308 #define PCHANGE "property-change"
311 * This handler handles the following ATK signals and
312 * converts them to AT-SPI events:
314 * Gtk:AtkObject:property-change -> object:property-change:(property-name)
316 * The property-name is part of the ATK property-change signal.
319 property_event_listener (GSignalInvocationHint *signal_hint,
320 guint n_param_values,
321 const GValue *param_values,
324 AtkObject *accessible;
325 AtkPropertyValues *values;
327 const gchar *pname = NULL;
333 accessible = g_value_get_object (¶m_values[0]);
334 values = (AtkPropertyValues*) g_value_get_pointer (¶m_values[1]);
336 pname = values[0].property_name;
338 /* TODO Could improve this control statement by matching
339 * on only the end of the signal names,
341 if (strcmp (pname, "accessible-table-summary") == 0)
343 otemp = atk_table_get_summary(ATK_TABLE (accessible));
344 stemp = atk_dbus_object_to_path (otemp);
346 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
348 else if (strcmp (pname, "accessible-table-column-header") == 0)
350 i = g_value_get_int (&(values->new_value));
351 otemp = atk_table_get_column_header(ATK_TABLE (accessible), i);
352 stemp = atk_dbus_object_to_path (otemp);
354 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
356 else if (strcmp (pname, "accessible-table-row-header") == 0)
358 i = g_value_get_int (&(values->new_value));
359 otemp = atk_table_get_row_header(ATK_TABLE (accessible), i);
360 stemp = atk_dbus_object_to_path (otemp);
362 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
364 else if (strcmp (pname, "accessible-table-row-description") == 0)
366 i = g_value_get_int (&(values->new_value));
367 stemp = atk_table_get_row_description(ATK_TABLE (accessible), i);
368 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
370 else if (strcmp (pname, "accessible-table-column-description") == 0)
372 i = g_value_get_int (&(values->new_value));
373 stemp = atk_table_get_column_description(ATK_TABLE (accessible), i);
374 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
376 else if (strcmp (pname, "accessible-table-caption-object") == 0)
378 otemp = atk_table_get_caption(ATK_TABLE(accessible));
379 stemp = atk_object_get_name(otemp);
380 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
384 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
389 /*---------------------------------------------------------------------------*/
391 #define STATE_CHANGED "state-changed"
394 * The state event listener handles 'Gtk:AtkObject:state-change' ATK signals
395 * and forwards them as object:state-changed:(param-name) AT-SPI events. Where
396 * the param-name is part of the ATK state-change signal.
399 state_event_listener (GSignalInvocationHint *signal_hint,
400 guint n_param_values,
401 const GValue *param_values,
404 AtkObject *accessible;
408 accessible = ATK_OBJECT(g_value_get_object (¶m_values[0]));
409 pname = g_strdup (g_value_get_string (¶m_values[1]));
411 /* TODO - Possibly ignore a change to the 'defunct' state.
412 * This is because without reference counting defunct objects should be removed.
414 detail1 = (g_value_get_boolean (¶m_values[2])) ? 1 : 0;
415 emit(accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
420 /*---------------------------------------------------------------------------*/
423 * The window event listener handles the following ATK signals and forwards
424 * them as AT-SPI events:
426 * window:create -> window:create
427 * window:destroy -> window:destroy
428 * window:minimize -> window:minimize
429 * window:maximize -> window:maximize
430 * window:activate -> window:activate
431 * window:deactivate -> window:deactivate
434 window_event_listener (GSignalInvocationHint *signal_hint,
435 guint n_param_values,
436 const GValue *param_values,
439 AtkObject *accessible;
440 GSignalQuery signal_query;
441 const gchar *name, *s;
443 g_signal_query (signal_hint->signal_id, &signal_query);
444 name = signal_query.signal_name;
446 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
447 s = atk_object_get_name (accessible);
448 emit(accessible, ITF_EVENT_WINDOW, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
453 /*---------------------------------------------------------------------------*/
456 * The document event listener handles the following ATK signals
457 * and converts them to AT-SPI events:
459 * Gtk:AtkDocument:load-complete -> document:load-complete
460 * Gtk:AtkDocument:load-stopped -> document:load-stopped
461 * Gtk:AtkDocument:reload -> document:reload
464 document_event_listener (GSignalInvocationHint *signal_hint,
465 guint n_param_values,
466 const GValue *param_values,
469 AtkObject *accessible;
470 GSignalQuery signal_query;
471 const gchar *name, *s;
473 g_signal_query (signal_hint->signal_id, &signal_query);
474 name = signal_query.signal_name;
476 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
477 s = atk_object_get_name (accessible);
478 emit(accessible, ITF_EVENT_DOCUMENT, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
483 /*---------------------------------------------------------------------------*/
486 * Signal handler for "Gtk:AtkComponent:bounds-changed". Converts
487 * this to an AT-SPI event - "object:bounds-changed".
490 bounds_event_listener (GSignalInvocationHint *signal_hint,
491 guint n_param_values,
492 const GValue *param_values,
495 AtkObject *accessible;
496 AtkRectangle *atk_rect;
497 GSignalQuery signal_query;
498 const gchar *name, *s;
500 g_signal_query (signal_hint->signal_id, &signal_query);
501 name = signal_query.signal_name;
503 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
505 if (G_VALUE_HOLDS_BOXED (param_values + 1))
506 atk_rect = g_value_get_boxed (param_values + 1);
508 emit_rect(accessible, ITF_EVENT_OBJECT, name, "", atk_rect);
512 /*---------------------------------------------------------------------------*/
515 * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and
516 * converts it to the AT-SPI signal - 'object:active-descendant-changed'.
520 active_descendant_event_listener (GSignalInvocationHint *signal_hint,
521 guint n_param_values,
522 const GValue *param_values,
525 AtkObject *accessible;
527 GSignalQuery signal_query;
528 const gchar *name, *minor;
532 g_signal_query (signal_hint->signal_id, &signal_query);
533 name = signal_query.signal_name;
535 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
536 child = ATK_OBJECT(g_value_get_pointer (¶m_values[1]));
537 g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
538 minor = g_quark_to_string (signal_hint->detail);
540 detail1 = atk_object_get_index_in_parent (child);
541 s = atk_dbus_object_to_path (child);
548 emit(accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, s);
553 /*---------------------------------------------------------------------------*/
556 * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and
557 * converts it to the AT-SPI signal - 'object:link-selected'
561 link_selected_event_listener (GSignalInvocationHint *signal_hint,
562 guint n_param_values,
563 const GValue *param_values,
566 AtkObject *accessible;
567 GSignalQuery signal_query;
568 const gchar *name, *minor;
571 g_signal_query (signal_hint->signal_id, &signal_query);
572 name = signal_query.signal_name;
574 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
575 minor = g_quark_to_string (signal_hint->detail);
577 if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT)
578 detail1 = g_value_get_int (¶m_values[1]);
580 emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
584 /*---------------------------------------------------------------------------*/
587 * Handles the ATK signal 'Gtk:AtkText:text-changed' and
588 * converts it to the AT-SPI signal - 'object:text-changed'
592 text_changed_event_listener (GSignalInvocationHint *signal_hint,
593 guint n_param_values,
594 const GValue *param_values,
597 AtkObject *accessible;
598 GSignalQuery signal_query;
599 const gchar *name, *minor;
601 gint detail1, detail2;
603 g_signal_query (signal_hint->signal_id, &signal_query);
604 name = signal_query.signal_name;
606 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
607 minor = g_quark_to_string (signal_hint->detail);
609 if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT)
610 detail1 = g_value_get_int (¶m_values[1]);
612 if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT)
613 detail2 = g_value_get_int (¶m_values[2]);
615 selected = atk_text_get_text (ATK_TEXT (accessible), detail1, detail1+detail2);
617 emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, selected);
621 /*---------------------------------------------------------------------------*/
624 * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and
625 * converts it to the AT-SPI signal - 'object:text-selection-changed'
629 text_selection_changed_event_listener (GSignalInvocationHint *signal_hint,
630 guint n_param_values,
631 const GValue *param_values,
634 AtkObject *accessible;
635 GSignalQuery signal_query;
636 const gchar *name, *minor;
637 gint detail1, detail2;
639 g_signal_query (signal_hint->signal_id, &signal_query);
640 name = signal_query.signal_name;
642 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
643 minor = g_quark_to_string (signal_hint->detail);
645 if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT)
646 detail1 = g_value_get_int (¶m_values[1]);
648 if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT)
649 detail2 = g_value_get_int (¶m_values[2]);
651 emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, "");
655 /*---------------------------------------------------------------------------*/
658 * Generic signal converter and forwarder.
660 * Klass (Interface) org.freedesktop.atspi.Event.Object
661 * Major is the signal name.
668 generic_event_listener (GSignalInvocationHint *signal_hint,
669 guint n_param_values,
670 const GValue *param_values,
673 AtkObject *accessible;
674 GSignalQuery signal_query;
677 g_signal_query (signal_hint->signal_id, &signal_query);
678 name = signal_query.signal_name;
680 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
681 emit(accessible, ITF_EVENT_OBJECT, name, "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
685 /*---------------------------------------------------------------------------*/
688 * Registers the provided function as a handler for the given signal name
689 * and stores the signal id returned so that the function may be
690 * de-registered later.
693 add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
697 id = atk_add_global_event_listener (listener, signal_name);
698 g_array_append_val (listener_ids, id);
702 * Initialization for the signal handlers.
704 * Registers all required signal handlers.
707 spi_atk_register_event_listeners (void)
710 * Kludge to make sure the Atk interface types are registered, otherwise
711 * the AtkText signal handlers below won't get registered
713 GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
714 AtkObject *bo = atk_no_op_object_new (ao);
716 g_object_unref (G_OBJECT (bo));
719 /* Register for focus event notifications, and register app with central registry */
720 listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
722 atk_bridge_focus_tracker_id = atk_add_focus_tracker (focus_tracker);
724 add_signal_listener (property_event_listener, "Gtk:AtkObject:property-change");
725 add_signal_listener (window_event_listener, "window:create");
726 add_signal_listener (window_event_listener, "window:destroy");
727 add_signal_listener (window_event_listener, "window:minimize");
728 add_signal_listener (window_event_listener, "window:maximize");
729 add_signal_listener (window_event_listener, "window:restore");
730 add_signal_listener (window_event_listener, "window:activate");
731 add_signal_listener (window_event_listener, "window:deactivate");
732 add_signal_listener (document_event_listener, "Gtk:AtkDocument:load-complete");
733 add_signal_listener (document_event_listener, "Gtk:AtkDocument:reload");
734 add_signal_listener (document_event_listener, "Gtk:AtkDocument:load-stopped");
735 add_signal_listener (state_event_listener, "Gtk:AtkObject:state-change");
736 add_signal_listener (active_descendant_event_listener, "Gtk:AtkObject:active-descendant-changed");
737 add_signal_listener (bounds_event_listener, "Gtk:AtkComponent:bounds-changed");
738 add_signal_listener (text_selection_changed_event_listener, "Gtk:AtkText:text-selection-changed");
739 add_signal_listener (text_changed_event_listener, "Gtk:AtkText:text-changed");
740 add_signal_listener (link_selected_event_listener, "Gtk:AtkHypertext:link-selected");
741 add_signal_listener (generic_event_listener, "Gtk:AtkObject:visible-data-changed");
742 add_signal_listener (generic_event_listener, "Gtk:AtkSelection:selection-changed");
743 add_signal_listener (generic_event_listener, "Gtk:AtkText:text-caret-moved");
744 add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-inserted");
745 add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-reordered");
746 add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-deleted");
747 add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-inserted");
748 add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-reordered");
749 add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-deleted");
750 add_signal_listener (generic_event_listener, "Gtk:AtkTable:model-changed");
753 * May add the following listeners to implement preemptive key listening for GTK+
755 * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
756 * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
758 atk_bridge_key_event_listener_id = atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL);
761 /*---------------------------------------------------------------------------*/
764 * De-registers all ATK signal handlers.
767 spi_atk_deregister_event_listeners (void)
770 GArray *ids = listener_ids;
773 if (atk_bridge_focus_tracker_id)
774 atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
776 for (i = 0; ids && i < ids->len; i++)
778 atk_remove_global_event_listener (g_array_index (ids, guint, i));
781 if (atk_bridge_key_event_listener_id)
782 atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
785 /*---------------------------------------------------------------------------*/
788 * TODO This function seems out of place here.
790 * Emits fake deactivate signals on all top-level windows.
791 * Used when shutting down AT-SPI, ensuring that all
792 * windows have been removed on the client side.
795 spi_atk_tidy_windows (void)
801 root = atk_get_root ();
802 n_children = atk_object_get_n_accessible_children (root);
803 for (i = 0; i < n_children; i++)
806 AtkStateSet *stateset;
809 child = atk_object_ref_accessible_child (root, i);
810 stateset = atk_object_ref_state_set (child);
812 name = atk_object_get_name (child);
813 if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
815 emit(child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
817 g_object_unref (stateset);
819 emit(child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
820 g_object_unref (child);
824 /*END------------------------------------------------------------------------*/