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