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