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