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