document: add support for newly added AtkDocument methods and signals
[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 2011, F123 Consulting & Mais Diferenças
6  * Copyright 2008, 2009, Codethink Ltd.
7  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
8  * Copyright 2001, 2002, 2003 Ximian, Inc.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #include <string.h>
27 #include <ctype.h>
28
29 #include <atk/atk.h>
30 #include <droute/droute.h>
31 #include <atspi/atspi.h>
32
33 #include "bridge.h"
34 #include "accessible-register.h"
35
36 #include "spi-dbus.h"
37 #include "event.h"
38 #include "object.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   DBusConnection *bus;
57   GMainLoop   *loop;
58   DBusMessage *reply;
59   guint timeout;
60 } SpiReentrantCallClosure;
61
62 static void
63 switch_main_context (GMainContext *cnx)
64 {
65   GList *list;
66
67   if (spi_global_app_data->server)
68     atspi_dbus_server_setup_with_g_main (spi_global_app_data->server, cnx);
69   atspi_dbus_connection_setup_with_g_main (spi_global_app_data->bus, cnx);
70   atspi_set_main_context (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 gboolean
87 timeout_reply (void *data)
88 {
89   SpiReentrantCallClosure *closure = data;
90
91   switch_main_context (NULL);
92   g_main_loop_quit (closure->loop);
93   closure->timeout = -1;
94   return FALSE;
95 }
96
97 static DBusMessage *
98 send_and_allow_reentry (DBusConnection * bus, DBusMessage * message)
99 {
100   DBusPendingCall *pending;
101   SpiReentrantCallClosure closure;
102   GSource *source;
103
104   closure.bus = bus;
105   closure.loop = g_main_loop_new (spi_global_app_data->main_context, FALSE);
106   closure.reply = NULL;
107   switch_main_context (spi_global_app_data->main_context);
108
109   if (!dbus_connection_send_with_reply (bus, message, &pending, 9000) || !pending)
110     {
111       switch_main_context (NULL);
112       return NULL;
113     }
114   dbus_pending_call_set_notify (pending, set_reply, (void *) &closure, NULL);
115   source = g_timeout_source_new (500);
116   g_source_set_callback (source, timeout_reply, &closure, NULL);
117   closure.timeout = g_source_attach (source, spi_global_app_data->main_context);
118   g_source_unref (source);
119   g_main_loop_run  (closure.loop);
120   if (closure.timeout != -1)
121     g_source_destroy (source);
122   
123   g_main_loop_unref (closure.loop);
124   if (!closure.reply)
125     dbus_pending_call_cancel (pending);
126   return closure.reply;
127 }
128
129 /*---------------------------------------------------------------------------*/
130
131 /*
132  * Functionality related to sending device events from the application.
133  *
134  * This is used for forwarding key events on to the registry daemon.
135  */
136
137 static gboolean
138 Accessibility_DeviceEventController_NotifyListenersSync (const
139                                                          AtspiDeviceEvent
140                                                          * key_event)
141 {
142   DBusMessage *message;
143   dbus_bool_t consumed = FALSE;
144
145   message =
146     dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY,
147                                   ATSPI_DBUS_PATH_DEC,
148                                   ATSPI_DBUS_INTERFACE_DEC,
149                                   "NotifyListenersSync");
150
151   if (spi_dbus_marshal_deviceEvent (message, key_event))
152     {
153       DBusMessage *reply =
154         send_and_allow_reentry (spi_global_app_data->bus, message);
155       if (reply)
156         {
157           DBusError error;
158           dbus_error_init (&error);
159           if (!dbus_message_get_args (reply, &error, DBUS_TYPE_BOOLEAN,
160               &consumed, DBUS_TYPE_INVALID))
161             {
162               /* TODO: print a warning */
163               dbus_error_free (&error);
164             }
165           dbus_message_unref (reply);
166         }
167     }
168   dbus_message_unref (message);
169   return consumed;
170 }
171
172 static void
173 spi_init_keystroke_from_atk_key_event (AtspiDeviceEvent * keystroke,
174                                        AtkKeyEventStruct * event)
175 {
176   keystroke->id = (dbus_int32_t) event->keyval;
177   keystroke->hw_code = (dbus_int16_t) event->keycode;
178   keystroke->timestamp = (dbus_uint32_t) event->timestamp;
179   keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF);
180   if (event->string)
181     {
182       gunichar c;
183
184       keystroke->event_string = g_strdup (event->string);
185       c = g_utf8_get_char_validated (event->string, -1);
186       if (c > 0 && g_unichar_isprint (c))
187         keystroke->is_text = TRUE;
188       else
189         keystroke->is_text = FALSE;
190     }
191   else
192     {
193       keystroke->event_string = g_strdup ("");
194       keystroke->is_text = FALSE;
195     }
196   switch (event->type)
197     {
198     case (ATK_KEY_EVENT_PRESS):
199       keystroke->type = ATSPI_KEY_PRESSED;
200       break;
201     case (ATK_KEY_EVENT_RELEASE):
202       keystroke->type = ATSPI_KEY_RELEASED;
203       break;
204     default:
205       keystroke->type = 0;
206       break;
207     }
208 #if 0
209   g_print
210     ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
211      (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
212      (int) keystroke->modifiers, keystroke->event_string,
213      (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
214 #endif
215 }
216
217
218 static gint
219 spi_atk_bridge_key_listener (AtkKeyEventStruct * event, gpointer data)
220 {
221   gboolean result;
222   AtspiDeviceEvent key_event;
223
224   spi_init_keystroke_from_atk_key_event (&key_event, event);
225
226   result =
227     Accessibility_DeviceEventController_NotifyListenersSync (&key_event);
228
229   if (key_event.event_string)
230     g_free (key_event.event_string);
231
232   return result;
233 }
234
235 /*---------------------------------------------------------------------------*/
236
237 static const void *
238 validate_for_dbus (const gint type,
239               const void *val)
240 {
241   switch (type)
242     {
243       case DBUS_TYPE_STRING:
244       case DBUS_TYPE_OBJECT_PATH:
245            if (!val)
246               return "";
247            else if (!g_utf8_validate (val, -1, NULL))
248              {
249                g_warning ("atk-bridge: Received bad UTF-8 string when emitting event");
250                return "";
251                }
252            else
253               return val;
254       default:
255            return val;
256     }
257 }
258
259 static void
260 append_basic (DBusMessageIter *iter,
261               const char *type,
262               const void *val)
263 {
264   DBusMessageIter sub;
265
266   dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &sub);
267
268     val = validate_for_dbus ((int) *type, val);
269     dbus_message_iter_append_basic(&sub, (int) *type, &val);
270
271   dbus_message_iter_close_container(iter, &sub);
272 }
273
274 static void
275 append_rect (DBusMessageIter *iter,
276              const char *type,
277              const void *val)
278 {
279   DBusMessageIter variant, sub;
280   const AtkRectangle *rect = (const AtkRectangle *) val;
281
282   dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &variant);
283
284     dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub);
285
286       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x));
287       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y));
288       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width));
289       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height));
290
291     dbus_message_iter_close_container (&variant, &sub);
292
293   dbus_message_iter_close_container(iter, &variant);
294 }
295
296 static void
297 append_object (DBusMessageIter *iter,
298                const char *type,
299                const void *val)
300 {
301   spi_object_append_v_reference (iter, ATK_OBJECT (val));
302 }
303
304 static gchar *
305 signal_name_to_dbus (const gchar *s)
306 {
307   gchar *ret = g_strdup (s);
308   gchar *t;
309
310   if (!ret)
311     return NULL;
312   ret [0] = toupper (ret [0]);
313   while ((t = strchr (ret, '-')) != NULL)
314   {
315     memmove (t, t + 1, strlen (t));
316     *t = toupper (*t);
317   }
318   return ret;
319 }
320
321 /*
322  * Converts names of the form "active-descendant-changed" to
323  * "ActiveDescendantChanged"
324  */
325 static gchar *
326 ensure_proper_format (const char *name)
327 {
328   gchar *ret = (gchar *) g_malloc (strlen (name) * 2 + 2);
329   gchar *p = ret;
330   gboolean need_upper = TRUE;
331
332   if (!ret)
333     return NULL;
334   while (*name)
335     {
336       if (need_upper)
337         {
338           *p++ = toupper (*name);
339           need_upper = FALSE;
340         }
341       else if (*name == '-')
342         need_upper = TRUE;
343       else if (*name == ':')
344         {
345           need_upper = TRUE;
346           *p++ = *name;
347         }
348       else
349         *p++ = *name;
350       name++;
351     }
352   *p = '\0';
353   return ret;
354 }
355
356 static gboolean
357 signal_is_needed (const gchar *klass, const gchar *major, const gchar *minor)
358 {
359   gchar *data [4];
360   event_data *evdata;
361   gboolean ret = FALSE;
362   GList *list;
363
364   if (!spi_global_app_data->events_initialized)
365     return TRUE;
366
367   data [0] = ensure_proper_format (klass + 21);
368   data [1] = ensure_proper_format (major);
369   data [2] = ensure_proper_format (minor);
370   data [3] = NULL;
371
372   /* Hack: Always pass events that update the cache.
373    * TODO: FOr 2.2, have at-spi2-core define a special "cache listener" for
374    * this instead, so that we don't send these if no one is listening */
375   if (!g_strcmp0 (data [1], "ChildrenChanged") ||
376       ((!g_strcmp0 (data [1], "PropertyChange")) &&
377        (!g_strcmp0 (data [2], "accessible-name") ||
378         !g_strcmp0 (data [2], "accessible-description") ||
379         !g_strcmp0 (data [2], "accessible-parent") ||
380         !g_strcmp0 (data [2], "accessible-role"))) ||
381       !g_strcmp0 (data [1], "StateChanged"))
382   {
383     g_free (data [2]);
384     g_free (data [1]);
385     g_free (data [0]);
386     return TRUE;
387   }
388
389   /* Hack: events such as "object::text-changed::insert:system" as
390      generated by Gecko */
391   data [2][strcspn (data [2], ":")] = '\0';
392   for (list = spi_global_app_data->events; list; list = list->next)
393     {
394       evdata = list->data;
395       if (spi_event_is_subtype (data, evdata->data))
396         {
397           ret = TRUE;
398           break;
399         }
400     }
401
402 #if 0
403   g_print("event: %s %s %s: %d\n", data[0], data[1], data[2], ret);
404 #endif
405   g_free (data [2]);
406   g_free (data [1]);
407   g_free (data [0]);
408   return ret;
409 }
410
411 /* Convert a : to a / so that listeners can use arg0path to match only
412  *  * the prefix */
413 static char *
414 adapt_minor_for_dbus (const char *source)
415 {
416   gchar *ret = g_strdup (source);
417   int i = strcspn (ret, ":");
418   if (ret[i] == ':')
419     ret[i] = '/';
420   return ret;
421 }
422
423 /*
424  * Emits an AT-SPI event.
425  * AT-SPI events names are split into three parts:
426  * class:major:minor
427  * This is mapped onto D-Bus events as:
428  * D-Bus Interface:Signal Name:Detail argument
429  *
430  * Marshals a basic type into the 'any_data' attribute of
431  * the AT-SPI event.
432  */
433 static void 
434 emit_event (AtkObject  *obj,
435             const char *klass,
436             const char *major,
437             const char *minor,
438             dbus_int32_t detail1,
439             dbus_int32_t detail2,
440             const char *type,
441             const void *val,
442             void (*append_variant) (DBusMessageIter *, const char *, const void *))
443 {
444   DBusConnection *bus = spi_global_app_data->bus;
445   char *path;
446   char *minor_dbus;
447
448   gchar *cname;
449   DBusMessage *sig;
450   DBusMessageIter iter;
451   
452   if (!klass) klass = "";
453   if (!major) major = "";
454   if (!minor) minor = "";
455   if (!type) type = "u";
456
457   if (!signal_is_needed (klass, major, minor))
458     return;
459
460   path =  spi_register_object_to_path (spi_global_register, G_OBJECT (obj));
461   g_return_if_fail (path != NULL);
462
463   /*
464    * This is very annoying, but as '-' isn't a legal signal
465    * name in D-Bus (Why not??!?) The names need converting
466    * on this side, and again on the client side.
467    */
468   cname = signal_name_to_dbus (major);
469   sig = dbus_message_new_signal(path, klass, cname);
470
471   dbus_message_iter_init_append(sig, &iter);
472
473   minor_dbus = adapt_minor_for_dbus (minor);
474   dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor_dbus);
475   g_free (minor_dbus);
476   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1);
477   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2);
478   append_variant (&iter, type, val);
479   spi_object_append_reference (&iter, spi_global_app_data->root);
480
481   dbus_connection_send(bus, sig, NULL);
482   dbus_message_unref(sig);
483
484   if (g_strcmp0 (cname, "ChildrenChanged") != 0)
485     spi_object_lease_if_needed (G_OBJECT (obj));
486
487   g_free(cname);
488   g_free (path);
489 }
490
491 /*---------------------------------------------------------------------------*/
492
493 /*
494  * The focus listener handles the ATK 'focus' signal and forwards it
495  * as the AT-SPI event, 'focus:'
496  */
497 static void
498 focus_tracker (AtkObject * accessible)
499 {
500   emit_event (accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0,
501               DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
502 }
503
504 /*---------------------------------------------------------------------------*/
505
506 #define PCHANGE "PropertyChange"
507
508 /* 
509  * This handler handles the following ATK signals and
510  * converts them to AT-SPI events:
511  *  
512  * Gtk:AtkObject:property-change -> object:property-change:(property-name)
513  *
514  * The property-name is part of the ATK property-change signal.
515  */
516 static gboolean
517 property_event_listener (GSignalInvocationHint * signal_hint,
518                          guint n_param_values,
519                          const GValue * param_values, gpointer data)
520 {
521   AtkObject *accessible;
522   AtkPropertyValues *values;
523
524   const gchar *pname = NULL;
525
526   AtkObject *otemp;
527   const gchar *s1;
528   gint i;
529
530   accessible = g_value_get_object (&param_values[0]);
531   values = (AtkPropertyValues *) g_value_get_pointer (&param_values[1]);
532
533   pname = values[0].property_name;
534
535   /* TODO Could improve this control statement by matching
536    * on only the end of the signal names,
537    */
538   if (strcmp (pname, "accessible-name") == 0)
539     {
540       s1 = atk_object_get_name (accessible);
541       if (s1 != NULL)
542         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
543                     DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
544     }
545   else if (strcmp (pname, "accessible-description") == 0)
546     {
547       s1 = atk_object_get_description (accessible);
548       if (s1 != NULL)
549         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
550                     DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
551     }
552   else if (strcmp (pname, "accessible-parent") == 0)
553     {
554       otemp = atk_object_get_parent (accessible);
555       if (otemp != NULL)
556         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
557                     "(so)", otemp, append_object);
558     }
559   else if (strcmp (pname, "accessible-role") == 0)
560     {
561       i = atk_object_get_role (accessible);
562       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
563                     DBUS_TYPE_UINT32_AS_STRING, GINT_TO_POINTER(i), append_basic);
564     }
565   else if (strcmp (pname, "accessible-table-summary") == 0)
566     {
567       otemp = atk_table_get_summary (ATK_TABLE (accessible));
568       if (otemp != NULL)
569         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
570                     "(so)", otemp, append_object);
571     }
572   else if (strcmp (pname, "accessible-table-column-header") == 0)
573     {
574       i = g_value_get_int (&(values->new_value));
575       otemp = atk_table_get_column_header (ATK_TABLE (accessible), i);
576       if (otemp != NULL)
577         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
578                     "(so)", otemp, append_object);
579     }
580   else if (strcmp (pname, "accessible-table-row-header") == 0)
581     {
582       i = g_value_get_int (&(values->new_value));
583       otemp = atk_table_get_row_header (ATK_TABLE (accessible), i);
584       if (otemp != NULL)
585         emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
586                     "(so)", otemp, append_object);
587     }
588   else if (strcmp (pname, "accessible-table-row-description") == 0)
589     {
590       i = g_value_get_int (&(values->new_value));
591       s1 = atk_table_get_row_description (ATK_TABLE (accessible), i);
592       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
593                   DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
594     }
595   else if (strcmp (pname, "accessible-table-column-description") == 0)
596     {
597       i = g_value_get_int (&(values->new_value));
598       s1 = atk_table_get_column_description (ATK_TABLE (accessible), i);
599       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
600                   DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
601     }
602   else if (strcmp (pname, "accessible-table-caption-object") == 0)
603     {
604       otemp = atk_table_get_caption (ATK_TABLE (accessible));
605       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
606                   "(so)", otemp, append_object);
607     }
608   else
609     {
610       emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
611             DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
612     }
613   return TRUE;
614 }
615
616 /*---------------------------------------------------------------------------*/
617
618 #define STATE_CHANGED "state-changed"
619
620 /*
621  * The state event listener handles 'Gtk:AtkObject:state-change' ATK signals
622  * and forwards them as object:state-changed:(param-name) AT-SPI events. Where
623  * the param-name is part of the ATK state-change signal.
624  */
625 static gboolean
626 state_event_listener (GSignalInvocationHint * signal_hint,
627                       guint n_param_values,
628                       const GValue * param_values, gpointer data)
629 {
630   AtkObject *accessible;
631   const gchar *pname;
632   guint detail1;
633
634   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
635   pname = g_value_get_string (&param_values[1]);
636
637   detail1 = (g_value_get_boolean (&param_values[2])) ? 1 : 0;
638   emit_event (accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0,
639               DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
640
641   if (!g_strcmp0 (pname, "defunct") && detail1)
642     spi_register_deregister_object (spi_global_register, G_OBJECT (accessible),
643                                     TRUE);
644   return TRUE;
645 }
646
647 /*---------------------------------------------------------------------------*/
648
649 /*
650  * The window event listener handles the following ATK signals and forwards
651  * them as AT-SPI events:
652  *
653  * window:create     -> window:create
654  * window:destroy    -> window:destroy
655  * window:minimize   -> window:minimize
656  * window:maximize   -> window:maximize
657  * window:activate   -> window:activate
658  * window:deactivate -> window:deactivate
659  */
660 static gboolean
661 window_event_listener (GSignalInvocationHint * signal_hint,
662                        guint n_param_values,
663                        const GValue * param_values, gpointer data)
664 {
665   AtkObject *accessible;
666   GSignalQuery signal_query;
667   const gchar *name, *s;
668
669   g_signal_query (signal_hint->signal_id, &signal_query);
670   name = signal_query.signal_name;
671
672   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
673   s = atk_object_get_name (accessible);
674   emit_event (accessible, ITF_EVENT_WINDOW, name, "", 0, 0,
675               DBUS_TYPE_STRING_AS_STRING, s, append_basic);
676
677   return TRUE;
678 }
679
680 /*---------------------------------------------------------------------------*/
681
682 /* 
683  * The document event listener handles the following ATK signals
684  * and converts them to AT-SPI events:
685  *
686  * Gtk:AtkDocument:load-complete ->  document:load-complete
687  * Gtk:AtkDocument:load-stopped  ->  document:load-stopped
688  * Gtk:AtkDocument:reload        ->  document:reload
689  * Gtk:AtkDocument:page-changed  ->  document:page-changed
690  */
691 static gboolean
692 document_event_listener (GSignalInvocationHint * signal_hint,
693                          guint n_param_values,
694                          const GValue * param_values, gpointer data)
695 {
696   AtkObject *accessible;
697   GSignalQuery signal_query;
698   const gchar *name, *s;
699   gint detail1 = 0;
700
701   g_signal_query (signal_hint->signal_id, &signal_query);
702   name = signal_query.signal_name;
703
704   if (n_param_values > 0) // on the case of page-changed
705     if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
706       detail1 = g_value_get_int (&param_values[1]);
707
708   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
709   s = atk_object_get_name (accessible);
710   emit_event (accessible, ITF_EVENT_DOCUMENT, name, "", detail1, 0,
711               DBUS_TYPE_STRING_AS_STRING, s, append_basic);
712
713   return TRUE;
714 }
715
716 /*---------------------------------------------------------------------------*/
717
718 /*
719  * Signal handler for  "Gtk:AtkComponent:bounds-changed". Converts
720  * this to an AT-SPI event - "object:bounds-changed".
721  */
722 static gboolean
723 bounds_event_listener (GSignalInvocationHint * signal_hint,
724                        guint n_param_values,
725                        const GValue * param_values, gpointer data)
726 {
727   AtkObject *accessible;
728   AtkRectangle *atk_rect;
729   GSignalQuery signal_query;
730   const gchar *name;
731
732   g_signal_query (signal_hint->signal_id, &signal_query);
733   name = signal_query.signal_name;
734
735   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
736
737   if (G_VALUE_HOLDS_BOXED (param_values + 1))
738   {
739     atk_rect = g_value_get_boxed (param_values + 1);
740
741     emit_event (accessible, ITF_EVENT_OBJECT, name, "", 0, 0,
742                 "(iiii)", atk_rect, append_rect);
743   }
744   return TRUE;
745 }
746
747 /*---------------------------------------------------------------------------*/
748
749 /* 
750  * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and 
751  * converts it to the AT-SPI signal - 'object:active-descendant-changed'.
752  *
753  */
754 static gboolean
755 active_descendant_event_listener (GSignalInvocationHint * signal_hint,
756                                   guint n_param_values,
757                                   const GValue * param_values, gpointer data)
758 {
759   AtkObject *accessible;
760   AtkObject *child;
761   GSignalQuery signal_query;
762   const gchar *name;
763   gint detail1;
764
765   g_signal_query (signal_hint->signal_id, &signal_query);
766   name = signal_query.signal_name;
767
768   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
769   child = ATK_OBJECT (g_value_get_pointer (&param_values[1]));
770   g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
771
772   detail1 = atk_object_get_index_in_parent (child);
773
774   emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, 0,
775               "(so)", child, append_object);
776   return TRUE;
777 }
778
779 /*---------------------------------------------------------------------------*/
780
781 /* 
782  * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and
783  * converts it to the AT-SPI signal - 'object:link-selected'
784  *
785  */
786 static gboolean
787 link_selected_event_listener (GSignalInvocationHint * signal_hint,
788                               guint n_param_values,
789                               const GValue * param_values, gpointer data)
790 {
791   AtkObject *accessible;
792   GSignalQuery signal_query;
793   const gchar *name, *minor;
794   gint detail1 = 0;
795
796   g_signal_query (signal_hint->signal_id, &signal_query);
797   name = signal_query.signal_name;
798
799   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
800   minor = g_quark_to_string (signal_hint->detail);
801
802   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
803     detail1 = g_value_get_int (&param_values[1]);
804
805   emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0,
806               DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
807   return TRUE;
808 }
809
810 /*---------------------------------------------------------------------------*/
811
812 /* 
813  * Handles the ATK signal 'Gtk:AtkText:text-changed' and
814  * converts it to the AT-SPI signal - 'object:text-changed'
815  * This signal is deprecated by Gtk:AtkText:text-insert
816  * and Gtk:AtkText:text-remove
817  *
818  */
819 static gboolean
820 text_changed_event_listener (GSignalInvocationHint * signal_hint,
821                              guint n_param_values,
822                              const GValue * param_values, gpointer data)
823 {
824   AtkObject *accessible;
825   GSignalQuery signal_query;
826   const gchar *name, *minor;
827   gchar *selected;
828   gint detail1 = 0, detail2 = 0;
829
830   g_signal_query (signal_hint->signal_id, &signal_query);
831   name = signal_query.signal_name;
832
833   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
834   minor = g_quark_to_string (signal_hint->detail);
835
836   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
837     detail1 = g_value_get_int (&param_values[1]);
838
839   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
840     detail2 = g_value_get_int (&param_values[2]);
841
842   selected =
843     atk_text_get_text (ATK_TEXT (accessible), detail1, detail1 + detail2);
844
845   emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
846               DBUS_TYPE_STRING_AS_STRING, selected, append_basic);
847   g_free (selected);
848
849   return TRUE;
850 }
851
852 /* 
853  * Handles the ATK signal 'Gtk:AtkText:text-insert' and
854  * converts it to the AT-SPI signal - 'object:text-changed'
855  *
856  */
857 static gboolean
858 text_insert_event_listener (GSignalInvocationHint * signal_hint,
859                             guint n_param_values,
860                             const GValue * param_values, gpointer data)
861 {
862   AtkObject *accessible;
863   guint text_changed_signal_id;
864   GSignalQuery signal_query;
865   const gchar *name;
866   const gchar *minor_raw, *text;
867   gchar *minor;
868   gint detail1 = 0, detail2 = 0;
869
870   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
871   /* Get signal name for 'Gtk:AtkText:text-changed' so
872    * we convert it to the AT-SPI signal - 'object:text-changed'
873    */
874   text_changed_signal_id = g_signal_lookup ("text-changed", G_OBJECT_TYPE (accessible));
875   g_signal_query (text_changed_signal_id, &signal_query);
876   name = signal_query.signal_name;
877
878
879   /* Add the insert and keep any detail coming from atk */
880   minor_raw = g_quark_to_string (signal_hint->detail);
881   if (minor_raw)
882     minor = g_strconcat ("insert:", minor_raw, NULL);
883   else
884     minor = g_strdup ("insert");
885
886   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
887     detail1 = g_value_get_int (&param_values[1]);
888
889   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
890     detail2 = g_value_get_int (&param_values[2]);
891
892   if (G_VALUE_TYPE (&param_values[3]) == G_TYPE_STRING)
893     text = g_value_get_string (&param_values[3]);
894
895   emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
896               DBUS_TYPE_STRING_AS_STRING, text, append_basic);
897   g_free (minor);
898   return TRUE;
899 }
900
901 /* 
902  * Handles the ATK signal 'Gtk:AtkText:text-remove' and
903  * converts it to the AT-SPI signal - 'object:text-changed'
904  *
905  */
906 static gboolean
907 text_remove_event_listener (GSignalInvocationHint * signal_hint,
908                             guint n_param_values,
909                             const GValue * param_values, gpointer data)
910 {
911   AtkObject *accessible;
912   guint text_changed_signal_id;
913   GSignalQuery signal_query;
914   const gchar *name;
915   const gchar *minor_raw, *text;
916   gchar *minor;
917   gint detail1 = 0, detail2 = 0;
918
919   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
920   /* Get signal name for 'Gtk:AtkText:text-changed' so
921    * we convert it to the AT-SPI signal - 'object:text-changed'
922    */
923   text_changed_signal_id = g_signal_lookup ("text-changed", G_OBJECT_TYPE (accessible));
924   g_signal_query (text_changed_signal_id, &signal_query);
925   name = signal_query.signal_name;
926
927   minor_raw = g_quark_to_string (signal_hint->detail);
928
929   /* Add the delete and keep any detail coming from atk */
930   if (minor_raw)
931     minor = g_strconcat ("delete:", minor_raw, NULL);
932   else
933     minor = g_strdup ("delete");
934
935   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
936     detail1 = g_value_get_int (&param_values[1]);
937
938   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
939     detail2 = g_value_get_int (&param_values[2]);
940
941   if (G_VALUE_TYPE (&param_values[3]) == G_TYPE_STRING)
942     text = g_value_get_string (&param_values[3]);
943
944   emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
945               DBUS_TYPE_STRING_AS_STRING, text, append_basic);
946   g_free (minor);
947   return TRUE;
948 }
949
950
951 /*---------------------------------------------------------------------------*/
952
953 /* 
954  * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and
955  * converts it to the AT-SPI signal - 'object:text-selection-changed'
956  *
957  */
958 static gboolean
959 text_selection_changed_event_listener (GSignalInvocationHint * signal_hint,
960                                        guint n_param_values,
961                                        const GValue * param_values,
962                                        gpointer data)
963 {
964   AtkObject *accessible;
965   GSignalQuery signal_query;
966   const gchar *name, *minor;
967   gint detail1 = 0, detail2 = 0;
968
969   g_signal_query (signal_hint->signal_id, &signal_query);
970   name = signal_query.signal_name;
971
972   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
973   minor = g_quark_to_string (signal_hint->detail);
974
975   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
976     detail1 = g_value_get_int (&param_values[1]);
977
978   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
979     detail2 = g_value_get_int (&param_values[2]);
980
981   emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
982               DBUS_TYPE_STRING_AS_STRING, "", append_basic);
983   return TRUE;
984 }
985
986 /*---------------------------------------------------------------------------*/
987
988 /*
989  * Children changed signal converter and forwarder.
990  *
991  * Klass (Interface) org.a11y.atspi.Event.Object
992  * Major is the signal name.
993  * Minor is 'add' or 'remove'
994  * detail1 is the index.
995  * detail2 is 0.
996  * any_data is the child reference.
997  */
998 static gboolean
999 children_changed_event_listener (GSignalInvocationHint * signal_hint,
1000                                  guint n_param_values,
1001                                  const GValue * param_values, gpointer data)
1002 {
1003   GSignalQuery signal_query;
1004   const gchar *name, *minor;
1005   gint detail1 = 0, detail2 = 0;
1006
1007   AtkObject *accessible, *ao=NULL;
1008   gpointer child;
1009
1010   g_signal_query (signal_hint->signal_id, &signal_query);
1011   name = signal_query.signal_name;
1012
1013   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
1014   minor = g_quark_to_string (signal_hint->detail);
1015
1016   detail1 = g_value_get_uint (param_values + 1);
1017   child = g_value_get_pointer (param_values + 2);
1018
1019   if (ATK_IS_OBJECT (child))
1020     {
1021       ao = ATK_OBJECT (child);
1022       emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
1023                   "(so)", ao, append_object);
1024     }
1025   else if ((minor != NULL) && (strcmp (minor, "add") == 0))
1026     {
1027       ao = atk_object_ref_accessible_child (accessible, 
1028                                             detail1);
1029       emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
1030                   "(so)", ao, append_object);
1031       g_object_unref (ao);
1032     }
1033   else
1034     {
1035       emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
1036                   "(so)", ao, append_object);
1037     }
1038  
1039   return TRUE;
1040 }
1041
1042 /*---------------------------------------------------------------------------*/
1043
1044 /*
1045  * Generic signal converter and forwarder.
1046  *
1047  * Klass (Interface) org.a11y.atspi.Event.Object
1048  * Major is the signal name.
1049  * Minor is NULL.
1050  * detail1 is 0.
1051  * detail2 is 0.
1052  * any_data is NULL.
1053  */
1054 static gboolean
1055 generic_event_listener (GSignalInvocationHint * signal_hint,
1056                         guint n_param_values,
1057                         const GValue * param_values, gpointer data)
1058 {
1059   AtkObject *accessible;
1060   GSignalQuery signal_query;
1061   const gchar *name;
1062   int detail1 = 0, detail2 = 0;
1063
1064   g_signal_query (signal_hint->signal_id, &signal_query);
1065   name = signal_query.signal_name;
1066
1067   accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
1068
1069   if (n_param_values > 1 && G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
1070     detail1 = g_value_get_int (&param_values[1]);
1071
1072   if (n_param_values > 2 && G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
1073     detail2 = g_value_get_int (&param_values[2]);
1074
1075   emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, detail2,
1076               DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
1077   return TRUE;
1078 }
1079
1080 /*---------------------------------------------------------------------------*/
1081
1082 /*
1083  * Registers the provided function as a handler for the given signal name
1084  * and stores the signal id returned so that the function may be
1085  * de-registered later.
1086  */
1087 static guint
1088 add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
1089 {
1090   guint id;
1091
1092   id = atk_add_global_event_listener (listener, signal_name);
1093
1094   if (id > 0) /* id == 0 is a failure */
1095     g_array_append_val (listener_ids, id);
1096
1097   return id;
1098 }
1099
1100 /*
1101  * Initialization for the signal handlers.
1102  *
1103  * Registers all required signal handlers.
1104  */
1105 void
1106 spi_atk_register_event_listeners (void)
1107 {
1108   /*
1109    * Kludge to make sure the Atk interface types are registered, otherwise
1110    * the AtkText signal handlers below won't get registered
1111    */
1112   GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
1113   AtkObject *bo = atk_no_op_object_new (ao);
1114   guint id = 0;
1115
1116   g_object_unref (G_OBJECT (bo));
1117   g_object_unref (ao);
1118
1119   if (listener_ids)
1120   {
1121     g_warning ("atk_bridge: spi_atk-register_event_listeners called multiple times");
1122     return;
1123   }
1124
1125   /* Register for focus event notifications, and register app with central registry  */
1126   listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
1127
1128   atk_bridge_focus_tracker_id = atk_add_focus_tracker (focus_tracker);
1129
1130   add_signal_listener (property_event_listener,
1131                        "Gtk:AtkObject:property-change");
1132
1133   /* window events: we tentative try to register using the old format */
1134   id = add_signal_listener (window_event_listener, "window:create");
1135
1136   if (id != 0)
1137     {
1138       /* If we are able to register using the old format, we assume
1139        * that the ATK implementor is managing window events without
1140        * AtkWindow. We can't use the opposite test because after
1141        * including AtkWindow on ATK you would be able to register to
1142        * that event, although the ATK implementor could or not use it.
1143        */
1144
1145       add_signal_listener (window_event_listener, "window:destroy");
1146       add_signal_listener (window_event_listener, "window:minimize");
1147       add_signal_listener (window_event_listener, "window:maximize");
1148       add_signal_listener (window_event_listener, "window:restore");
1149       add_signal_listener (window_event_listener, "window:activate");
1150       add_signal_listener (window_event_listener, "window:deactivate");
1151     }
1152   else
1153     {
1154       add_signal_listener (window_event_listener, "Atk:AtkWindow:create");
1155       add_signal_listener (window_event_listener, "Atk:AtkWindow:destroy");
1156       add_signal_listener (window_event_listener, "Atk:AtkWindow:minimize");
1157       add_signal_listener (window_event_listener, "Atk:AtkWindow:maximize");
1158       add_signal_listener (window_event_listener, "Atk:AtkWindow:restore");
1159       add_signal_listener (window_event_listener, "Atk:AtkWindow:activate");
1160       add_signal_listener (window_event_listener, "Atk:AtkWindow:deactivate");
1161     }
1162
1163   add_signal_listener (document_event_listener,
1164                        "Gtk:AtkDocument:load-complete");
1165   add_signal_listener (document_event_listener, "Gtk:AtkDocument:reload");
1166   add_signal_listener (document_event_listener,
1167                        "Gtk:AtkDocument:load-stopped");
1168   add_signal_listener (document_event_listener,
1169                        "Gtk:AtkDocument:page-changed");
1170   /* TODO Fake this event on the client side */
1171   add_signal_listener (state_event_listener, "Gtk:AtkObject:state-change");
1172   /* TODO */
1173   add_signal_listener (active_descendant_event_listener,
1174                        "Gtk:AtkObject:active-descendant-changed");
1175   add_signal_listener (bounds_event_listener,
1176                        "Gtk:AtkComponent:bounds-changed");
1177   add_signal_listener (text_selection_changed_event_listener,
1178                        "Gtk:AtkText:text-selection-changed");
1179   add_signal_listener (text_changed_event_listener,
1180                        "Gtk:AtkText:text-changed");
1181   add_signal_listener (text_insert_event_listener,
1182                        "Gtk:AtkText:text-insert");
1183   add_signal_listener (text_remove_event_listener,
1184                        "Gtk:AtkText:text-remove");
1185   add_signal_listener (link_selected_event_listener,
1186                        "Gtk:AtkHypertext:link-selected");
1187   add_signal_listener (generic_event_listener,
1188                        "Gtk:AtkObject:visible-data-changed");
1189   add_signal_listener (generic_event_listener,
1190                        "Gtk:AtkSelection:selection-changed");
1191   add_signal_listener (generic_event_listener,
1192                        "Gtk:AtkText:text-attributes-changed");
1193   add_signal_listener (generic_event_listener,
1194                        "Gtk:AtkText:text-caret-moved");
1195   add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-inserted");
1196   add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-reordered");
1197   add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-deleted");
1198   add_signal_listener (generic_event_listener,
1199                        "Gtk:AtkTable:column-inserted");
1200   add_signal_listener (generic_event_listener,
1201                        "Gtk:AtkTable:column-reordered");
1202   add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-deleted");
1203   add_signal_listener (generic_event_listener, "Gtk:AtkTable:model-changed");
1204   add_signal_listener (children_changed_event_listener, "Gtk:AtkObject:children-changed");
1205
1206 #if 0
1207   g_signal_connect (G_OBJECT (spi_global_app_data->root),
1208                     "children-changed::add",
1209                     (GCallback) toplevel_added_event_listener, NULL);
1210
1211   g_signal_connect (G_OBJECT (spi_global_app_data->root),
1212                     "children-changed::remove",
1213                     (GCallback) toplevel_removed_event_listener, NULL);
1214 #endif
1215
1216   /*
1217    * May add the following listeners to implement preemptive key listening for GTK+
1218    *
1219    * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
1220    * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
1221    */
1222   atk_bridge_key_event_listener_id =
1223     atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL);
1224 }
1225
1226 /*---------------------------------------------------------------------------*/
1227
1228 /* 
1229  * De-registers all ATK signal handlers.
1230  */
1231 void
1232 spi_atk_deregister_event_listeners (void)
1233 {
1234   gint i;
1235   GArray *ids = listener_ids;
1236   listener_ids = NULL;
1237
1238   if (atk_bridge_focus_tracker_id)
1239   {
1240     atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
1241     atk_bridge_focus_tracker_id = 0;
1242   }
1243
1244   if (ids)
1245     {
1246       for (i = 0; i < ids->len; i++)
1247         {
1248           atk_remove_global_event_listener (g_array_index (ids, guint, i));
1249         }
1250       g_array_free (ids, TRUE);
1251     }
1252
1253   if (atk_bridge_key_event_listener_id)
1254   {
1255     atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
1256     atk_bridge_key_event_listener_id = 0;
1257   }
1258 }
1259
1260 /*---------------------------------------------------------------------------*/
1261
1262 /*
1263  * TODO This function seems out of place here.
1264  *
1265  * Emits fake deactivate signals on all top-level windows.
1266  * Used when shutting down AT-SPI, ensuring that all
1267  * windows have been removed on the client side.
1268  */
1269 void
1270 spi_atk_tidy_windows (void)
1271 {
1272   AtkObject *root;
1273   gint n_children;
1274   gint i;
1275
1276   root = atk_get_root ();
1277   n_children = atk_object_get_n_accessible_children (root);
1278   for (i = 0; i < n_children; i++)
1279     {
1280       AtkObject *child;
1281       AtkStateSet *stateset;
1282       const gchar *name;
1283
1284       child = atk_object_ref_accessible_child (root, i);
1285       stateset = atk_object_ref_state_set (child);
1286
1287       name = atk_object_get_name (child);
1288       if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
1289         {
1290           emit_event (child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0,
1291                       DBUS_TYPE_STRING_AS_STRING, name, append_basic);
1292         }
1293       g_object_unref (stateset);
1294
1295       emit_event (child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0,
1296                   DBUS_TYPE_STRING_AS_STRING, name, append_basic);
1297       g_object_unref (child);
1298     }
1299 }
1300
1301 gboolean
1302 spi_event_is_subtype (gchar **needle, gchar **haystack)
1303 {
1304   while (*haystack && **haystack)
1305     {
1306       if (g_strcmp0 (*needle, *haystack))
1307         return FALSE;
1308       needle++;
1309       haystack++;
1310     }
1311   return TRUE;
1312 }
1313
1314 /*END------------------------------------------------------------------------*/