Merge branch 'master' into p2p
[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 "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.a11y.atspi.Event.Object"
43 #define ITF_EVENT_WINDOW   "org.a11y.atspi.Event.Window"
44 #define ITF_EVENT_DOCUMENT "org.a11y.atspi.Event.Document"
45 #define ITF_EVENT_FOCUS    "org.a11y.atspi.Event.Focus"
46
47 /*---------------------------------------------------------------------------*/
48
49 typedef struct _SpiReentrantCallClosure 
50 {
51   GMainLoop   *loop;
52   DBusMessage *reply;
53 } SpiReentrantCallClosure;
54
55 static void
56 switch_main_context (GMainContext *cnx)
57 {
58   GList *list;
59
60   dbus_server_setup_with_g_main (spi_global_app_data->server, cnx);
61   dbus_connection_setup_with_g_main (spi_global_app_data->bus, cnx);
62   for (list = spi_global_app_data->direct_connections; list; list = list->next)
63     dbus_connection_setup_with_g_main (list->data, cnx);
64 }
65
66 static void
67 set_reply (DBusPendingCall * pending, void *user_data)
68 {
69   SpiReentrantCallClosure* closure = (SpiReentrantCallClosure *) user_data; 
70
71   closure->reply = dbus_pending_call_steal_reply (pending);
72   switch_main_context (NULL);
73   g_main_loop_quit (closure->loop);
74 }
75
76 static DBusMessage *
77 send_and_allow_reentry (DBusConnection * bus, DBusMessage * message)
78 {
79   DBusPendingCall *pending;
80   SpiReentrantCallClosure closure;
81
82   closure.loop = g_main_loop_new (spi_global_app_data->main_context, FALSE);
83   switch_main_context (spi_global_app_data->main_context);
84
85   if (!dbus_connection_send_with_reply (bus, message, &pending, -1))
86     {
87       switch_main_context (NULL);
88       return NULL;
89     }
90   dbus_pending_call_set_notify (pending, set_reply, (void *) &closure, NULL);
91   g_main_loop_run  (closure.loop);
92   
93   g_main_loop_unref (closure.loop);
94   return closure.reply;
95 }
96
97 /*---------------------------------------------------------------------------*/
98
99 /*
100  * Functionality related to sending device events from the application.
101  *
102  * This is used for forwarding key events on to the registry daemon.
103  */
104
105 static gboolean
106 Accessibility_DeviceEventController_NotifyListenersSync (const
107                                                          Accessibility_DeviceEvent
108                                                          * key_event)
109 {
110   DBusMessage *message;
111   DBusError error;
112   dbus_bool_t consumed = FALSE;
113
114   message =
115     dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY,
116                                   SPI_DBUS_PATH_DEC,
117                                   SPI_DBUS_INTERFACE_DEC,
118                                   "NotifyListenersSync");
119
120   dbus_error_init (&error);
121   if (spi_dbus_marshal_deviceEvent (message, key_event))
122     {
123       DBusMessage *reply =
124         send_and_allow_reentry (spi_global_app_data->bus, message);
125       if (reply)
126         {
127           DBusError error;
128           dbus_error_init (&error);
129           dbus_message_get_args (reply, &error, DBUS_TYPE_BOOLEAN, &consumed,
130                                  DBUS_TYPE_INVALID);
131           dbus_message_unref (reply);
132         }
133     }
134   dbus_message_unref (message);
135   return consumed;
136 }
137
138 static void
139 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent * keystroke,
140                                        AtkKeyEventStruct * event)
141 {
142   keystroke->id = (dbus_int32_t) event->keyval;
143   keystroke->hw_code = (dbus_int16_t) event->keycode;
144   keystroke->timestamp = (dbus_uint32_t) event->timestamp;
145   keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF);
146   if (event->string)
147     {
148       gunichar c;
149
150       keystroke->event_string = g_strdup (event->string);
151       c = g_utf8_get_char_validated (event->string, -1);
152       if (c > 0 && g_unichar_isprint (c))
153         keystroke->is_text = TRUE;
154       else
155         keystroke->is_text = FALSE;
156     }
157   else
158     {
159       keystroke->event_string = g_strdup ("");
160       keystroke->is_text = FALSE;
161     }
162   switch (event->type)
163     {
164     case (ATK_KEY_EVENT_PRESS):
165       keystroke->type = Accessibility_KEY_PRESSED_EVENT;
166       break;
167     case (ATK_KEY_EVENT_RELEASE):
168       keystroke->type = Accessibility_KEY_RELEASED_EVENT;
169       break;
170     default:
171       keystroke->type = 0;
172       break;
173     }
174 #if 0
175   g_print
176     ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
177      (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
178      (int) keystroke->modifiers, keystroke->event_string,
179      (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
180 #endif
181 }
182
183
184 static gint
185 spi_atk_bridge_key_listener (AtkKeyEventStruct * event, gpointer data)
186 {
187   gboolean result;
188   Accessibility_DeviceEvent key_event;
189
190   spi_init_keystroke_from_atk_key_event (&key_event, event);
191
192   result =
193     Accessibility_DeviceEventController_NotifyListenersSync (&key_event);
194
195   if (key_event.event_string)
196     g_free (key_event.event_string);
197
198   return result;
199 }
200
201 /*---------------------------------------------------------------------------*/
202
203 static gchar *
204 convert_signal_name (const gchar * s)
205 {
206   gchar *ret = g_strdup (s);
207   gchar *t;
208
209   if (!ret)
210     return NULL;
211   ret[0] = toupper (ret[0]);
212   while ((t = strchr (ret, '-')) != NULL)
213     {
214       memmove (t, t + 1, strlen (t));
215       *t = toupper (*t);
216     }
217   return ret;
218 }
219
220 static const void *
221 replace_null (const gint type,
222               const void *val)
223 {
224   switch (type)
225     {
226       case DBUS_TYPE_STRING:
227       case DBUS_TYPE_OBJECT_PATH:
228            if (!val)
229               return "";
230            else
231               return val;
232       default:
233            return val;
234     }
235 }
236
237 static void
238 append_basic (DBusMessageIter *iter,
239               const char *type,
240               const void *val)
241 {
242   DBusMessageIter sub;
243
244   dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &sub);
245
246     val = replace_null ((int) *type, val);
247     dbus_message_iter_append_basic(&sub, (int) *type, &val);
248
249   dbus_message_iter_close_container(iter, &sub);
250 }
251
252 static void
253 append_rect (DBusMessageIter *iter,
254              const char *type,
255              const void *val)
256 {
257   DBusMessageIter variant, sub;
258   const AtkRectangle *rect = (const AtkRectangle *) val;
259
260   dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &variant);
261
262     dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub);
263
264       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x));
265       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y));
266       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width));
267       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height));
268
269     dbus_message_iter_close_container (&variant, &sub);
270
271   dbus_message_iter_close_container(iter, &variant);
272 }
273
274 static void
275 append_object (DBusMessageIter *iter,
276                const char *type,
277                const void *val)
278 {
279   spi_object_append_v_reference (iter, ATK_OBJECT (val));
280 }
281
282 static gchar *
283 signal_name_to_dbus (const gchar *s)
284 {
285   gchar *ret = g_strdup (s);
286   gchar *t;
287
288   if (!ret)
289     return NULL;
290   ret [0] = toupper (ret [0]);
291   while ((t = strchr (ret, '-')) != NULL)
292   {
293     memmove (t, t + 1, strlen (t));
294     *t = toupper (*t);
295   }
296   return ret;
297 }
298
299 /*
300  * Converts names of the form "active-descendant-changed" to
301  * "ActiveDescendantChanged"
302  */
303 static gchar *
304 ensure_proper_format (const char *name)
305 {
306   gchar *ret = (gchar *) g_malloc (strlen (name) * 2 + 2);
307   gchar *p = ret;
308   gboolean need_upper = TRUE;
309
310   if (!ret)
311     return NULL;
312   while (*name)
313     {
314       if (need_upper)
315         {
316           *p++ = toupper (*name);
317           need_upper = FALSE;
318         }
319       else if (*name == '-')
320         need_upper = TRUE;
321       else if (*name == ':')
322         {
323           need_upper = TRUE;
324           *p++ = *name;
325         }
326       else
327         *p++ = *name;
328       name++;
329     }
330   *p = '\0';
331   return ret;
332 }
333
334 static gboolean
335 signal_is_needed (const gchar *klass, const gchar *major, const gchar *minor)
336 {
337   gchar *data [4];
338   GList *iter;
339   event_data *evdata;
340   gboolean ret = FALSE;
341   GList *list;
342
343   data [0] = ensure_proper_format (klass + 21);
344   data [1] = ensure_proper_format (major);
345   data [2] = ensure_proper_format (minor);
346   data [3] = NULL;
347
348   /* Hack: events such as "object::text-changed::insert:system" as
349      generated by Gecko */
350   data [2][strcspn (data [2], ":")] = '\0';
351   for (list = spi_global_app_data->events; list; list = list->next)
352     {
353       evdata = list->data;
354       if (spi_event_is_subtype (data, evdata->data))
355         {
356           ret = TRUE;
357           break;
358         }
359     }
360
361   g_free (data [2]);
362   g_free (data [1]);
363   g_free (data [0]);
364   return ret;
365 }
366
367 /*
368  * Emits an AT-SPI event.
369  * AT-SPI events names are split into three parts:
370  * class:major:minor
371  * This is mapped onto D-Bus events as:
372  * D-Bus Interface:Signal Name:Detail argument
373  *
374  * Marshals a basic type into the 'any_data' attribute of
375  * the AT-SPI event.
376  */
377 static void 
378 emit_event (AtkObject  *obj,
379             const char *klass,
380             const char *major,
381             const char *minor,
382             dbus_int32_t detail1,
383             dbus_int32_t detail2,
384             const char *type,
385             const void *val,
386             void (*append_variant) (DBusMessageIter *, const char *, const void *))
387 {
388   DBusConnection *bus = spi_global_app_data->bus;
389   const char *path =  spi_register_object_to_path (spi_global_register,
390                                                    G_OBJECT (obj));
391
392   gchar *cname, *t;
393   DBusMessage *sig;
394   DBusMessageIter iter, iter_struct;
395   
396   if (!klass) klass = "";
397   if (!major) major = "";
398   if (!minor) minor = "";
399   if (!type) type = "u";
400
401   if (!signal_is_needed (klass, major, minor))
402     return;
403
404   /*
405    * This is very annoying, but as '-' isn't a legal signal
406    * name in D-Bus (Why not??!?) The names need converting
407    * on this side, and again on the client side.
408    */
409   cname = signal_name_to_dbus (major);
410   sig = dbus_message_new_signal(path, klass, cname);
411   g_free(cname);
412
413   dbus_message_iter_init_append(sig, &iter);
414
415   dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
416   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1);
417   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2);
418   append_variant (&iter, type, val);
419   spi_object_append_reference (&iter, spi_global_app_data->root);
420
421   dbus_connection_send(bus, sig, NULL);
422   dbus_message_unref(sig);
423
424   if (g_strcmp0 (cname, "ChildrenChanged") != 0)
425     spi_object_lease_if_needed (G_OBJECT (obj));
426 }
427
428 /*---------------------------------------------------------------------------*/
429
430 /*
431  * The focus listener handles the ATK 'focus' signal and forwards it
432  * as the AT-SPI event, 'focus:'
433  */
434 static void
435 focus_tracker (AtkObject * accessible)
436 {
437   emit_event (accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0,
438               DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
439 }
440
441 /*---------------------------------------------------------------------------*/
442
443 #define PCHANGE "PropertyChange"
444
445 /* 
446  * This handler handles the following ATK signals and
447  * converts them to AT-SPI events:
448  *  
449  * Gtk:AtkObject:property-change -> object:property-change:(property-name)
450  *
451  * The property-name is part of the ATK property-change signal.
452  */
453 static gboolean
454 property_event_listener (GSignalInvocationHint * signal_hint,
455                          guint n_param_values,
456                          const GValue * param_values, gpointer data)
457 {
458   AtkObject *accessible;
459   AtkPropertyValues *values;
460
461   const gchar *pname = NULL;
462
463   AtkObject *otemp;
464   const gchar *s1, s2;
465   gint i;
466
467   accessible = g_value_get_object (&param_values[0]);
468   values = (AtkPropertyValues *) g_value_get_pointer (&param_values[1]);
469
470   pname = values[0].property_name;
471
472   /* TODO Could improve this control statement by matching
473    * on only the end of the signal names,
474    */
475   if (strcmp (pname, "accessible-name") == 0)
476     {
477       s1 = atk_object_get_name (accessible);
478       if (s1 != NULL)
479         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
480                     DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
481     }
482   else if (strcmp (pname, "accessible-description") == 0)
483     {
484       s1 = atk_object_get_description (accessible);
485       if (s1 != NULL)
486         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
487                     DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
488     }
489   else if (strcmp (pname, "accessible-parent") == 0)
490     {
491       otemp = atk_object_get_parent (accessible);
492       if (otemp != NULL)
493         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
494                     "(so)", otemp, append_object);
495     }
496   else if (strcmp (pname, "accessible-role") == 0)
497     {
498       i = atk_object_get_role (accessible);
499       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
500                     DBUS_TYPE_UINT32_AS_STRING, GINT_TO_POINTER(i), append_basic);
501     }
502   else if (strcmp (pname, "accessible-table-summary") == 0)
503     {
504       otemp = atk_table_get_summary (ATK_TABLE (accessible));
505       if (otemp != NULL)
506         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
507                     "(so)", otemp, append_object);
508     }
509   else if (strcmp (pname, "accessible-table-column-header") == 0)
510     {
511       i = g_value_get_int (&(values->new_value));
512       otemp = atk_table_get_column_header (ATK_TABLE (accessible), i);
513       if (otemp != NULL)
514         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
515                     "(so)", otemp, append_object);
516     }
517   else if (strcmp (pname, "accessible-table-row-header") == 0)
518     {
519       i = g_value_get_int (&(values->new_value));
520       otemp = atk_table_get_row_header (ATK_TABLE (accessible), i);
521       if (otemp != NULL)
522         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
523                     "(so)", otemp, append_object);
524     }
525   else if (strcmp (pname, "accessible-table-row-description") == 0)
526     {
527       i = g_value_get_int (&(values->new_value));
528       s1 = atk_table_get_row_description (ATK_TABLE (accessible), i);
529       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
530                   DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
531     }
532   else if (strcmp (pname, "accessible-table-column-description") == 0)
533     {
534       i = g_value_get_int (&(values->new_value));
535       s1 = atk_table_get_column_description (ATK_TABLE (accessible), i);
536       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
537                   DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
538     }
539   else if (strcmp (pname, "accessible-table-caption-object") == 0)
540     {
541       otemp = atk_table_get_caption (ATK_TABLE (accessible));
542       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
543                   "(so)", otemp, append_object);
544     }
545   else
546     {
547       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
548             DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
549     }
550   return TRUE;
551 }
552
553 /*---------------------------------------------------------------------------*/
554
555 #define STATE_CHANGED "state-changed"
556
557 /*
558  * The state event listener handles 'Gtk:AtkObject:state-change' ATK signals
559  * and forwards them as object:state-changed:(param-name) AT-SPI events. Where
560  * the param-name is part of the ATK state-change signal.
561  */
562 static gboolean
563 state_event_listener (GSignalInvocationHint * signal_hint,
564                       guint n_param_values,
565                       const GValue * param_values, gpointer data)
566 {
567   AtkObject *accessible;
568   gchar *pname;
569   guint detail1;
570
571   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
572   pname = g_strdup (g_value_get_string (&param_values[1]));
573
574   /* TODO - Possibly ignore a change to the 'defunct' state.
575    * This is because without reference counting defunct objects should be removed.
576    */
577   detail1 = (g_value_get_boolean (&param_values[2])) ? 1 : 0;
578   emit_event (accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0,
579               DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
580   g_free (pname);
581   return TRUE;
582 }
583
584 /*---------------------------------------------------------------------------*/
585
586 /*
587  * The window event listener handles the following ATK signals and forwards
588  * them as AT-SPI events:
589  *
590  * window:create     -> window:create
591  * window:destroy    -> window:destroy
592  * window:minimize   -> window:minimize
593  * window:maximize   -> window:maximize
594  * window:activate   -> window:activate
595  * window:deactivate -> window:deactivate
596  */
597 static gboolean
598 window_event_listener (GSignalInvocationHint * signal_hint,
599                        guint n_param_values,
600                        const GValue * param_values, gpointer data)
601 {
602   AtkObject *accessible;
603   GSignalQuery signal_query;
604   const gchar *name, *s;
605
606   g_signal_query (signal_hint->signal_id, &signal_query);
607   name = signal_query.signal_name;
608
609   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
610   s = atk_object_get_name (accessible);
611   emit_event (accessible, ITF_EVENT_WINDOW, name, "", 0, 0,
612               DBUS_TYPE_STRING_AS_STRING, s, append_basic);
613
614   return TRUE;
615 }
616
617 /*---------------------------------------------------------------------------*/
618
619 /* 
620  * The document event listener handles the following ATK signals
621  * and converts them to AT-SPI events:
622  *
623  * Gtk:AtkDocument:load-complete ->  document:load-complete
624  * Gtk:AtkDocument:load-stopped  ->  document:load-stopped
625  * Gtk:AtkDocument:reload        ->  document:reload
626  */
627 static gboolean
628 document_event_listener (GSignalInvocationHint * signal_hint,
629                          guint n_param_values,
630                          const GValue * param_values, gpointer data)
631 {
632   AtkObject *accessible;
633   GSignalQuery signal_query;
634   const gchar *name, *s;
635
636   g_signal_query (signal_hint->signal_id, &signal_query);
637   name = signal_query.signal_name;
638
639   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
640   s = atk_object_get_name (accessible);
641   emit_event (accessible, ITF_EVENT_DOCUMENT, name, "", 0, 0,
642               DBUS_TYPE_STRING_AS_STRING, s, append_basic);
643
644   return TRUE;
645 }
646
647 /*---------------------------------------------------------------------------*/
648
649 /*
650  * Signal handler for  "Gtk:AtkComponent:bounds-changed". Converts
651  * this to an AT-SPI event - "object:bounds-changed".
652  */
653 static gboolean
654 bounds_event_listener (GSignalInvocationHint * signal_hint,
655                        guint n_param_values,
656                        const GValue * param_values, gpointer data)
657 {
658   AtkObject *accessible;
659   AtkRectangle *atk_rect;
660   GSignalQuery signal_query;
661   const gchar *name, *s;
662
663   g_signal_query (signal_hint->signal_id, &signal_query);
664   name = signal_query.signal_name;
665
666   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
667
668   if (G_VALUE_HOLDS_BOXED (param_values + 1))
669   {
670     atk_rect = g_value_get_boxed (param_values + 1);
671
672     emit_event (accessible, ITF_EVENT_OBJECT, name, "", 0, 0,
673                 "(iiii)", atk_rect, append_rect);
674   }
675   return TRUE;
676 }
677
678 /*---------------------------------------------------------------------------*/
679
680 /* 
681  * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and 
682  * converts it to the AT-SPI signal - 'object:active-descendant-changed'.
683  *
684  */
685 static gboolean
686 active_descendant_event_listener (GSignalInvocationHint * signal_hint,
687                                   guint n_param_values,
688                                   const GValue * param_values, gpointer data)
689 {
690   AtkObject *accessible;
691   AtkObject *child;
692   GSignalQuery signal_query;
693   const gchar *name, *minor;
694   gint detail1;
695
696   g_signal_query (signal_hint->signal_id, &signal_query);
697   name = signal_query.signal_name;
698
699   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
700   child = ATK_OBJECT (g_value_get_pointer (&param_values[1]));
701   g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
702   minor = g_quark_to_string (signal_hint->detail);
703
704   detail1 = atk_object_get_index_in_parent (child);
705
706   emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, 0,
707               "(so)", child, append_object);
708   return TRUE;
709 }
710
711 /*---------------------------------------------------------------------------*/
712
713 /* 
714  * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and
715  * converts it to the AT-SPI signal - 'object:link-selected'
716  *
717  */
718 static gboolean
719 link_selected_event_listener (GSignalInvocationHint * signal_hint,
720                               guint n_param_values,
721                               const GValue * param_values, gpointer data)
722 {
723   AtkObject *accessible;
724   GSignalQuery signal_query;
725   const gchar *name, *minor;
726   gint detail1;
727
728   g_signal_query (signal_hint->signal_id, &signal_query);
729   name = signal_query.signal_name;
730
731   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
732   minor = g_quark_to_string (signal_hint->detail);
733
734   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
735     detail1 = g_value_get_int (&param_values[1]);
736
737   emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0,
738               DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
739   return TRUE;
740 }
741
742 /*---------------------------------------------------------------------------*/
743
744 /* 
745  * Handles the ATK signal 'Gtk:AtkText:text-changed' and
746  * converts it to the AT-SPI signal - 'object:text-changed'
747  *
748  */
749 static gboolean
750 text_changed_event_listener (GSignalInvocationHint * signal_hint,
751                              guint n_param_values,
752                              const GValue * param_values, gpointer data)
753 {
754   AtkObject *accessible;
755   GSignalQuery signal_query;
756   const gchar *name, *minor;
757   gchar *selected;
758   gint detail1, detail2;
759
760   g_signal_query (signal_hint->signal_id, &signal_query);
761   name = signal_query.signal_name;
762
763   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
764   minor = g_quark_to_string (signal_hint->detail);
765
766   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
767     detail1 = g_value_get_int (&param_values[1]);
768
769   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
770     detail2 = g_value_get_int (&param_values[2]);
771
772   selected =
773     atk_text_get_text (ATK_TEXT (accessible), detail1, detail1 + detail2);
774
775   emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
776               DBUS_TYPE_STRING_AS_STRING, selected, append_basic);
777   return TRUE;
778 }
779
780 /*---------------------------------------------------------------------------*/
781
782 /* 
783  * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and
784  * converts it to the AT-SPI signal - 'object:text-selection-changed'
785  *
786  */
787 static gboolean
788 text_selection_changed_event_listener (GSignalInvocationHint * signal_hint,
789                                        guint n_param_values,
790                                        const GValue * param_values,
791                                        gpointer data)
792 {
793   AtkObject *accessible;
794   GSignalQuery signal_query;
795   const gchar *name, *minor;
796   gint detail1, detail2;
797
798   g_signal_query (signal_hint->signal_id, &signal_query);
799   name = signal_query.signal_name;
800
801   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
802   minor = g_quark_to_string (signal_hint->detail);
803
804   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
805     detail1 = g_value_get_int (&param_values[1]);
806
807   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
808     detail2 = g_value_get_int (&param_values[2]);
809
810   emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
811               DBUS_TYPE_STRING_AS_STRING, "", append_basic);
812   return TRUE;
813 }
814
815 /*---------------------------------------------------------------------------*/
816
817 /*
818  * Children changed signal converter and forwarder.
819  *
820  * Klass (Interface) org.a11y.atspi.Event.Object
821  * Major is the signal name.
822  * Minor is 'add' or 'remove'
823  * detail1 is the index.
824  * detail2 is 0.
825  * any_data is the child reference.
826  */
827 static gboolean
828 children_changed_event_listener (GSignalInvocationHint * signal_hint,
829                                  guint n_param_values,
830                                  const GValue * param_values, gpointer data)
831 {
832   GSignalQuery signal_query;
833   const gchar *name, *minor;
834   gint detail1, detail2 = 0;
835
836   AtkObject *accessible, *ao=NULL;
837   gpointer child;
838
839   g_signal_query (signal_hint->signal_id, &signal_query);
840   name = signal_query.signal_name;
841
842   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
843   minor = g_quark_to_string (signal_hint->detail);
844
845   detail1 = g_value_get_uint (param_values + 1);
846   child = g_value_get_pointer (param_values + 2);
847
848   if (ATK_IS_OBJECT (child))
849     {
850       ao = ATK_OBJECT (child);
851       emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
852                   "(so)", ao, append_object);
853     }
854   else if ((minor != NULL) && (strcmp (minor, "add") == 0))
855     {
856       ao = atk_object_ref_accessible_child (accessible, 
857                                             detail1);
858       emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
859                   "(so)", ao, append_object);
860     }
861   else
862     {
863       emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
864                   "(so)", ao, append_object);
865     }
866  
867   return TRUE;
868 }
869
870 /*---------------------------------------------------------------------------*/
871
872 static void
873 toplevel_added_event_listener (AtkObject * accessible,
874                                guint index, AtkObject * child)
875 {
876   emit_event (accessible, ITF_EVENT_OBJECT, "children-changed", "add", index, 0,
877               "(so)", child, append_object);
878 }
879
880 static void
881 toplevel_removed_event_listener (AtkObject * accessible,
882                                  guint index, AtkObject * child)
883 {
884   emit_event (accessible, ITF_EVENT_OBJECT, "children-changed", "remove", index, 0,
885               "(so)", child, append_object);
886 }
887
888 /*---------------------------------------------------------------------------*/
889
890 /*
891  * Generic signal converter and forwarder.
892  *
893  * Klass (Interface) org.a11y.atspi.Event.Object
894  * Major is the signal name.
895  * Minor is NULL.
896  * detail1 is 0.
897  * detail2 is 0.
898  * any_data is NULL.
899  */
900 static gboolean
901 generic_event_listener (GSignalInvocationHint * signal_hint,
902                         guint n_param_values,
903                         const GValue * param_values, gpointer data)
904 {
905   AtkObject *accessible;
906   GSignalQuery signal_query;
907   const gchar *name;
908   int detail1 = 0, detail2 = 0;
909
910   g_signal_query (signal_hint->signal_id, &signal_query);
911   name = signal_query.signal_name;
912
913   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
914
915   if (n_param_values > 1 && G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
916     detail1 = g_value_get_int (&param_values[1]);
917
918   if (n_param_values > 2 && G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
919     detail2 = g_value_get_int (&param_values[2]);
920
921   emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, detail2,
922               DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
923   return TRUE;
924 }
925
926 /*---------------------------------------------------------------------------*/
927
928 /*
929  * Registers the provided function as a handler for the given signal name
930  * and stores the signal id returned so that the function may be
931  * de-registered later.
932  */
933 static void
934 add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
935 {
936   guint id;
937
938   id = atk_add_global_event_listener (listener, signal_name);
939   g_array_append_val (listener_ids, id);
940 }
941
942 /*
943  * Initialization for the signal handlers.
944  *
945  * Registers all required signal handlers.
946  */
947 void
948 spi_atk_register_event_listeners (void)
949 {
950   /*
951    * Kludge to make sure the Atk interface types are registered, otherwise
952    * the AtkText signal handlers below won't get registered
953    */
954   GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
955   AtkObject *bo = atk_no_op_object_new (ao);
956
957   g_object_unref (G_OBJECT (bo));
958   g_object_unref (ao);
959
960   /* Register for focus event notifications, and register app with central registry  */
961   listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
962
963   atk_bridge_focus_tracker_id = atk_add_focus_tracker (focus_tracker);
964
965   add_signal_listener (property_event_listener,
966                        "Gtk:AtkObject:property-change");
967   add_signal_listener (window_event_listener, "window:create");
968   add_signal_listener (window_event_listener, "window:destroy");
969   add_signal_listener (window_event_listener, "window:minimize");
970   add_signal_listener (window_event_listener, "window:maximize");
971   add_signal_listener (window_event_listener, "window:restore");
972   add_signal_listener (window_event_listener, "window:activate");
973   add_signal_listener (window_event_listener, "window:deactivate");
974   add_signal_listener (document_event_listener,
975                        "Gtk:AtkDocument:load-complete");
976   add_signal_listener (document_event_listener, "Gtk:AtkDocument:reload");
977   add_signal_listener (document_event_listener,
978                        "Gtk:AtkDocument:load-stopped");
979   /* TODO Fake this event on the client side */
980   add_signal_listener (state_event_listener, "Gtk:AtkObject:state-change");
981   /* TODO */
982   add_signal_listener (active_descendant_event_listener,
983                        "Gtk:AtkObject:active-descendant-changed");
984   add_signal_listener (bounds_event_listener,
985                        "Gtk:AtkComponent:bounds-changed");
986   add_signal_listener (text_selection_changed_event_listener,
987                        "Gtk:AtkText:text-selection-changed");
988   add_signal_listener (text_changed_event_listener,
989                        "Gtk:AtkText:text-changed");
990   add_signal_listener (link_selected_event_listener,
991                        "Gtk:AtkHypertext:link-selected");
992   add_signal_listener (generic_event_listener,
993                        "Gtk:AtkObject:visible-data-changed");
994   add_signal_listener (generic_event_listener,
995                        "Gtk:AtkSelection:selection-changed");
996   add_signal_listener (generic_event_listener,
997                        "Gtk:AtkText:text-attributes-changed");
998   add_signal_listener (generic_event_listener,
999                        "Gtk:AtkText:text-caret-moved");
1000   add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-inserted");
1001   add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-reordered");
1002   add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-deleted");
1003   add_signal_listener (generic_event_listener,
1004                        "Gtk:AtkTable:column-inserted");
1005   add_signal_listener (generic_event_listener,
1006                        "Gtk:AtkTable:column-reordered");
1007   add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-deleted");
1008   add_signal_listener (generic_event_listener, "Gtk:AtkTable:model-changed");
1009
1010   /* Children signal listeners */
1011   atk_add_global_event_listener (children_changed_event_listener,
1012                                  "Gtk:AtkObject:children-changed");
1013
1014 #if 0
1015   g_signal_connect (G_OBJECT (spi_global_app_data->root),
1016                     "children-changed::add",
1017                     (GCallback) toplevel_added_event_listener, NULL);
1018
1019   g_signal_connect (G_OBJECT (spi_global_app_data->root),
1020                     "children-changed::remove",
1021                     (GCallback) toplevel_removed_event_listener, NULL);
1022 #endif
1023
1024   /*
1025    * May add the following listeners to implement preemptive key listening for GTK+
1026    *
1027    * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
1028    * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
1029    */
1030   atk_bridge_key_event_listener_id =
1031     atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL);
1032 }
1033
1034 /*---------------------------------------------------------------------------*/
1035
1036 /* 
1037  * De-registers all ATK signal handlers.
1038  */
1039 void
1040 spi_atk_deregister_event_listeners (void)
1041 {
1042   gint i;
1043   GArray *ids = listener_ids;
1044   listener_ids = NULL;
1045
1046   if (atk_bridge_focus_tracker_id)
1047     atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
1048
1049   for (i = 0; ids && i < ids->len; i++)
1050     {
1051       atk_remove_global_event_listener (g_array_index (ids, guint, i));
1052     }
1053
1054   if (atk_bridge_key_event_listener_id)
1055     atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
1056 }
1057
1058 /*---------------------------------------------------------------------------*/
1059
1060 /*
1061  * TODO This function seems out of place here.
1062  *
1063  * Emits fake deactivate signals on all top-level windows.
1064  * Used when shutting down AT-SPI, ensuring that all
1065  * windows have been removed on the client side.
1066  */
1067 void
1068 spi_atk_tidy_windows (void)
1069 {
1070   AtkObject *root;
1071   gint n_children;
1072   gint i;
1073
1074   root = atk_get_root ();
1075   n_children = atk_object_get_n_accessible_children (root);
1076   for (i = 0; i < n_children; i++)
1077     {
1078       AtkObject *child;
1079       AtkStateSet *stateset;
1080       const gchar *name;
1081
1082       child = atk_object_ref_accessible_child (root, i);
1083       stateset = atk_object_ref_state_set (child);
1084
1085       name = atk_object_get_name (child);
1086       if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
1087         {
1088           emit_event (child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0,
1089                       DBUS_TYPE_STRING_AS_STRING, name, append_basic);
1090         }
1091       g_object_unref (stateset);
1092
1093       emit_event (child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0,
1094                   DBUS_TYPE_STRING_AS_STRING, name, append_basic);
1095       g_object_unref (child);
1096     }
1097 }
1098
1099 gboolean
1100 spi_event_is_subtype (gchar **needle, gchar **haystack)
1101 {
1102   while (*haystack && **haystack)
1103     {
1104       if (g_strcmp0 (*needle, *haystack))
1105         return FALSE;
1106       needle++;
1107       haystack++;
1108     }
1109   return TRUE;
1110 }
1111
1112 /*END------------------------------------------------------------------------*/