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