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