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