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