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