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