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