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