Finish off the adaptor changes for updating objects.
[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_REGISTRY,
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 /*
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
156  */
157 static const void *
158 provide_defaults(const gint type,
159                  const void *val)
160 {
161   switch (type)
162     {
163       case DBUS_TYPE_STRING:
164       case DBUS_TYPE_OBJECT_PATH:
165            if (!val)
166               return "";
167            else
168               return val;
169       default:
170            return val;
171     }
172 }
173
174 static void 
175 emit(AtkObject  *accessible,
176      const char *klass,
177      const char *major,
178      const char *minor,
179      dbus_int32_t detail1,
180      dbus_int32_t detail2,
181      const char *type,
182      const void *val)
183 {
184   DBusMessage *sig;
185   DBusMessageIter iter, sub;
186   gchar *path, *cname, *t;
187
188   path = atk_dbus_object_to_path (accessible);
189
190   /* Tough decision here
191    * We won't send events from accessible
192    * objects that have not yet been added to the accessible tree.
193    */
194   if (path == NULL)
195       return;
196
197   if (!klass) klass = "";
198   if (!major) major = "";
199   if (!minor) minor = "";
200
201   /*
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.
205    */
206   cname = g_strdup(major);
207   while ((t = strchr(cname, '-')) != NULL) *t = '_';
208
209   sig = dbus_message_new_signal(path, klass, cname);
210   g_free(cname);
211   g_free(path);
212
213   dbus_message_iter_init_append(sig, &iter);
214
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);
218
219   dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type, &sub);
220   /*
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
224    * integer.
225    */
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);
229
230   dbus_connection_send(atk_adaptor_app_data->bus, sig, NULL);
231   dbus_message_unref(sig);
232 }
233
234 /*---------------------------------------------------------------------------*/
235
236 /*
237  * Emits an AT-SPI event, marshalling a BoundingBox structure into the 
238  * 'any_data' variant of the event.
239  */
240 static void
241 emit_rect(AtkObject  *accessible,
242           const char *klass,
243           const char *major,
244           const char *minor,
245           AtkRectangle *rect)
246 {
247   DBusMessage *sig;
248   DBusMessageIter iter, variant, sub;
249   gchar *path, *cname, *t;
250   dbus_int32_t dummy = 0;
251
252   path = atk_dbus_object_to_path (accessible);
253
254   /* Tough decision here
255    * We won't send events from accessible
256    * objects that have not yet been added to the accessible tree.
257    */
258   if (path == NULL)
259       return;
260
261   if (!klass) klass = "";
262   if (!major) major = "";
263   if (!minor) minor = "";
264
265   /*
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.
269    */
270   cname = g_strdup(major);
271   while ((t = strchr(cname, '-')) != NULL) *t = '_';
272
273   sig = dbus_message_new_signal(path, klass, cname);
274   g_free(path);
275   g_free(cname);
276
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);
281
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);
290
291   dbus_connection_send(atk_adaptor_app_data->bus, sig, NULL);
292 }
293
294 /*---------------------------------------------------------------------------*/
295
296 /*
297  * The focus listener handles the ATK 'focus' signal and forwards it
298  * as the AT-SPI event, 'focus:'
299  */
300 static void
301 focus_tracker (AtkObject *accessible)
302 {
303   emit(accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
304 }
305
306 /*---------------------------------------------------------------------------*/
307
308 #define PCHANGE "property-change"
309
310 /* 
311  * This handler handles the following ATK signals and
312  * converts them to AT-SPI events:
313  *  
314  * Gtk:AtkObject:property-change -> object:property-change:(property-name)
315  *
316  * The property-name is part of the ATK property-change signal.
317  */
318 static gboolean
319 property_event_listener (GSignalInvocationHint *signal_hint,
320                          guint                  n_param_values,
321                          const GValue          *param_values,
322                          gpointer               data)
323 {
324   AtkObject *accessible;
325   AtkPropertyValues *values;
326
327   const gchar *pname = NULL;
328
329   AtkObject *otemp;
330   const gchar *stemp;
331   gint i;
332   
333   accessible = g_value_get_object (&param_values[0]);
334   values = (AtkPropertyValues*) g_value_get_pointer (&param_values[1]);
335
336   pname = values[0].property_name;
337
338   /* TODO Could improve this control statement by matching
339    * on only the end of the signal names,
340    */
341   if (strcmp (pname, "accessible-table-summary") == 0)
342     {
343       otemp = atk_table_get_summary(ATK_TABLE (accessible));
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-column-header") == 0)
349     {
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);
353       if (stemp != NULL)
354           emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
355     }
356   else if (strcmp (pname, "accessible-table-row-header") == 0)
357     {
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);
361       if (stemp != NULL)
362           emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
363     }
364   else if (strcmp (pname, "accessible-table-row-description") == 0)
365     {
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);
369     }
370   else if (strcmp (pname, "accessible-table-column-description") == 0)
371     {
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);
375     }
376   else if (strcmp (pname, "accessible-table-caption-object") == 0)
377     {
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);
381     }
382   else
383     {
384       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
385     }
386   return TRUE;
387 }
388
389 /*---------------------------------------------------------------------------*/
390
391 #define STATE_CHANGED "state-changed"
392
393 /*
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.
397  */
398 static gboolean
399 state_event_listener (GSignalInvocationHint *signal_hint,
400                       guint n_param_values,
401                       const GValue *param_values,
402                       gpointer data)
403 {
404   AtkObject *accessible;
405   gchar *pname;
406   guint detail1;
407
408   accessible = ATK_OBJECT(g_value_get_object (&param_values[0]));
409   pname = g_strdup (g_value_get_string (&param_values[1]));
410
411   /* TODO - Possibly ignore a change to the 'defunct' state.
412    * This is because without reference counting defunct objects should be removed.
413    */
414   detail1 = (g_value_get_boolean (&param_values[2])) ? 1 : 0;
415   emit(accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
416   g_free (pname);
417   return TRUE;
418 }
419
420 /*---------------------------------------------------------------------------*/
421
422 /*
423  * The window event listener handles the following ATK signals and forwards
424  * them as AT-SPI events:
425  *
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
432  */
433 static gboolean
434 window_event_listener (GSignalInvocationHint *signal_hint,
435                        guint n_param_values,
436                        const GValue *param_values,
437                        gpointer data)
438 {
439   AtkObject *accessible;
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   s = atk_object_get_name (accessible);
448   emit(accessible, ITF_EVENT_WINDOW, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
449
450   return TRUE;
451 }
452
453 /*---------------------------------------------------------------------------*/
454
455 /* 
456  * The document event listener handles the following ATK signals
457  * and converts them to AT-SPI events:
458  *
459  * Gtk:AtkDocument:load-complete ->  document:load-complete
460  * Gtk:AtkDocument:load-stopped  ->  document:load-stopped
461  * Gtk:AtkDocument:reload        ->  document:reload
462  */
463 static gboolean
464 document_event_listener (GSignalInvocationHint *signal_hint,
465                          guint n_param_values,
466                          const GValue *param_values,
467                          gpointer data)
468 {
469   AtkObject *accessible;
470   GSignalQuery signal_query;
471   const gchar *name, *s;
472
473   g_signal_query (signal_hint->signal_id, &signal_query);
474   name = signal_query.signal_name;
475
476   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
477   s = atk_object_get_name (accessible);
478   emit(accessible, ITF_EVENT_DOCUMENT, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
479
480   return TRUE;
481 }
482
483 /*---------------------------------------------------------------------------*/
484
485 /*
486  * Signal handler for  "Gtk:AtkComponent:bounds-changed". Converts
487  * this to an AT-SPI event - "object:bounds-changed".
488  */
489 static gboolean
490 bounds_event_listener (GSignalInvocationHint *signal_hint,
491                        guint n_param_values,
492                        const GValue *param_values,
493                        gpointer data)
494 {
495   AtkObject *accessible;
496   AtkRectangle *atk_rect;
497   GSignalQuery signal_query;
498   const gchar *name, *s;
499
500   g_signal_query (signal_hint->signal_id, &signal_query);
501   name = signal_query.signal_name;
502
503   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
504
505   if (G_VALUE_HOLDS_BOXED (param_values + 1))
506     atk_rect = g_value_get_boxed (param_values + 1);
507
508   emit_rect(accessible, ITF_EVENT_OBJECT, name, "", atk_rect);
509   return TRUE;
510 }
511
512 /*---------------------------------------------------------------------------*/
513
514 /* 
515  * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and 
516  * converts it to the AT-SPI signal - 'object:active-descendant-changed'.
517  *
518  */
519 static gboolean
520 active_descendant_event_listener (GSignalInvocationHint *signal_hint,
521                                   guint n_param_values,
522                                   const GValue *param_values,
523                                   gpointer data)
524 {
525   AtkObject *accessible;
526   AtkObject *child;
527   GSignalQuery signal_query;
528   const gchar *name, *minor;
529   gchar *s;
530   gint detail1;
531
532   g_signal_query (signal_hint->signal_id, &signal_query);
533   name = signal_query.signal_name;
534
535   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
536   child = ATK_OBJECT(g_value_get_pointer (&param_values[1]));
537   g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
538   minor = g_quark_to_string (signal_hint->detail);
539
540   detail1 = atk_object_get_index_in_parent (child);
541   s = atk_dbus_object_to_path (child);
542   if (s == NULL)
543     {
544       g_free (s);
545       return TRUE;
546     }
547
548   emit(accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, s);
549   g_free(s);
550   return TRUE;
551 }
552
553 /*---------------------------------------------------------------------------*/
554
555 /* 
556  * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and
557  * converts it to the AT-SPI signal - 'object:link-selected'
558  *
559  */
560 static gboolean
561 link_selected_event_listener (GSignalInvocationHint *signal_hint,
562                               guint n_param_values,
563                               const GValue *param_values,
564                               gpointer data)
565 {
566   AtkObject *accessible;
567   GSignalQuery signal_query;
568   const gchar *name, *minor;
569   gint detail1;
570
571   g_signal_query (signal_hint->signal_id, &signal_query);
572   name = signal_query.signal_name;
573
574   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
575   minor = g_quark_to_string (signal_hint->detail);
576
577   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
578         detail1 = g_value_get_int (&param_values[1]);
579
580   emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
581   return TRUE;
582 }
583
584 /*---------------------------------------------------------------------------*/
585
586 /* 
587  * Handles the ATK signal 'Gtk:AtkText:text-changed' and
588  * converts it to the AT-SPI signal - 'object:text-changed'
589  *
590  */
591 static gboolean
592 text_changed_event_listener (GSignalInvocationHint *signal_hint,
593                              guint n_param_values,
594                              const GValue *param_values,
595                              gpointer data)
596 {
597   AtkObject *accessible;
598   GSignalQuery signal_query;
599   const gchar *name, *minor;
600   gchar *selected;
601   gint detail1, detail2;
602
603   g_signal_query (signal_hint->signal_id, &signal_query);
604   name = signal_query.signal_name;
605
606   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
607   minor = g_quark_to_string (signal_hint->detail);
608
609   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
610         detail1 = g_value_get_int (&param_values[1]);
611
612   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
613         detail2 = g_value_get_int (&param_values[2]);
614
615   selected = atk_text_get_text (ATK_TEXT (accessible), detail1, detail1+detail2);
616
617   emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, selected);
618   return TRUE;
619 }
620
621 /*---------------------------------------------------------------------------*/
622
623 /* 
624  * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and
625  * converts it to the AT-SPI signal - 'object:text-selection-changed'
626  *
627  */
628 static gboolean
629 text_selection_changed_event_listener (GSignalInvocationHint *signal_hint,
630                                        guint n_param_values,
631                                        const GValue *param_values,
632                                        gpointer data)
633 {
634   AtkObject *accessible;
635   GSignalQuery signal_query;
636   const gchar *name, *minor;
637   gint detail1, detail2;
638
639   g_signal_query (signal_hint->signal_id, &signal_query);
640   name = signal_query.signal_name;
641
642   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
643   minor = g_quark_to_string (signal_hint->detail);
644
645   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
646         detail1 = g_value_get_int (&param_values[1]);
647
648   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
649         detail2 = g_value_get_int (&param_values[2]);
650
651   emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, "");
652   return TRUE;
653 }
654
655 /*---------------------------------------------------------------------------*/
656
657 /*
658  * Generic signal converter and forwarder.
659  *
660  * Klass (Interface) org.freedesktop.atspi.Event.Object
661  * Major is the signal name.
662  * Minor is NULL.
663  * detail1 is 0.
664  * detail2 is 0.
665  * any_data is NULL.
666  */
667 static gboolean
668 generic_event_listener (GSignalInvocationHint *signal_hint,
669                         guint n_param_values,
670                         const GValue *param_values,
671                         gpointer data)
672 {
673   AtkObject *accessible;
674   GSignalQuery signal_query;
675   const gchar *name;
676
677   g_signal_query (signal_hint->signal_id, &signal_query);
678   name = signal_query.signal_name;
679
680   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
681   emit(accessible, ITF_EVENT_OBJECT, name, "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
682   return TRUE;
683 }
684
685 /*---------------------------------------------------------------------------*/
686
687 /*
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.
691  */
692 static void
693 add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
694 {
695   guint id;
696
697   id = atk_add_global_event_listener (listener, signal_name);
698   g_array_append_val (listener_ids, id);
699 }
700
701 /*
702  * Initialization for the signal handlers.
703  *
704  * Registers all required signal handlers.
705  */
706 void
707 spi_atk_register_event_listeners (void)
708 {
709   /*
710    * Kludge to make sure the Atk interface types are registered, otherwise
711    * the AtkText signal handlers below won't get registered
712    */
713   GObject   *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
714   AtkObject *bo = atk_no_op_object_new (ao);
715
716   g_object_unref (G_OBJECT (bo));
717   g_object_unref (ao);
718
719   /* Register for focus event notifications, and register app with central registry  */
720   listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
721
722   atk_bridge_focus_tracker_id = atk_add_focus_tracker (focus_tracker);
723
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");
751
752   /*
753    * May add the following listeners to implement preemptive key listening for GTK+
754    *
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");
757    */
758   atk_bridge_key_event_listener_id = atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL);
759 }
760
761 /*---------------------------------------------------------------------------*/
762
763 /* 
764  * De-registers all ATK signal handlers.
765  */
766 void
767 spi_atk_deregister_event_listeners (void)
768 {
769   gint i;
770   GArray *ids = listener_ids;
771   listener_ids = NULL;
772
773   if (atk_bridge_focus_tracker_id)
774         atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
775   
776   for (i = 0; ids && i < ids->len; i++)
777     {
778           atk_remove_global_event_listener (g_array_index (ids, guint, i));
779     }
780   
781   if (atk_bridge_key_event_listener_id)
782           atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
783 }
784
785 /*---------------------------------------------------------------------------*/
786
787 /*
788  * TODO This function seems out of place here.
789  *
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.
793  */
794 void
795 spi_atk_tidy_windows (void)
796 {
797   AtkObject *root;
798   gint n_children;
799   gint i;
800
801   root = atk_get_root ();
802   n_children = atk_object_get_n_accessible_children (root);
803   for (i = 0; i < n_children; i++)
804     {
805       AtkObject *child;
806       AtkStateSet *stateset;
807       const gchar *name;
808      
809       child = atk_object_ref_accessible_child (root, i);
810       stateset = atk_object_ref_state_set (child);
811       
812       name = atk_object_get_name (child);
813       if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
814         {
815           emit(child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
816         }
817       g_object_unref (stateset);
818
819       emit(child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
820       g_object_unref (child);
821     }
822 }
823
824 /*END------------------------------------------------------------------------*/