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 "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.freedesktop.atspi.Event.Document"
45 #define ITF_EVENT_FOCUS "org.freedesktop.atspi.Event.Focus"
47 /*---------------------------------------------------------------------------*/
50 set_reply (DBusPendingCall *pending, void *user_data)
52 void **replyptr = (void **)user_data;
54 *replyptr = dbus_pending_call_steal_reply (pending);
58 send_and_allow_reentry (DBusConnection *bus, DBusMessage *message)
60 DBusPendingCall *pending;
61 DBusMessage *reply = NULL;
63 if (!dbus_connection_send_with_reply (bus, message, &pending, -1))
67 dbus_pending_call_set_notify (pending, set_reply, (void *)&reply, NULL);
70 if (!dbus_connection_read_write_dispatch (bus, -1)) return NULL;
76 Accessibility_DeviceEventController_NotifyListenersSync(const Accessibility_DeviceEvent *key_event)
80 dbus_bool_t consumed = FALSE;
83 dbus_message_new_method_call(SPI_DBUS_NAME_REGISTRY,
85 SPI_DBUS_INTERFACE_DEC,
86 "NotifyListenersSync");
88 dbus_error_init(&error);
89 if (spi_dbus_marshal_deviceEvent(message, key_event))
91 DBusMessage *reply = send_and_allow_reentry (atk_adaptor_app_data->bus, message);
95 dbus_error_init(&error);
96 dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &consumed, DBUS_TYPE_INVALID);
97 dbus_message_unref(reply);
100 dbus_message_unref(message);
105 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke,
106 AtkKeyEventStruct *event)
108 keystroke->id = (dbus_int32_t) event->keyval;
109 keystroke->hw_code = (dbus_int16_t) event->keycode;
110 keystroke->timestamp = (dbus_uint32_t) event->timestamp;
111 keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF);
116 keystroke->event_string = g_strdup (event->string);
117 c = g_utf8_get_char_validated (event->string, -1);
118 if (c > 0 && g_unichar_isprint (c))
119 keystroke->is_text = TRUE;
121 keystroke->is_text = FALSE;
125 keystroke->event_string = g_strdup ("");
126 keystroke->is_text = FALSE;
130 case (ATK_KEY_EVENT_PRESS):
131 keystroke->type = Accessibility_KEY_PRESSED_EVENT;
133 case (ATK_KEY_EVENT_RELEASE):
134 keystroke->type = Accessibility_KEY_RELEASED_EVENT;
141 g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
142 (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
143 (int) keystroke->modifiers,
144 keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
150 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
153 Accessibility_DeviceEvent key_event;
155 spi_init_keystroke_from_atk_key_event (&key_event, event);
157 result = Accessibility_DeviceEventController_NotifyListenersSync (&key_event);
159 if (key_event.event_string) g_free (key_event.event_string);
165 /*---------------------------------------------------------------------------*/
168 * Emits an AT-SPI event.
169 * AT-SPI events names are split into three parts:
171 * This is mapped onto D-Bus events as:
172 * D-Bus Interface:Signal Name:Detail argument
174 * Marshals a basic type into the 'any_data' attribute of
179 DBusSignalName (const gchar *s)
181 gchar *ret = g_strdup (s);
186 ret [0] = toupper (ret [0]);
187 while ((t = strchr (ret, '-')) != NULL)
189 memmove (t, t + 1, strlen (t));
196 emit(AtkObject *accessible,
200 dbus_int32_t detail1,
201 dbus_int32_t detail2,
208 /* TODO this is a hack, used becuase child-added events are not guaranteed.
209 * On recieving an event from a non-registered object we check if it can be safely
210 * registered before sending the event.
212 path = atk_dbus_object_attempt_registration (accessible);
214 /* Tough decision here
215 * We won't send events from accessible
216 * objects that have not yet been added to the accessible tree.
221 g_debug ("AT-SPI: Event recieved from non-registered object");
226 cname = DBusSignalName (major);
227 spi_dbus_emit_signal (atk_adaptor_app_data->bus, path, klass, cname, minor, detail1, detail2, type, val);
232 /*---------------------------------------------------------------------------*/
235 * Emits an AT-SPI event, marshalling a BoundingBox structure into the
236 * 'any_data' variant of the event.
239 emit_rect(AtkObject *accessible,
246 DBusMessageIter iter, variant, sub;
248 dbus_int32_t dummy = 0;
250 path = atk_dbus_object_to_path (accessible, FALSE);
252 /* Tough decision here
253 * We won't send events from accessible
254 * objects that have not yet been added to the accessible tree.
259 if (!klass) klass = "";
260 if (!major) major = "";
261 if (!minor) minor = "";
264 * This is very annoying, but as '-' isn't a legal signal
265 * name in D-Bus (Why not??!?) The names need converting
266 * on this side, and again on the client side.
268 cname = DBusSignalName (major);
270 sig = dbus_message_new_signal(path, klass, cname);
274 dbus_message_iter_init_append (sig, &iter);
275 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
276 dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
277 dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
279 dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(iiii)", &variant);
280 dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub);
281 dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x));
282 dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y));
283 dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width));
284 dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height));
285 dbus_message_iter_close_container (&variant, &sub);
286 dbus_message_iter_close_container (&iter, &variant);
288 dbus_connection_send(atk_adaptor_app_data->bus, sig, NULL);
290 dbus_message_unref (sig);
293 /*---------------------------------------------------------------------------*/
296 * The focus listener handles the ATK 'focus' signal and forwards it
297 * as the AT-SPI event, 'focus:'
300 focus_tracker (AtkObject *accessible)
302 emit(accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
305 /*---------------------------------------------------------------------------*/
307 #define PCHANGE "property-change"
310 * This handler handles the following ATK signals and
311 * converts them to AT-SPI events:
313 * Gtk:AtkObject:property-change -> object:property-change:(property-name)
315 * The property-name is part of the ATK property-change signal.
318 property_event_listener (GSignalInvocationHint *signal_hint,
319 guint n_param_values,
320 const GValue *param_values,
323 AtkObject *accessible;
324 AtkPropertyValues *values;
326 const gchar *pname = NULL;
332 accessible = g_value_get_object (¶m_values[0]);
333 values = (AtkPropertyValues*) g_value_get_pointer (¶m_values[1]);
335 pname = values[0].property_name;
336 if (strcmp (pname, "accessible-name") == 0 ||
337 strcmp (pname, "accessible-description") == 0 ||
338 strcmp (pname, "accessible-role") == 0 ||
339 strcmp (pname, "accessible-parent") == 0)
344 /* TODO Could improve this control statement by matching
345 * on only the end of the signal names,
347 if (strcmp (pname, "accessible-table-summary") == 0)
349 otemp = atk_table_get_summary(ATK_TABLE (accessible));
350 stemp = atk_dbus_object_to_path (otemp, FALSE);
352 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
354 else if (strcmp (pname, "accessible-table-column-header") == 0)
356 i = g_value_get_int (&(values->new_value));
357 otemp = atk_table_get_column_header(ATK_TABLE (accessible), i);
358 stemp = atk_dbus_object_to_path (otemp, FALSE);
360 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
362 else if (strcmp (pname, "accessible-table-row-header") == 0)
364 i = g_value_get_int (&(values->new_value));
365 otemp = atk_table_get_row_header(ATK_TABLE (accessible), i);
366 stemp = atk_dbus_object_to_path (otemp, FALSE);
368 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
370 else if (strcmp (pname, "accessible-table-row-description") == 0)
372 i = g_value_get_int (&(values->new_value));
373 stemp = atk_table_get_row_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-column-description") == 0)
378 i = g_value_get_int (&(values->new_value));
379 stemp = atk_table_get_column_description(ATK_TABLE (accessible), i);
380 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
382 else if (strcmp (pname, "accessible-table-caption-object") == 0)
384 otemp = atk_table_get_caption(ATK_TABLE(accessible));
385 stemp = atk_object_get_name(otemp);
386 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
390 emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
395 /*---------------------------------------------------------------------------*/
397 #define STATE_CHANGED "state-changed"
400 * The state event listener handles 'Gtk:AtkObject:state-change' ATK signals
401 * and forwards them as object:state-changed:(param-name) AT-SPI events. Where
402 * the param-name is part of the ATK state-change signal.
405 state_event_listener (GSignalInvocationHint *signal_hint,
406 guint n_param_values,
407 const GValue *param_values,
410 AtkObject *accessible;
414 accessible = ATK_OBJECT(g_value_get_object (¶m_values[0]));
415 pname = g_strdup (g_value_get_string (¶m_values[1]));
417 /* TODO - Possibly ignore a change to the 'defunct' state.
418 * This is because without reference counting defunct objects should be removed.
420 detail1 = (g_value_get_boolean (¶m_values[2])) ? 1 : 0;
421 emit(accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
426 /*---------------------------------------------------------------------------*/
429 * The window event listener handles the following ATK signals and forwards
430 * them as AT-SPI events:
432 * window:create -> window:create
433 * window:destroy -> window:destroy
434 * window:minimize -> window:minimize
435 * window:maximize -> window:maximize
436 * window:activate -> window:activate
437 * window:deactivate -> window:deactivate
440 window_event_listener (GSignalInvocationHint *signal_hint,
441 guint n_param_values,
442 const GValue *param_values,
445 AtkObject *accessible;
446 GSignalQuery signal_query;
447 const gchar *name, *s;
449 g_signal_query (signal_hint->signal_id, &signal_query);
450 name = signal_query.signal_name;
452 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
453 s = atk_object_get_name (accessible);
454 emit(accessible, ITF_EVENT_WINDOW, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
459 /*---------------------------------------------------------------------------*/
462 * The document event listener handles the following ATK signals
463 * and converts them to AT-SPI events:
465 * Gtk:AtkDocument:load-complete -> document:load-complete
466 * Gtk:AtkDocument:load-stopped -> document:load-stopped
467 * Gtk:AtkDocument:reload -> document:reload
470 document_event_listener (GSignalInvocationHint *signal_hint,
471 guint n_param_values,
472 const GValue *param_values,
475 AtkObject *accessible;
476 GSignalQuery signal_query;
477 const gchar *name, *s;
479 g_signal_query (signal_hint->signal_id, &signal_query);
480 name = signal_query.signal_name;
482 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
483 s = atk_object_get_name (accessible);
484 emit(accessible, ITF_EVENT_DOCUMENT, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
489 /*---------------------------------------------------------------------------*/
492 * Signal handler for "Gtk:AtkComponent:bounds-changed". Converts
493 * this to an AT-SPI event - "object:bounds-changed".
496 bounds_event_listener (GSignalInvocationHint *signal_hint,
497 guint n_param_values,
498 const GValue *param_values,
501 AtkObject *accessible;
502 AtkRectangle *atk_rect;
503 GSignalQuery signal_query;
504 const gchar *name, *s;
506 g_signal_query (signal_hint->signal_id, &signal_query);
507 name = signal_query.signal_name;
509 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
511 if (G_VALUE_HOLDS_BOXED (param_values + 1))
512 atk_rect = g_value_get_boxed (param_values + 1);
514 emit_rect(accessible, ITF_EVENT_OBJECT, name, "", atk_rect);
518 /*---------------------------------------------------------------------------*/
521 * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and
522 * converts it to the AT-SPI signal - 'object:active-descendant-changed'.
526 active_descendant_event_listener (GSignalInvocationHint *signal_hint,
527 guint n_param_values,
528 const GValue *param_values,
531 AtkObject *accessible;
533 GSignalQuery signal_query;
534 const gchar *name, *minor;
538 g_signal_query (signal_hint->signal_id, &signal_query);
539 name = signal_query.signal_name;
541 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
542 child = ATK_OBJECT(g_value_get_pointer (¶m_values[1]));
543 g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
544 minor = g_quark_to_string (signal_hint->detail);
546 detail1 = atk_object_get_index_in_parent (child);
547 s = atk_dbus_object_to_path (child, FALSE);
554 emit(accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, s);
559 /*---------------------------------------------------------------------------*/
562 * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and
563 * converts it to the AT-SPI signal - 'object:link-selected'
567 link_selected_event_listener (GSignalInvocationHint *signal_hint,
568 guint n_param_values,
569 const GValue *param_values,
572 AtkObject *accessible;
573 GSignalQuery signal_query;
574 const gchar *name, *minor;
577 g_signal_query (signal_hint->signal_id, &signal_query);
578 name = signal_query.signal_name;
580 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
581 minor = g_quark_to_string (signal_hint->detail);
583 if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT)
584 detail1 = g_value_get_int (¶m_values[1]);
586 emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
590 /*---------------------------------------------------------------------------*/
593 * Handles the ATK signal 'Gtk:AtkText:text-changed' and
594 * converts it to the AT-SPI signal - 'object:text-changed'
598 text_changed_event_listener (GSignalInvocationHint *signal_hint,
599 guint n_param_values,
600 const GValue *param_values,
603 AtkObject *accessible;
604 GSignalQuery signal_query;
605 const gchar *name, *minor;
607 gint detail1, detail2;
609 g_signal_query (signal_hint->signal_id, &signal_query);
610 name = signal_query.signal_name;
612 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
613 minor = g_quark_to_string (signal_hint->detail);
615 if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT)
616 detail1 = g_value_get_int (¶m_values[1]);
618 if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT)
619 detail2 = g_value_get_int (¶m_values[2]);
621 selected = atk_text_get_text (ATK_TEXT (accessible), detail1, detail1+detail2);
623 emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, selected);
627 /*---------------------------------------------------------------------------*/
630 * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and
631 * converts it to the AT-SPI signal - 'object:text-selection-changed'
635 text_selection_changed_event_listener (GSignalInvocationHint *signal_hint,
636 guint n_param_values,
637 const GValue *param_values,
640 AtkObject *accessible;
641 GSignalQuery signal_query;
642 const gchar *name, *minor;
643 gint detail1, detail2;
645 g_signal_query (signal_hint->signal_id, &signal_query);
646 name = signal_query.signal_name;
648 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
649 minor = g_quark_to_string (signal_hint->detail);
651 if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT)
652 detail1 = g_value_get_int (¶m_values[1]);
654 if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT)
655 detail2 = g_value_get_int (¶m_values[2]);
657 emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, "");
661 /*---------------------------------------------------------------------------*/
664 * Generic signal converter and forwarder.
666 * Klass (Interface) org.freedesktop.atspi.Event.Object
667 * Major is the signal name.
674 generic_event_listener (GSignalInvocationHint *signal_hint,
675 guint n_param_values,
676 const GValue *param_values,
679 AtkObject *accessible;
680 GSignalQuery signal_query;
683 g_signal_query (signal_hint->signal_id, &signal_query);
684 name = signal_query.signal_name;
686 accessible = ATK_OBJECT(g_value_get_object(¶m_values[0]));
687 emit(accessible, ITF_EVENT_OBJECT, name, "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
691 /*---------------------------------------------------------------------------*/
694 * Registers the provided function as a handler for the given signal name
695 * and stores the signal id returned so that the function may be
696 * de-registered later.
699 add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
703 id = atk_add_global_event_listener (listener, signal_name);
704 g_array_append_val (listener_ids, id);
708 * Initialization for the signal handlers.
710 * Registers all required signal handlers.
713 spi_atk_register_event_listeners (void)
716 * Kludge to make sure the Atk interface types are registered, otherwise
717 * the AtkText signal handlers below won't get registered
719 GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
720 AtkObject *bo = atk_no_op_object_new (ao);
722 g_object_unref (G_OBJECT (bo));
725 /* Register for focus event notifications, and register app with central registry */
726 listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
728 atk_bridge_focus_tracker_id = atk_add_focus_tracker (focus_tracker);
730 add_signal_listener (property_event_listener, "Gtk:AtkObject:property-change");
731 add_signal_listener (window_event_listener, "window:create");
732 add_signal_listener (window_event_listener, "window:destroy");
733 add_signal_listener (window_event_listener, "window:minimize");
734 add_signal_listener (window_event_listener, "window:maximize");
735 add_signal_listener (window_event_listener, "window:restore");
736 add_signal_listener (window_event_listener, "window:activate");
737 add_signal_listener (window_event_listener, "window:deactivate");
738 add_signal_listener (document_event_listener, "Gtk:AtkDocument:load-complete");
739 add_signal_listener (document_event_listener, "Gtk:AtkDocument:reload");
740 add_signal_listener (document_event_listener, "Gtk:AtkDocument:load-stopped");
741 /* TODO Fake this event on the client side */
742 add_signal_listener (state_event_listener, "Gtk:AtkObject:state-change");
744 add_signal_listener (active_descendant_event_listener, "Gtk:AtkObject:active-descendant-changed");
745 add_signal_listener (bounds_event_listener, "Gtk:AtkComponent:bounds-changed");
746 add_signal_listener (text_selection_changed_event_listener, "Gtk:AtkText:text-selection-changed");
747 add_signal_listener (text_changed_event_listener, "Gtk:AtkText:text-changed");
748 add_signal_listener (link_selected_event_listener, "Gtk:AtkHypertext:link-selected");
749 add_signal_listener (generic_event_listener, "Gtk:AtkObject:visible-data-changed");
750 add_signal_listener (generic_event_listener, "Gtk:AtkSelection:selection-changed");
751 add_signal_listener (generic_event_listener, "Gtk:AtkText:text-attributes-changed");
752 add_signal_listener (generic_event_listener, "Gtk:AtkText:text-caret-moved");
753 add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-inserted");
754 add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-reordered");
755 add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-deleted");
756 add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-inserted");
757 add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-reordered");
758 add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-deleted");
759 add_signal_listener (generic_event_listener, "Gtk:AtkTable:model-changed");
762 * May add the following listeners to implement preemptive key listening for GTK+
764 * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
765 * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
767 atk_bridge_key_event_listener_id = atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL);
770 /*---------------------------------------------------------------------------*/
773 * De-registers all ATK signal handlers.
776 spi_atk_deregister_event_listeners (void)
779 GArray *ids = listener_ids;
782 if (atk_bridge_focus_tracker_id)
783 atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
785 for (i = 0; ids && i < ids->len; i++)
787 atk_remove_global_event_listener (g_array_index (ids, guint, i));
790 if (atk_bridge_key_event_listener_id)
791 atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
794 /*---------------------------------------------------------------------------*/
797 * TODO This function seems out of place here.
799 * Emits fake deactivate signals on all top-level windows.
800 * Used when shutting down AT-SPI, ensuring that all
801 * windows have been removed on the client side.
804 spi_atk_tidy_windows (void)
810 root = atk_get_root ();
811 n_children = atk_object_get_n_accessible_children (root);
812 for (i = 0; i < n_children; i++)
815 AtkStateSet *stateset;
818 child = atk_object_ref_accessible_child (root, i);
819 stateset = atk_object_ref_state_set (child);
821 name = atk_object_get_name (child);
822 if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
824 emit(child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
826 g_object_unref (stateset);
828 emit(child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
829 g_object_unref (child);
833 /*END------------------------------------------------------------------------*/