Merge branch 'master' of git+ssh://doffm@git.freedesktop.org/git/at-spi2/at-spi2...
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / event.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2008, 2009, Codethink Ltd.
6  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
7  * Copyright 2001, 2002, 2003 Ximian, Inc.
8  *
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.
13  *
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.
18  *
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.
23  */
24
25 #include <string.h>
26
27 #include <atk/atk.h>
28 #include <droute/droute.h>
29
30 #include "bridge.h"
31 #include "accessible-register.h"
32
33 #include "spi-common/spi-dbus.h"
34
35 static GArray *listener_ids = NULL;
36
37 static gint atk_bridge_key_event_listener_id;
38 static gint atk_bridge_focus_tracker_id;
39
40 /*---------------------------------------------------------------------------*/
41
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"
46
47 /*---------------------------------------------------------------------------*/
48
49 static gboolean
50 Accessibility_DeviceEventController_notifyListenersSync(const Accessibility_DeviceEvent *key_event)
51 {
52   DBusMessage *message;
53   DBusError error;
54   dbus_bool_t consumed = FALSE;
55
56   message = 
57   dbus_message_new_method_call(SPI_DBUS_NAME_REGISTRY, 
58                                SPI_DBUS_PATH_DEC,
59                                SPI_DBUS_INTERFACE_DEC,
60                                "notifyListenersSync");
61
62   dbus_error_init(&error);
63   if (spi_dbus_marshal_deviceEvent(message, key_event))
64   {
65     DBusMessage *reply = dbus_connection_send_with_reply_and_block(atk_adaptor_app_data->bus, message, 1000, &error);
66     if (reply)
67     {
68       DBusError 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);
72     }
73   }
74   dbus_message_unref(message);
75   return consumed;
76 }
77
78 static void
79 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent  *keystroke,
80                                        AtkKeyEventStruct          *event)
81 {
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);
86   if (event->string)
87     {
88       gunichar c;
89
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;
94       else
95         keystroke->is_text = FALSE;
96     }
97   else
98     {
99       keystroke->event_string = g_strdup ("");
100       keystroke->is_text = FALSE;
101     }
102   switch (event->type)
103     {
104     case (ATK_KEY_EVENT_PRESS):
105       keystroke->type = Accessibility_KEY_PRESSED_EVENT;
106       break;
107     case (ATK_KEY_EVENT_RELEASE):
108       keystroke->type = Accessibility_KEY_RELEASED_EVENT;
109       break;
110     default:
111       keystroke->type = 0;
112       break;
113     }
114 #if 0  
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);
119 #endif
120 }
121
122
123 static gint
124 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
125 {
126   gboolean             result;
127   Accessibility_DeviceEvent key_event;
128
129   spi_init_keystroke_from_atk_key_event (&key_event, event);
130
131   result = Accessibility_DeviceEventController_notifyListenersSync (&key_event);
132
133   if (key_event.event_string) g_free (key_event.event_string);
134
135   return result;
136 }
137
138
139 /*---------------------------------------------------------------------------*/
140
141 /*
142  * Emits an AT-SPI event.
143  * AT-SPI events names are split into three parts:
144  * class:major:minor
145  * This is mapped onto D-Bus events as:
146  * D-Bus Interface:Signal Name:Detail argument
147  *
148  * Marshals a basic type into the 'any_data' attribute of
149  * the AT-SPI event.
150  */
151
152 static void 
153 emit(AtkObject  *accessible,
154      const char *klass,
155      const char *major,
156      const char *minor,
157      dbus_int32_t detail1,
158      dbus_int32_t detail2,
159      const char *type,
160      const void *val)
161 {
162   gchar *path;
163
164   path = atk_dbus_object_to_path (accessible);
165
166   /* Tough decision here
167    * We won't send events from accessible
168    * objects that have not yet been added to the accessible tree.
169    */
170   if (path == NULL)
171       return;
172
173   spi_dbus_emit_signal (atk_adaptor_app_data->bus, path, klass, major, minor, detail1, detail2, type, val);
174   g_free(path);
175 }
176
177 /*---------------------------------------------------------------------------*/
178
179 /*
180  * Emits an AT-SPI event, marshalling a BoundingBox structure into the 
181  * 'any_data' variant of the event.
182  */
183 static void
184 emit_rect(AtkObject  *accessible,
185           const char *klass,
186           const char *major,
187           const char *minor,
188           AtkRectangle *rect)
189 {
190   DBusMessage *sig;
191   DBusMessageIter iter, variant, sub;
192   gchar *path, *cname, *t;
193   dbus_int32_t dummy = 0;
194
195   path = atk_dbus_object_to_path (accessible);
196
197   /* Tough decision here
198    * We won't send events from accessible
199    * objects that have not yet been added to the accessible tree.
200    */
201   if (path == NULL)
202       return;
203
204   if (!klass) klass = "";
205   if (!major) major = "";
206   if (!minor) minor = "";
207
208   /*
209    * This is very annoying, but as '-' isn't a legal signal
210    * name in D-Bus (Why not??!?) The names need converting
211    * on this side, and again on the client side.
212    */
213   cname = g_strdup(major);
214   while ((t = strchr(cname, '-')) != NULL) *t = '_';
215
216   sig = dbus_message_new_signal(path, klass, cname);
217   g_free(path);
218   g_free(cname);
219
220   dbus_message_iter_init_append (sig, &iter);
221   dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
222   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
223   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
224
225   dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(iiii)", &variant);
226     dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub);
227       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x));
228       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y));
229       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width));
230       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height));
231     dbus_message_iter_close_container (&variant, &sub);
232   dbus_message_iter_close_container (&iter, &variant);
233
234   dbus_connection_send(atk_adaptor_app_data->bus, sig, NULL);
235 }
236
237 /*---------------------------------------------------------------------------*/
238
239 /*
240  * The focus listener handles the ATK 'focus' signal and forwards it
241  * as the AT-SPI event, 'focus:'
242  */
243 static void
244 focus_tracker (AtkObject *accessible)
245 {
246   emit(accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
247 }
248
249 /*---------------------------------------------------------------------------*/
250
251 #define PCHANGE "property-change"
252
253 /* 
254  * This handler handles the following ATK signals and
255  * converts them to AT-SPI events:
256  *  
257  * Gtk:AtkObject:property-change -> object:property-change:(property-name)
258  *
259  * The property-name is part of the ATK property-change signal.
260  */
261 static gboolean
262 property_event_listener (GSignalInvocationHint *signal_hint,
263                          guint                  n_param_values,
264                          const GValue          *param_values,
265                          gpointer               data)
266 {
267   AtkObject *accessible;
268   AtkPropertyValues *values;
269
270   const gchar *pname = NULL;
271
272   AtkObject *otemp;
273   const gchar *stemp;
274   gint i;
275   
276   accessible = g_value_get_object (&param_values[0]);
277   values = (AtkPropertyValues*) g_value_get_pointer (&param_values[1]);
278
279   pname = values[0].property_name;
280
281   /* TODO Could improve this control statement by matching
282    * on only the end of the signal names,
283    */
284   if (strcmp (pname, "accessible-table-summary") == 0)
285     {
286       otemp = atk_table_get_summary(ATK_TABLE (accessible));
287       stemp = atk_dbus_object_to_path (otemp);
288       if (stemp != NULL)
289           emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
290     }
291   else if (strcmp (pname, "accessible-table-column-header") == 0)
292     {
293       i = g_value_get_int (&(values->new_value));
294       otemp = atk_table_get_column_header(ATK_TABLE (accessible), i);
295       stemp = atk_dbus_object_to_path (otemp);
296       if (stemp != NULL)
297           emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
298     }
299   else if (strcmp (pname, "accessible-table-row-header") == 0)
300     {
301       i = g_value_get_int (&(values->new_value));
302       otemp = atk_table_get_row_header(ATK_TABLE (accessible), i);
303       stemp = atk_dbus_object_to_path (otemp);
304       if (stemp != NULL)
305           emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
306     }
307   else if (strcmp (pname, "accessible-table-row-description") == 0)
308     {
309       i = g_value_get_int (&(values->new_value));
310       stemp = atk_table_get_row_description(ATK_TABLE (accessible), i);
311       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
312     }
313   else if (strcmp (pname, "accessible-table-column-description") == 0)
314     {
315       i = g_value_get_int (&(values->new_value));
316       stemp = atk_table_get_column_description(ATK_TABLE (accessible), i);
317       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
318     }
319   else if (strcmp (pname, "accessible-table-caption-object") == 0)
320     {
321       otemp = atk_table_get_caption(ATK_TABLE(accessible));
322       stemp = atk_object_get_name(otemp);
323       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
324     }
325   else
326     {
327       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
328     }
329   return TRUE;
330 }
331
332 /*---------------------------------------------------------------------------*/
333
334 #define STATE_CHANGED "state-changed"
335
336 /*
337  * The state event listener handles 'Gtk:AtkObject:state-change' ATK signals
338  * and forwards them as object:state-changed:(param-name) AT-SPI events. Where
339  * the param-name is part of the ATK state-change signal.
340  */
341 static gboolean
342 state_event_listener (GSignalInvocationHint *signal_hint,
343                       guint n_param_values,
344                       const GValue *param_values,
345                       gpointer data)
346 {
347   AtkObject *accessible;
348   gchar *pname;
349   guint detail1;
350
351   accessible = ATK_OBJECT(g_value_get_object (&param_values[0]));
352   pname = g_strdup (g_value_get_string (&param_values[1]));
353
354   /* TODO - Possibly ignore a change to the 'defunct' state.
355    * This is because without reference counting defunct objects should be removed.
356    */
357   detail1 = (g_value_get_boolean (&param_values[2])) ? 1 : 0;
358   emit(accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
359   g_free (pname);
360   return TRUE;
361 }
362
363 /*---------------------------------------------------------------------------*/
364
365 /*
366  * The window event listener handles the following ATK signals and forwards
367  * them as AT-SPI events:
368  *
369  * window:create     -> window:create
370  * window:destroy    -> window:destroy
371  * window:minimize   -> window:minimize
372  * window:maximize   -> window:maximize
373  * window:activate   -> window:activate
374  * window:deactivate -> window:deactivate
375  */
376 static gboolean
377 window_event_listener (GSignalInvocationHint *signal_hint,
378                        guint n_param_values,
379                        const GValue *param_values,
380                        gpointer data)
381 {
382   AtkObject *accessible;
383   GSignalQuery signal_query;
384   const gchar *name, *s;
385   
386   g_signal_query (signal_hint->signal_id, &signal_query);
387   name = signal_query.signal_name;
388
389   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
390   s = atk_object_get_name (accessible);
391   emit(accessible, ITF_EVENT_WINDOW, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
392
393   return TRUE;
394 }
395
396 /*---------------------------------------------------------------------------*/
397
398 /* 
399  * The document event listener handles the following ATK signals
400  * and converts them to AT-SPI events:
401  *
402  * Gtk:AtkDocument:load-complete ->  document:load-complete
403  * Gtk:AtkDocument:load-stopped  ->  document:load-stopped
404  * Gtk:AtkDocument:reload        ->  document:reload
405  */
406 static gboolean
407 document_event_listener (GSignalInvocationHint *signal_hint,
408                          guint n_param_values,
409                          const GValue *param_values,
410                          gpointer data)
411 {
412   AtkObject *accessible;
413   GSignalQuery signal_query;
414   const gchar *name, *s;
415
416   g_signal_query (signal_hint->signal_id, &signal_query);
417   name = signal_query.signal_name;
418
419   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
420   s = atk_object_get_name (accessible);
421   emit(accessible, ITF_EVENT_DOCUMENT, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
422
423   return TRUE;
424 }
425
426 /*---------------------------------------------------------------------------*/
427
428 /*
429  * Signal handler for  "Gtk:AtkComponent:bounds-changed". Converts
430  * this to an AT-SPI event - "object:bounds-changed".
431  */
432 static gboolean
433 bounds_event_listener (GSignalInvocationHint *signal_hint,
434                        guint n_param_values,
435                        const GValue *param_values,
436                        gpointer data)
437 {
438   AtkObject *accessible;
439   AtkRectangle *atk_rect;
440   GSignalQuery signal_query;
441   const gchar *name, *s;
442
443   g_signal_query (signal_hint->signal_id, &signal_query);
444   name = signal_query.signal_name;
445
446   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
447
448   if (G_VALUE_HOLDS_BOXED (param_values + 1))
449     atk_rect = g_value_get_boxed (param_values + 1);
450
451   emit_rect(accessible, ITF_EVENT_OBJECT, name, "", atk_rect);
452   return TRUE;
453 }
454
455 /*---------------------------------------------------------------------------*/
456
457 /* 
458  * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and 
459  * converts it to the AT-SPI signal - 'object:active-descendant-changed'.
460  *
461  */
462 static gboolean
463 active_descendant_event_listener (GSignalInvocationHint *signal_hint,
464                                   guint n_param_values,
465                                   const GValue *param_values,
466                                   gpointer data)
467 {
468   AtkObject *accessible;
469   AtkObject *child;
470   GSignalQuery signal_query;
471   const gchar *name, *minor;
472   gchar *s;
473   gint detail1;
474
475   g_signal_query (signal_hint->signal_id, &signal_query);
476   name = signal_query.signal_name;
477
478   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
479   child = ATK_OBJECT(g_value_get_pointer (&param_values[1]));
480   g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
481   minor = g_quark_to_string (signal_hint->detail);
482
483   detail1 = atk_object_get_index_in_parent (child);
484   s = atk_dbus_object_to_path (child);
485   if (s == NULL)
486     {
487       g_free (s);
488       return TRUE;
489     }
490
491   emit(accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, s);
492   g_free(s);
493   return TRUE;
494 }
495
496 /*---------------------------------------------------------------------------*/
497
498 /* 
499  * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and
500  * converts it to the AT-SPI signal - 'object:link-selected'
501  *
502  */
503 static gboolean
504 link_selected_event_listener (GSignalInvocationHint *signal_hint,
505                               guint n_param_values,
506                               const GValue *param_values,
507                               gpointer data)
508 {
509   AtkObject *accessible;
510   GSignalQuery signal_query;
511   const gchar *name, *minor;
512   gint detail1;
513
514   g_signal_query (signal_hint->signal_id, &signal_query);
515   name = signal_query.signal_name;
516
517   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
518   minor = g_quark_to_string (signal_hint->detail);
519
520   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
521         detail1 = g_value_get_int (&param_values[1]);
522
523   emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
524   return TRUE;
525 }
526
527 /*---------------------------------------------------------------------------*/
528
529 /* 
530  * Handles the ATK signal 'Gtk:AtkText:text-changed' and
531  * converts it to the AT-SPI signal - 'object:text-changed'
532  *
533  */
534 static gboolean
535 text_changed_event_listener (GSignalInvocationHint *signal_hint,
536                              guint n_param_values,
537                              const GValue *param_values,
538                              gpointer data)
539 {
540   AtkObject *accessible;
541   GSignalQuery signal_query;
542   const gchar *name, *minor;
543   gchar *selected;
544   gint detail1, detail2;
545
546   g_signal_query (signal_hint->signal_id, &signal_query);
547   name = signal_query.signal_name;
548
549   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
550   minor = g_quark_to_string (signal_hint->detail);
551
552   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
553         detail1 = g_value_get_int (&param_values[1]);
554
555   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
556         detail2 = g_value_get_int (&param_values[2]);
557
558   selected = atk_text_get_text (ATK_TEXT (accessible), detail1, detail1+detail2);
559
560   emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, selected);
561   return TRUE;
562 }
563
564 /*---------------------------------------------------------------------------*/
565
566 /* 
567  * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and
568  * converts it to the AT-SPI signal - 'object:text-selection-changed'
569  *
570  */
571 static gboolean
572 text_selection_changed_event_listener (GSignalInvocationHint *signal_hint,
573                                        guint n_param_values,
574                                        const GValue *param_values,
575                                        gpointer data)
576 {
577   AtkObject *accessible;
578   GSignalQuery signal_query;
579   const gchar *name, *minor;
580   gint detail1, detail2;
581
582   g_signal_query (signal_hint->signal_id, &signal_query);
583   name = signal_query.signal_name;
584
585   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
586   minor = g_quark_to_string (signal_hint->detail);
587
588   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
589         detail1 = g_value_get_int (&param_values[1]);
590
591   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
592         detail2 = g_value_get_int (&param_values[2]);
593
594   emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, "");
595   return TRUE;
596 }
597
598 /*---------------------------------------------------------------------------*/
599
600 /*
601  * Generic signal converter and forwarder.
602  *
603  * Klass (Interface) org.freedesktop.atspi.Event.Object
604  * Major is the signal name.
605  * Minor is NULL.
606  * detail1 is 0.
607  * detail2 is 0.
608  * any_data is NULL.
609  */
610 static gboolean
611 generic_event_listener (GSignalInvocationHint *signal_hint,
612                         guint n_param_values,
613                         const GValue *param_values,
614                         gpointer data)
615 {
616   AtkObject *accessible;
617   GSignalQuery signal_query;
618   const gchar *name;
619
620   g_signal_query (signal_hint->signal_id, &signal_query);
621   name = signal_query.signal_name;
622
623   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
624   emit(accessible, ITF_EVENT_OBJECT, name, "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
625   return TRUE;
626 }
627
628 /*---------------------------------------------------------------------------*/
629
630 /*
631  * Registers the provided function as a handler for the given signal name
632  * and stores the signal id returned so that the function may be
633  * de-registered later.
634  */
635 static void
636 add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
637 {
638   guint id;
639
640   id = atk_add_global_event_listener (listener, signal_name);
641   g_array_append_val (listener_ids, id);
642 }
643
644 /*
645  * Initialization for the signal handlers.
646  *
647  * Registers all required signal handlers.
648  */
649 void
650 spi_atk_register_event_listeners (void)
651 {
652   /*
653    * Kludge to make sure the Atk interface types are registered, otherwise
654    * the AtkText signal handlers below won't get registered
655    */
656   GObject   *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
657   AtkObject *bo = atk_no_op_object_new (ao);
658
659   g_object_unref (G_OBJECT (bo));
660   g_object_unref (ao);
661
662   /* Register for focus event notifications, and register app with central registry  */
663   listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
664
665   atk_bridge_focus_tracker_id = atk_add_focus_tracker (focus_tracker);
666
667   add_signal_listener (property_event_listener,               "Gtk:AtkObject:property-change");
668   add_signal_listener (window_event_listener,                 "window:create");
669   add_signal_listener (window_event_listener,                 "window:destroy");
670   add_signal_listener (window_event_listener,                 "window:minimize");
671   add_signal_listener (window_event_listener,                 "window:maximize");
672   add_signal_listener (window_event_listener,                 "window:restore");
673   add_signal_listener (window_event_listener,                 "window:activate");
674   add_signal_listener (window_event_listener,                 "window:deactivate");
675   add_signal_listener (document_event_listener,               "Gtk:AtkDocument:load-complete");
676   add_signal_listener (document_event_listener,               "Gtk:AtkDocument:reload");
677   add_signal_listener (document_event_listener,               "Gtk:AtkDocument:load-stopped");
678   add_signal_listener (state_event_listener,                  "Gtk:AtkObject:state-change");
679   add_signal_listener (active_descendant_event_listener,      "Gtk:AtkObject:active-descendant-changed");
680   add_signal_listener (bounds_event_listener,                 "Gtk:AtkComponent:bounds-changed");
681   add_signal_listener (text_selection_changed_event_listener, "Gtk:AtkText:text-selection-changed");
682   add_signal_listener (text_changed_event_listener,           "Gtk:AtkText:text-changed");
683   add_signal_listener (link_selected_event_listener,          "Gtk:AtkHypertext:link-selected");
684   add_signal_listener (generic_event_listener,                "Gtk:AtkObject:visible-data-changed");
685   add_signal_listener (generic_event_listener,                "Gtk:AtkSelection:selection-changed");
686   add_signal_listener (generic_event_listener,                "Gtk:AtkText:text-caret-moved");
687   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:row-inserted");
688   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:row-reordered");
689   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:row-deleted");
690   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:column-inserted");
691   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:column-reordered");
692   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:column-deleted");
693   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:model-changed");
694
695   /*
696    * May add the following listeners to implement preemptive key listening for GTK+
697    *
698    * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
699    * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
700    */
701   atk_bridge_key_event_listener_id = atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL);
702 }
703
704 /*---------------------------------------------------------------------------*/
705
706 /* 
707  * De-registers all ATK signal handlers.
708  */
709 void
710 spi_atk_deregister_event_listeners (void)
711 {
712   gint i;
713   GArray *ids = listener_ids;
714   listener_ids = NULL;
715
716   if (atk_bridge_focus_tracker_id)
717         atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
718   
719   for (i = 0; ids && i < ids->len; i++)
720     {
721           atk_remove_global_event_listener (g_array_index (ids, guint, i));
722     }
723   
724   if (atk_bridge_key_event_listener_id)
725           atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
726 }
727
728 /*---------------------------------------------------------------------------*/
729
730 /*
731  * TODO This function seems out of place here.
732  *
733  * Emits fake deactivate signals on all top-level windows.
734  * Used when shutting down AT-SPI, ensuring that all
735  * windows have been removed on the client side.
736  */
737 void
738 spi_atk_tidy_windows (void)
739 {
740   AtkObject *root;
741   gint n_children;
742   gint i;
743
744   root = atk_get_root ();
745   n_children = atk_object_get_n_accessible_children (root);
746   for (i = 0; i < n_children; i++)
747     {
748       AtkObject *child;
749       AtkStateSet *stateset;
750       const gchar *name;
751      
752       child = atk_object_ref_accessible_child (root, i);
753       stateset = atk_object_ref_state_set (child);
754       
755       name = atk_object_get_name (child);
756       if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
757         {
758           emit(child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
759         }
760       g_object_unref (stateset);
761
762       emit(child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
763       g_object_unref (child);
764     }
765 }
766
767 /*END------------------------------------------------------------------------*/