More error handling fixes
[platform/upstream/at-spi2-core.git] / atspi / atspi-misc.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001, 2002 Sun Microsystems Inc.,
6  * Copyright 2001, 2002 Ximian, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /*
25  *
26  * Basic SPI initialization and event loop function prototypes
27  *
28  */
29
30 #include "atspi-private.h"
31 #include "X11/Xlib.h"
32 #include <stdio.h>
33 #include <string.h>
34
35 static void handle_get_items (DBusPendingCall *pending, void *user_data);
36
37 static DBusConnection *bus = NULL;
38 static GHashTable *apps = NULL;
39 static GHashTable *live_refs = NULL;
40 static GQueue *exception_handlers = NULL;
41 static DBusError exception;
42
43 const char *atspi_path_dec = ATSPI_DBUS_PATH_DEC;
44 const char *atspi_path_registry = ATSPI_DBUS_PATH_REGISTRY;
45 const char *atspi_path_root = ATSPI_DBUS_PATH_ROOT;
46 const char *atspi_bus_registry = ATSPI_DBUS_NAME_REGISTRY;
47 const char *atspi_interface_accessible = ATSPI_DBUS_INTERFACE_ACCESSIBLE;
48 const char *atspi_interface_action = ATSPI_DBUS_INTERFACE_ACTION;
49 const char *atspi_interface_application = ATSPI_DBUS_INTERFACE_APPLICATION;
50 const char *atspi_interface_collection = ATSPI_DBUS_INTERFACE_COLLECTION;
51 const char *atspi_interface_component = ATSPI_DBUS_INTERFACE_COMPONENT;
52 const char *atspi_interface_dec = ATSPI_DBUS_INTERFACE_DEC;
53 const char *atspi_interface_device_event_listener = ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER;
54 const char *atspi_interface_document = ATSPI_DBUS_INTERFACE_DOCUMENT;
55 const char *atspi_interface_editable_text = ATSPI_DBUS_INTERFACE_EDITABLE_TEXT;
56 const char *atspi_interface_event_object = ATSPI_DBUS_INTERFACE_EVENT_OBJECT;
57 const char *atspi_interface_hyperlink = ATSPI_DBUS_INTERFACE_HYPERLINK;
58 const char *atspi_interface_hypertext = ATSPI_DBUS_INTERFACE_HYPERTEXT;
59 const char *atspi_interface_image = ATSPI_DBUS_INTERFACE_IMAGE;
60 const char *atspi_interface_registry = ATSPI_DBUS_INTERFACE_REGISTRY;
61 const char *atspi_interface_selection = ATSPI_DBUS_INTERFACE_SELECTION;
62 const char *atspi_interface_table = ATSPI_DBUS_INTERFACE_TABLE;
63 const char *atspi_interface_text = ATSPI_DBUS_INTERFACE_TEXT;
64 const char *atspi_interface_cache = ATSPI_DBUS_INTERFACE_CACHE;
65 const char *atspi_interface_value = ATSPI_DBUS_INTERFACE_VALUE;
66
67 static const char *interfaces[] =
68 {
69   ATSPI_DBUS_INTERFACE_ACCESSIBLE,
70   ATSPI_DBUS_INTERFACE_ACTION,
71   ATSPI_DBUS_INTERFACE_APPLICATION,
72   ATSPI_DBUS_INTERFACE_COLLECTION,
73   ATSPI_DBUS_INTERFACE_COMPONENT,
74   ATSPI_DBUS_INTERFACE_DOCUMENT,
75   ATSPI_DBUS_INTERFACE_EDITABLE_TEXT,
76   ATSPI_DBUS_INTERFACE_HYPERLINK,
77   ATSPI_DBUS_INTERFACE_HYPERTEXT,
78   ATSPI_DBUS_INTERFACE_IMAGE,
79   "org.a11y.atspi.LoginHelper",
80   ATSPI_DBUS_INTERFACE_SELECTION,
81   ATSPI_DBUS_INTERFACE_TABLE,
82   ATSPI_DBUS_INTERFACE_TEXT,
83   ATSPI_DBUS_INTERFACE_VALUE,
84   NULL
85 };
86
87 gint
88 _atspi_get_iface_num (const char *iface)
89 {
90   /* TODO: Use a binary search or hash to improve performance */
91   int i;
92
93   for (i = 0; interfaces[i]; i++)
94   {
95     if (!strcmp(iface, interfaces[i])) return i;
96   }
97   return -1;
98 }
99
100 static GHashTable *
101 get_live_refs (void)
102 {
103   if (!live_refs) 
104     {
105       live_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
106     }
107   return live_refs;
108 }
109
110 /* TODO: Add an application parameter */
111 DBusConnection *
112 _atspi_bus ()
113 {
114   if (!bus)
115     atspi_init ();
116   return bus;
117 }
118
119 #define APP_IS_REGISTRY(app) (!strcmp (app->bus_name, atspi_bus_registry))
120
121 static void
122 cleanup ()
123 {
124   GHashTable *refs;
125
126   refs = live_refs;
127   live_refs = NULL;
128   if (refs)
129     {
130       g_hash_table_destroy (refs);
131     }
132 }
133
134 static gboolean atspi_inited = FALSE;
135
136 static GHashTable *app_hash = NULL;
137
138 static void
139 handle_get_bus_address (DBusPendingCall *pending, void *user_data)
140 {
141   AtspiApplication *app = user_data;
142   DBusMessage *reply = dbus_pending_call_steal_reply (pending);
143   DBusMessage *message;
144   const char *address;
145   DBusPendingCall *new_pending;
146
147   if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
148   {
149     if (dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &address,
150                                DBUS_TYPE_INVALID))
151     {
152       DBusError error;
153       dbus_error_init (&error);
154       DBusConnection *bus = dbus_connection_open (address, &error);
155       if (bus)
156       {
157         if (app->bus)
158           dbus_connection_unref (app->bus);
159         app->bus = bus;
160       }
161     }
162   }
163   dbus_message_unref (reply);
164   dbus_pending_call_unref (pending);
165
166   message = dbus_message_new_method_call (app->bus_name,
167                                           "/org/a11y/atspi/cache",
168                                           atspi_interface_cache, "GetItems");
169
170    dbus_connection_send_with_reply (app->bus, message, &new_pending, 2000);
171   dbus_pending_call_set_notify (new_pending, handle_get_items, app, NULL);
172   dbus_message_unref (message);
173 }
174
175 static AtspiApplication *
176 get_application (const char *bus_name)
177 {
178   AtspiApplication *app = NULL;
179   char *bus_name_dup;
180   DBusMessage *message;
181   DBusError error;
182   DBusPendingCall *pending = NULL;
183
184   if (!app_hash)
185   {
186     app_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_unref);
187     if (!app_hash) return NULL;
188   }
189   app = g_hash_table_lookup (app_hash, bus_name);
190   if (app) return app;
191   bus_name_dup = g_strdup (bus_name);
192   if (!bus_name_dup) return NULL;
193   // TODO: change below to something that will send state-change:defunct notification if necessary */
194   app = _atspi_application_new (bus_name);
195   if (!app) return NULL;
196   app->bus_name = bus_name_dup;
197   app->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
198   app->bus = dbus_connection_ref (_atspi_bus ());
199   g_hash_table_insert (app_hash, bus_name_dup, app);
200   dbus_error_init (&error);
201   message = dbus_message_new_method_call (bus_name, atspi_path_root,
202                                           atspi_interface_application, "GetApplicationBusAddress");
203
204    dbus_connection_send_with_reply (app->bus, message, &pending, 2000);
205   dbus_pending_call_set_notify (pending, handle_get_bus_address, app, NULL);
206   dbus_message_unref (message);
207   return app;
208 }
209
210 static AtspiAccessible *
211 ref_accessible (const char *app_name, const char *path)
212 {
213   AtspiApplication *app;
214   AtspiAccessible *a;
215
216   if (!strcmp (path, ATSPI_DBUS_PATH_NULL))
217     return NULL;
218
219   app = get_application (app_name);
220
221   if (!strcmp (path, "/org/a11y/atspi/accessible/root"))
222   {
223     if (!app->root)
224     {
225       app->root = atspi_accessible_new (app, atspi_path_root);
226       app->root->accessible_parent = atspi_get_desktop (0);
227     }
228     return g_object_ref (app->root);
229   }
230
231   a = g_hash_table_lookup (app->hash, path);
232   if (a)
233   {
234     return g_object_ref (a);
235   }
236   a = atspi_accessible_new (app, path);
237   if (!a)
238     return NULL;
239   g_hash_table_insert (app->hash, g_strdup (a->parent.path), a);
240   g_object_ref (a);     /* for the hash */
241   return a;
242 }
243
244 static AtspiHyperlink *
245 ref_hyperlink (const char *app_name, const char *path)
246 {
247   AtspiApplication *app = get_application (app_name);
248   AtspiHyperlink *hyperlink;
249
250   if (!strcmp (path, ATSPI_DBUS_PATH_NULL))
251     return NULL;
252
253   hyperlink = g_hash_table_lookup (app->hash, path);
254   if (hyperlink)
255   {
256     return g_object_ref (hyperlink);
257   }
258   hyperlink = atspi_hyperlink_new (app, path);
259   if (!hyperlink)
260     return NULL;
261   g_hash_table_insert (app->hash, g_strdup (hyperlink->parent.path), hyperlink);
262   /* TODO: This should be a weak ref */
263   g_object_ref (hyperlink);     /* for the hash */
264   return hyperlink;
265 }
266
267 typedef struct
268 {
269   char *path;
270   char *parent;
271   GArray *children;
272   GArray *interfaces;
273   char *name;
274   dbus_uint32_t role;
275   char *description;
276   GArray *state_bitflags;
277 } CACHE_ADDITION;
278
279 static DBusHandlerResult
280 handle_remove_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
281 {
282   const char *sender = dbus_message_get_sender (message);
283   AtspiApplication *app;
284   const char *path;
285   DBusMessageIter iter, iter_struct;
286   const char *signature = dbus_message_get_signature (message);
287   AtspiAccessible *a;
288   int id;
289
290   if (strcmp (signature, "(so)") != 0)
291   {
292     g_warning (_("AT-SPI: Unknown signature %s for RemoveAccessible"), signature);
293     return DBUS_HANDLER_RESULT_HANDLED;
294   }
295
296   dbus_message_iter_init (message, &iter);
297   dbus_message_iter_recurse (&iter, &iter_struct);
298   dbus_message_iter_get_basic (&iter_struct, &sender);
299   dbus_message_iter_next (&iter_struct);
300   dbus_message_iter_get_basic (&iter_struct, &path);
301   app = get_application (sender);
302   a = ref_accessible (sender, path);
303   if (!a)
304     return DBUS_HANDLER_RESULT_HANDLED;
305   g_object_run_dispose (G_OBJECT (a));
306   g_hash_table_remove (app->hash, a->parent.path);
307   g_object_unref (a);   /* unref our own ref */
308   return DBUS_HANDLER_RESULT_HANDLED;
309 }
310
311 static gboolean
312 add_app_to_desktop (AtspiAccessible *a, const char *bus_name)
313 {
314   DBusError error;
315   char *root_path;
316
317   dbus_error_init (&error);
318   AtspiAccessible *obj = ref_accessible (bus_name, atspi_path_root);
319   if (obj)
320   {
321     GList *new_list = g_list_append (a->children, obj);
322     if (new_list)
323     {
324       a->children = new_list;
325       return TRUE;
326     }
327   }
328   else
329   {
330     g_warning (_("AT-SPI: Error calling getRoot for %s: %s"), bus_name, error.message);
331   }
332   return FALSE;
333 }
334
335 static void
336 send_children_changed (AtspiAccessible *parent, AtspiAccessible *child, gboolean add)
337 {
338   AtspiEvent e;
339
340   memset (&e, 0, sizeof (e));
341   e.type = (add? "object:children-changed:add": "object:children-changed:remove");
342   e.source = parent;
343   e.detail1 = g_list_index (parent->children, child);
344   e.detail2 = 0;
345   _atspi_send_event (&e);
346 }
347
348 static void
349 unref_object_and_descendants (AtspiAccessible *obj)
350 {
351   GList *l;
352
353   for (l = obj->children; l; l = l->next)
354   {
355     unref_object_and_descendants (l->data);
356   }
357   g_object_unref (obj);
358 }
359
360 static gboolean
361 remove_app_from_desktop (AtspiAccessible *a, const char *bus_name)
362 {
363   GList *l;
364   AtspiAccessible *child;
365
366   for (l = a->children; l; l = l->next)
367   {
368     child = l->data;
369     if (!strcmp (bus_name, child->parent.app->bus_name)) break;
370   }
371   if (!l)
372   {
373     return FALSE;
374   }
375   send_children_changed (a, child, FALSE);
376   a->children = g_list_remove (a->children, child);
377   unref_object_and_descendants (child);
378   return TRUE;
379 }
380
381 static AtspiAccessible *desktop;
382
383 void
384 get_reference_from_iter (DBusMessageIter *iter, const char **app_name, const char **path)
385 {
386   DBusMessageIter iter_struct;
387
388   dbus_message_iter_recurse (iter, &iter_struct);
389   dbus_message_iter_get_basic (&iter_struct, app_name);
390   dbus_message_iter_next (&iter_struct);
391   dbus_message_iter_get_basic (&iter_struct, path);
392   dbus_message_iter_next (iter);
393 }
394
395 static void
396 add_accessible_from_iter (DBusMessageIter *iter)
397 {
398   gint i;
399   GList *new_list;
400   DBusMessageIter iter_struct, iter_array;
401   const char *app_name, *path;
402   AtspiApplication *app;
403   AtspiAccessible *accessible;
404   const char *name, *description;
405   dbus_uint32_t role;
406
407   dbus_message_iter_recurse (iter, &iter_struct);
408
409   /* get accessible */
410   get_reference_from_iter (&iter_struct, &app_name, &path);
411   accessible = ref_accessible (app_name, path);
412   if (!accessible)
413     return;
414
415   /* Get application: TODO */
416   dbus_message_iter_next (&iter_struct);
417
418   /* get parent */
419   get_reference_from_iter (&iter_struct, &app_name, &path);
420   if (accessible->accessible_parent)
421     g_object_unref (accessible->accessible_parent);
422   accessible->accessible_parent = ref_accessible (app_name, path);
423
424   /* Get children */
425   while (accessible->children)
426   {
427     g_object_unref (accessible->children->data);
428     accessible->children = g_list_remove (accessible->children, accessible->children->data);
429   }
430   dbus_message_iter_recurse (&iter_struct, &iter_array);
431   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
432   {
433     AtspiAccessible *child;
434     get_reference_from_iter (&iter_array, &app_name, &path);
435     child = ref_accessible (app_name, path);
436     new_list = g_list_append (accessible->children, child);
437     if (new_list) accessible->children = new_list;
438   }
439
440   /* interfaces */
441   dbus_message_iter_next (&iter_struct);
442   _atspi_dbus_set_interfaces (accessible, &iter_struct);
443   dbus_message_iter_next (&iter_struct);
444
445   /* name */
446   if (accessible->name)
447     g_free (accessible->name);
448   dbus_message_iter_get_basic (&iter_struct, &name);
449   accessible->name = g_strdup (name);
450   dbus_message_iter_next (&iter_struct);
451
452   /* role */
453   dbus_message_iter_get_basic (&iter_struct, &role);
454   accessible->role = role;
455   dbus_message_iter_next (&iter_struct);
456
457   /* description */
458   if (accessible->description)
459     g_free (accessible->description);
460   dbus_message_iter_get_basic (&iter_struct, &description);
461   accessible->description = g_strdup (description);
462   dbus_message_iter_next (&iter_struct);
463
464   _atspi_dbus_set_state (accessible, &iter_struct);
465   dbus_message_iter_next (&iter_struct);
466
467   accessible->cached_properties |= ATSPI_CACHE_NAME | ATSPI_CACHE_ROLE | ATSPI_CACHE_PARENT;
468   if (!atspi_state_set_contains (accessible->states,
469                                        ATSPI_STATE_MANAGES_DESCENDANTS))
470     accessible->cached_properties |= ATSPI_CACHE_CHILDREN;
471
472   /* This is a bit of a hack since the cache holds a ref, so we don't need
473    * the one provided for us anymore */
474   g_object_unref (accessible);
475 }
476
477 static void
478 handle_get_items (DBusPendingCall *pending, void *user_data)
479 {
480   AtspiApplication *app = user_data;
481   DBusMessage *reply = dbus_pending_call_steal_reply (pending);
482   DBusMessageIter iter, iter_array;
483
484   if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
485   {
486     const char *sender = dbus_message_get_sender (reply);
487     const char *error = NULL;
488     dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &error,
489                            DBUS_TYPE_INVALID);
490     g_warning (_("AT-SPI: Error in GetItems, sender=%s, error=%s"), sender, error);
491     dbus_message_unref (reply);
492     dbus_pending_call_unref (pending);
493     return;
494   }
495
496   dbus_message_iter_init (reply, &iter);
497   dbus_message_iter_recurse (&iter, &iter_array);
498   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
499   {
500     add_accessible_from_iter (&iter_array);
501     dbus_message_iter_next (&iter_array);
502   }
503   dbus_message_unref (reply);
504   dbus_pending_call_unref (pending);
505 }
506
507 /* TODO: Do we stil need this function? */
508 static AtspiAccessible *
509 ref_accessible_desktop (AtspiApplication *app)
510 {
511   DBusError error;
512   GArray *apps = NULL;
513   GArray *additions;
514   gint i;
515   DBusMessage *message, *reply;
516   DBusMessageIter iter, iter_array;
517   gchar *bus_name_dup;
518
519   if (desktop)
520   {
521     g_object_ref (desktop);
522     return desktop;
523   }
524   desktop = atspi_accessible_new (app, atspi_path_root);
525   if (!desktop)
526   {
527     return NULL;
528   }
529   g_hash_table_insert (app->hash, desktop->parent.path, desktop);
530   g_object_ref (desktop);       /* for the hash */
531   desktop->name = g_strdup ("main");
532   dbus_error_init (&error);
533   message = dbus_message_new_method_call (atspi_bus_registry,
534         atspi_path_root,
535         atspi_interface_accessible,
536         "GetChildren");
537   if (!message)
538     return;
539   reply = _atspi_dbus_send_with_reply_and_block (message, NULL);
540   if (!reply || strcmp (dbus_message_get_signature (reply), "a(so)") != 0)
541   {
542     g_error ("Couldn't get application list: %s", error.message);
543     if (reply)
544       dbus_message_unref (reply);
545     return;
546   }
547   dbus_message_iter_init (reply, &iter);
548   dbus_message_iter_recurse (&iter, &iter_array);
549   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
550   {
551     const char *app_name, *path;
552     get_reference_from_iter (&iter_array, &app_name, &path);
553     add_app_to_desktop (desktop, app_name);
554   }
555   dbus_message_unref (reply);
556
557   /* Record the alternate name as an alias for org.a11y.atspi.Registry */
558   bus_name_dup = g_strdup (dbus_message_get_sender (reply));
559   if (bus_name_dup)
560     g_hash_table_insert (app_hash, bus_name_dup, app);
561
562   return desktop;
563 }
564
565 AtspiAccessible *
566 _atspi_ref_accessible (const char *app, const char *path)
567 {
568   AtspiApplication *a = get_application (app);
569   if (!a) return NULL;
570   if ( APP_IS_REGISTRY(a))
571   {
572     return ref_accessible_desktop (a);
573   }
574   return ref_accessible (app, path);
575 }
576
577 AtspiAccessible *
578 _atspi_dbus_return_accessible_from_message (DBusMessage *message)
579 {
580   DBusMessageIter iter;
581   AtspiAccessible *retval = NULL;
582   const char *signature;
583
584   if (!message)
585     return NULL;
586
587   signature = dbus_message_get_signature (message);
588   if (!strcmp (signature, "(so)"))
589   {
590     dbus_message_iter_init (message, &iter);
591     retval =  _atspi_dbus_return_accessible_from_iter (&iter);
592   }
593   else
594   {
595     g_warning (_("AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange signature %s"), signature);
596   }
597   dbus_message_unref (message);
598   return retval;
599 }
600
601 AtspiAccessible *
602 _atspi_dbus_return_accessible_from_iter (DBusMessageIter *iter)
603 {
604   const char *app_name, *path;
605
606   get_reference_from_iter (iter, &app_name, &path);
607   return ref_accessible (app_name, path);
608 }
609
610 AtspiHyperlink *
611 _atspi_dbus_return_hyperlink_from_message (DBusMessage *message)
612 {
613   DBusMessageIter iter;
614   AtspiHyperlink *retval = NULL;
615   const char *signature = dbus_message_get_signature (message);
616    
617   if (!strcmp (signature, "(so)"))
618   {
619     dbus_message_iter_init (message, &iter);
620     retval =  _atspi_dbus_return_hyperlink_from_iter (&iter);
621   }
622   else
623   {
624     g_warning (_("AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange signature %s"), signature);
625   }
626   dbus_message_unref (message);
627   return retval;
628 }
629
630 AtspiHyperlink *
631 _atspi_dbus_return_hyperlink_from_iter (DBusMessageIter *iter)
632 {
633   const char *app_name, *path;
634
635   get_reference_from_iter (iter, &app_name, &path);
636   return ref_hyperlink (app_name, path);
637 }
638
639 const char *cache_signal_type = "((so)(so)(so)a(so)assusau)";
640
641 static DBusHandlerResult
642 handle_add_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
643 {
644   DBusMessageIter iter;
645   const char *sender = dbus_message_get_sender (message);
646   AtspiApplication *app = get_application (sender);
647   const char *type = cache_signal_type;
648
649   if (strcmp (dbus_message_get_signature (message), cache_signal_type) != 0)
650   {
651     g_warning (_("AT-SPI: AddAccessible with unknown signature %s\n"),
652                dbus_message_get_signature (message));
653     return;
654   }
655
656   dbus_message_iter_init (message, &iter);
657   add_accessible_from_iter (&iter);
658 }
659
660 typedef struct
661 {
662   DBusConnection *bus;
663   DBusMessage *message;
664   void *data;
665 } BusDataClosure;
666
667 static guint process_deferred_messages_id = -1;
668
669 static void
670 process_deferred_message (BusDataClosure *closure)
671 {
672   int type = dbus_message_get_type (closure->message);
673   const char *interface = dbus_message_get_interface (closure->message);
674   const char *member = dbus_message_get_member (closure->message); 
675   dbus_uint32_t v;
676   char *bus_name;
677
678   if (type == DBUS_MESSAGE_TYPE_SIGNAL &&
679       !strncmp (interface, "org.a11y.atspi.Event.", 21))
680   {
681     atspi_dbus_handle_event (closure->bus, closure->message, closure->data);
682   }
683   if (dbus_message_is_method_call (closure->message, atspi_interface_device_event_listener, "NotifyEvent"))
684   {
685     atspi_dbus_handle_DeviceEvent (closure->bus,
686                                    closure->message, closure->data);
687   }
688   if (dbus_message_is_signal (closure->message, atspi_interface_cache, "AddAccessible"))
689   {
690     handle_add_accessible (closure->bus, closure->message, closure->data);
691   }
692   if (dbus_message_is_signal (closure->message, atspi_interface_cache, "RemoveAccessible"))
693   {
694     handle_remove_accessible (closure->bus, closure->message, closure->data);
695   }
696 }
697
698 static GList *deferred_messages = NULL;
699
700 gboolean
701 _atspi_process_deferred_messages (gpointer data)
702 {
703   static int in_process_deferred_messages = 0;
704
705   if (in_process_deferred_messages)
706     return;
707   in_process_deferred_messages = 1;
708   while (deferred_messages != NULL)
709   {
710     BusDataClosure *closure = deferred_messages->data;
711     process_deferred_message (closure);
712     deferred_messages = g_list_remove (deferred_messages, closure);
713     dbus_message_unref (closure->message);
714     dbus_connection_unref (closure->bus);
715     g_free (closure);
716   }
717   /* If data is NULL, assume that we were called from GLib */
718   if (!data)
719     process_deferred_messages_id = -1;
720   in_process_deferred_messages = 0;
721   return FALSE;
722 }
723
724 static DBusHandlerResult
725 defer_message (DBusConnection *connection, DBusMessage *message, void *user_data)
726 {
727   BusDataClosure *closure = g_new (BusDataClosure, 1);
728   GList *new_list;
729
730   if (!closure)
731     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
732   closure->bus = dbus_connection_ref (bus);
733   closure->message = dbus_message_ref (message);
734   closure->data = user_data;
735
736   new_list = g_list_append (deferred_messages, closure);
737   if (new_list)
738     deferred_messages = new_list;
739
740   if (process_deferred_messages_id == -1)
741     process_deferred_messages_id = g_idle_add (_atspi_process_deferred_messages, NULL);
742   return DBUS_HANDLER_RESULT_HANDLED;
743 }
744
745 static DBusHandlerResult
746 atspi_dbus_filter (DBusConnection *bus, DBusMessage *message, void *data)
747 {
748   int type = dbus_message_get_type (message);
749   const char *interface = dbus_message_get_interface (message);
750   const char *member = dbus_message_get_member (message); 
751   dbus_uint32_t v;
752   char *bus_name;
753
754   if (type == DBUS_MESSAGE_TYPE_SIGNAL &&
755       !strncmp (interface, "org.a11y.atspi.Event.", 21))
756   {
757     return defer_message (bus, message, data);
758   }
759   if (dbus_message_is_method_call (message, atspi_interface_device_event_listener, "NotifyEvent"))
760   {
761     return defer_message (bus, message, data);
762   }
763   if (dbus_message_is_signal (message, atspi_interface_cache, "AddAccessible"))
764   {
765     return defer_message (bus, message, data);
766   }
767   if (dbus_message_is_signal (message, atspi_interface_cache, "RemoveAccessible"))
768   {
769     return defer_message (bus, message, data);
770   }
771   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
772 }
773
774 static const char *signal_interfaces[] =
775 {
776   "org.a11y.atspi.Event.Object",
777   "org.a11y.atspi.Event.Window",
778   "org.a11y.atspi.Event.Mouse",
779   "org.a11y.atspi.Event.Terminal",
780   "org.a11y.atspi.Event.Document",
781   "org.a11y.atspi.Event.Focus",
782   NULL
783 };
784
785 /*
786  * Returns a 'canonicalized' value for DISPLAY,
787  * with the screen number stripped off if present.
788  *
789  * TODO: Avoid having duplicate functions for this here and in at-spi2-atk
790  */
791 static const gchar *
792 spi_display_name (void)
793 {
794   static const char *canonical_display_name = NULL;
795   if (!canonical_display_name)
796     {
797       const gchar *display_env = g_getenv ("AT_SPI_DISPLAY");
798       if (!display_env)
799         {
800           display_env = g_getenv ("DISPLAY");
801           if (!display_env || !display_env[0])
802             canonical_display_name = ":0";
803           else
804             {
805               gchar *display_p, *screen_p;
806               canonical_display_name = g_strdup (display_env);
807               display_p = g_utf8_strrchr (canonical_display_name, -1, ':');
808               screen_p = g_utf8_strrchr (canonical_display_name, -1, '.');
809               if (screen_p && display_p && (screen_p > display_p))
810                 {
811                   *screen_p = '\0';
812                 }
813             }
814         }
815       else
816         {
817           canonical_display_name = display_env;
818         }
819     }
820   return canonical_display_name;
821 }
822
823 /* TODO: Avoid having duplicate functions for this here and in at-spi2-atk */
824 static DBusConnection *
825 get_accessibility_bus ()
826 {
827   Atom AT_SPI_BUS;
828   Atom actual_type;
829   Display *bridge_display;
830   int actual_format;
831   unsigned char *data = NULL;
832   unsigned long nitems;
833   unsigned long leftover;
834
835   DBusConnection *bus = NULL;
836   DBusError error;
837
838   bridge_display = XOpenDisplay (spi_display_name ());
839   if (!bridge_display)
840     {
841       g_warning (_("AT-SPI: Could not get the display\n"));
842       return NULL;
843     }
844
845   AT_SPI_BUS = XInternAtom (bridge_display, "AT_SPI_BUS", False);
846   XGetWindowProperty (bridge_display,
847                       XDefaultRootWindow (bridge_display),
848                       AT_SPI_BUS, 0L,
849                       (long) BUFSIZ, False,
850                       (Atom) 31, &actual_type, &actual_format,
851                       &nitems, &leftover, &data);
852   XCloseDisplay (bridge_display);
853
854   dbus_error_init (&error);
855
856   if (data == NULL)
857     {
858       g_warning
859         (_("AT-SPI: Accessibility bus not found - Using session bus.\n"));
860       bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
861       if (!bus)
862         {
863           g_warning (_("AT-SPI: Couldn't connect to bus: %s\n"), error.message);
864           return NULL;
865         }
866     }
867   else
868     {
869       bus = dbus_connection_open (data, &error);
870       if (!bus)
871         {
872           g_warning (_("AT-SPI: Couldn't connect to bus: %s\n"), error.message);
873           return NULL;
874         }
875       else
876         {
877           if (!dbus_bus_register (bus, &error))
878             {
879               g_warning (_("AT-SPI: Couldn't register with bus: %s\n"), error.message);
880               return NULL;
881             }
882         }
883     }
884
885   return bus;
886 }
887
888 /**
889  * atspi_init:
890  *
891  * Connects to the accessibility registry and initializes the SPI.
892  *
893  * Returns: 0 on success, otherwise an integer error code.  
894  **/
895 int
896 atspi_init (void)
897 {
898   DBusError error;
899   char *match;
900   int i;
901
902   if (atspi_inited)
903     {
904       return 1;
905     }
906
907   atspi_inited = TRUE;
908
909   g_type_init ();
910
911   get_live_refs();
912
913   dbus_error_init (&error);
914   bus = get_accessibility_bus ();
915   if (!bus)
916   {
917     g_error ("Couldn't get session bus");
918     return 2;
919   }
920   dbus_bus_register (bus, &error);
921   dbus_connection_setup_with_g_main(bus, g_main_context_default());
922   dbus_connection_add_filter (bus, atspi_dbus_filter, NULL, NULL);
923   dbind_set_timeout (1000);
924   match = g_strdup_printf ("type='signal',interface='%s',member='AddAccessible'", atspi_interface_cache);
925   dbus_error_init (&error);
926   dbus_bus_add_match (bus, match, &error);
927   g_free (match);
928   match = g_strdup_printf ("type='signal',interface='%s',member='RemoveAccessible'", atspi_interface_cache);
929   dbus_bus_add_match (bus, match, &error);
930   g_free (match);
931   match = g_strdup_printf ("type='signal',interface='%s',member='ChildrenChanged'", atspi_interface_event_object);
932   dbus_bus_add_match (bus, match, &error);
933   g_free (match);
934   match = g_strdup_printf ("type='signal',interface='%s',member='PropertyChange'", atspi_interface_event_object);
935   dbus_bus_add_match (bus, match, &error);
936   g_free (match);
937   match = g_strdup_printf ("type='signal',interface='%s',member='StateChanged'", atspi_interface_event_object);
938   dbus_bus_add_match (bus, match, &error);
939   g_free (match);
940   return 0;
941 }
942
943   static GMainLoop *mainloop;
944
945 /**
946  * atspi_event_main:
947  *
948  * Starts/enters the main event loop for the AT-SPI services.
949  *
950  * (NOTE: This method does not return control, it is exited via a call to
951  *  atspi_event_quit () from within an event handler).
952  *
953  **/
954 void
955 atspi_event_main (void)
956 {
957   mainloop = g_main_loop_new (NULL, FALSE);
958   g_main_loop_run (mainloop);
959 }
960
961 /**
962  * atspi_event_quit:
963  *
964  * Quits the last main event loop for the SPI services,
965  * see atspi_event_main
966  **/
967 void
968 atspi_event_quit (void)
969 {
970   g_main_loop_quit (mainloop);
971 }
972
973 /**
974  * atspi_exit:
975  *
976  * Disconnects from the Accessibility Registry and releases 
977  * any floating resources. Call only once at exit.
978  *
979  * Returns: 0 if there were no leaks, otherwise non zero.
980  **/
981 int
982 atspi_exit (void)
983 {
984   int leaked;
985
986   if (!atspi_inited)
987     {
988       return 0;
989     }
990
991   atspi_inited = FALSE;
992
993   if (live_refs)
994     {
995       leaked = g_hash_table_size (live_refs);
996     }
997   else
998     {
999       leaked = 0;
1000     }
1001
1002   cleanup ();
1003
1004   return leaked;
1005 }
1006
1007 dbus_bool_t
1008 _atspi_dbus_call (gpointer obj, const char *interface, const char *method, GError **error, const char *type, ...)
1009 {
1010   va_list args;
1011   dbus_bool_t retval;
1012   DBusError err;
1013   AtspiObject *aobj = ATSPI_OBJECT (obj);
1014
1015   if (!aobj->app || !aobj->app->bus)
1016   {
1017     g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_APPLICATION_GONE,
1018                           _("The application no longer exists"));
1019     return FALSE;
1020   }
1021
1022   va_start (args, type);
1023   dbus_error_init (&err);
1024   retval = dbind_method_call_reentrant_va (aobj->app->bus, aobj->app->bus_name,
1025                                            aobj->path, interface, method, &err,
1026                                            type, args);
1027   va_end (args);
1028   _atspi_process_deferred_messages ((gpointer)TRUE);
1029   if (dbus_error_is_set (&err))
1030   {
1031     /* TODO: Set gerror */
1032     dbus_error_free (&err);
1033   }
1034   return retval;
1035 }
1036
1037 DBusMessage *
1038 _atspi_dbus_call_partial (gpointer obj,
1039                           const char *interface,
1040                           const char *method,
1041                           GError **error,
1042                           const char *type, ...)
1043 {
1044   va_list args;
1045
1046   va_start (args, type);
1047   return _atspi_dbus_call_partial_va (obj, interface, method, error, type, args);
1048 }
1049
1050 DBusMessage *
1051 _atspi_dbus_call_partial_va (gpointer obj,
1052                           const char *interface,
1053                           const char *method,
1054                           GError **error,
1055                           const char *type,
1056                           va_list args)
1057 {
1058   AtspiObject *aobj = ATSPI_OBJECT (obj);
1059   dbus_bool_t retval;
1060   DBusError err;
1061     DBusMessage *msg = NULL, *reply = NULL;
1062     DBusMessageIter iter;
1063     const char *p;
1064
1065   dbus_error_init (&err);
1066
1067   if (!aobj->app || !aobj->app->bus)
1068   {
1069     g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_APPLICATION_GONE,
1070                           _("The application no longer exists"));
1071     goto out;
1072   }
1073
1074     msg = dbus_message_new_method_call (aobj->app->bus_name, aobj->path, interface, method);
1075     if (!msg)
1076         goto out;
1077
1078     p = type;
1079     dbus_message_iter_init_append (msg, &iter);
1080     dbind_any_marshal_va (&iter, &p, args);
1081
1082     reply = dbind_send_and_allow_reentry (aobj->app->bus, msg, &err);
1083 out:
1084   va_end (args);
1085   if (msg)
1086     dbus_message_unref (msg);
1087   _atspi_process_deferred_messages ((gpointer)TRUE);
1088   if (dbus_error_is_set (&err))
1089   {
1090     /* TODO: Set gerror */
1091     dbus_error_free (&err);
1092   }
1093   return reply;
1094 }
1095
1096 dbus_bool_t
1097 _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, GError **error, const char *type, void *data)
1098 {
1099   DBusMessage *message, *reply;
1100   DBusMessageIter iter, iter_variant;
1101   DBusError err;
1102   dbus_bool_t retval = FALSE;
1103   AtspiObject *aobj = ATSPI_OBJECT (obj);
1104
1105   if (!aobj)
1106     return FALSE;
1107
1108   if (!aobj->app || !aobj->app->bus)
1109   {
1110     g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_APPLICATION_GONE,
1111                           _("The application no longer exists"));
1112     return FALSE;
1113   }
1114
1115   message = dbus_message_new_method_call (aobj->app->bus_name,
1116                                           aobj->path,
1117                                           "org.freedesktop.DBus.Properties",
1118                                           "Get");
1119   if (!message)
1120   {
1121     // TODO: throw exception
1122     return FALSE;
1123   }
1124   dbus_message_append_args (message, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
1125   dbus_error_init (&err);
1126   reply = dbind_send_and_allow_reentry (aobj->app->bus, message, &err);
1127   dbus_message_unref (message);
1128   _atspi_process_deferred_messages ((gpointer)TRUE);
1129   if (!reply)
1130   {
1131     // TODO: throw exception
1132     goto done;
1133   }
1134
1135   if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
1136   {
1137     const char *err;
1138     dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &err, DBUS_TYPE_INVALID);
1139     if (err)
1140       g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err);
1141     goto done;
1142   }
1143
1144   dbus_message_iter_init (reply, &iter);
1145   if (dbus_message_iter_get_arg_type (&iter) != 'v')
1146   {
1147     g_warning (_("AT-SPI: expected a variant when fetching %s from interface %s; got %s\n"), name, interface, dbus_message_get_signature (reply));
1148     goto done;
1149   }
1150   dbus_message_iter_recurse (&iter, &iter_variant);
1151   if (dbus_message_iter_get_arg_type (&iter_variant) != type[0])
1152   {
1153     g_warning (_("atspi_dbus_get_property: Wrong type: expected %s, got %c\n"), type, dbus_message_iter_get_arg_type (&iter_variant));
1154     goto done;
1155   }
1156   if (!strcmp (type, "(so)"))
1157   {
1158     *((AtspiAccessible **)data) = _atspi_dbus_return_accessible_from_iter (&iter_variant);
1159   }
1160   else
1161   {
1162     dbus_message_iter_get_basic (&iter_variant, data);
1163     if (type [0] == 's')
1164       *(char **)data = g_strdup (*(char **)data);
1165   }
1166   retval = TRUE;
1167 done:
1168   if (reply)
1169     dbus_message_unref (reply);
1170   return retval;
1171 }
1172
1173 DBusMessage *
1174 _atspi_dbus_send_with_reply_and_block (DBusMessage *message, GError **error)
1175 {
1176   DBusMessage *reply;
1177   DBusError err;
1178   AtspiApplication *app;
1179   DBusConnection *bus;
1180
1181   app = get_application (dbus_message_get_destination (message));
1182
1183   if (app && !app->bus)
1184     return NULL;        /* will fail anyway; app has been disposed */
1185
1186   bus = (app ? app->bus : _atspi_bus());
1187   dbus_error_init (&err);
1188   reply = dbind_send_and_allow_reentry (bus, message, &err);
1189   _atspi_process_deferred_messages ((gpointer)TRUE);
1190   dbus_message_unref (message);
1191   if (err.message)
1192   {
1193     if (error)
1194       g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err.message);
1195     dbus_error_free (&err);
1196   }
1197   return reply;
1198 }
1199
1200 GHashTable *
1201 _atspi_dbus_return_hash_from_message (DBusMessage *message)
1202 {
1203   DBusMessageIter iter;
1204   GHashTable *ret;
1205
1206   if (!message)
1207     return NULL;
1208
1209   _ATSPI_DBUS_CHECK_SIG (message, "a{ss}", NULL, NULL);
1210
1211   dbus_message_iter_init (message, &iter);
1212   ret = _atspi_dbus_hash_from_iter (&iter);
1213   dbus_message_unref (message);
1214   return ret;
1215 }
1216
1217 GHashTable *
1218 _atspi_dbus_hash_from_iter (DBusMessageIter *iter)
1219 {
1220   GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal);
1221   DBusMessageIter iter_array, iter_dict;
1222
1223   dbus_message_iter_recurse (iter, &iter_array);
1224   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
1225   {
1226     const char *name, *value;
1227     dbus_message_iter_recurse (&iter_array, &iter_dict);
1228     dbus_message_iter_get_basic (&iter_dict, &name);
1229     dbus_message_iter_next (&iter_dict);
1230     dbus_message_iter_get_basic (&iter_dict, &value);
1231     g_hash_table_insert (hash, g_strdup (name), g_strdup (value));
1232     dbus_message_iter_next (&iter_array);
1233   }
1234   return hash;
1235 }
1236
1237 GArray *
1238 _atspi_dbus_return_attribute_array_from_message (DBusMessage *message)
1239 {
1240   DBusMessageIter iter;
1241   GArray *ret;
1242
1243   if (!message)
1244     return NULL;
1245
1246   _ATSPI_DBUS_CHECK_SIG (message, "a{ss}", NULL, NULL);
1247
1248   dbus_message_iter_init (message, &iter);
1249
1250   ret = _atspi_dbus_attribute_array_from_iter (&iter);
1251   dbus_message_unref (message);
1252   return ret;
1253 }
1254
1255 GArray *
1256 _atspi_dbus_attribute_array_from_iter (DBusMessageIter *iter)
1257 {
1258   DBusMessageIter iter_array, iter_dict;
1259   GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
1260   gint count = 0;
1261
1262   dbus_message_iter_recurse (iter, &iter_array);
1263   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
1264   {
1265     const char *name, *value;
1266     gchar *str;
1267     GArray *new_array;
1268     dbus_message_iter_recurse (&iter_array, &iter_dict);
1269     dbus_message_iter_get_basic (&iter_dict, &name);
1270     dbus_message_iter_next (&iter_dict);
1271     dbus_message_iter_get_basic (&iter_dict, &value);
1272     str = g_strdup_printf ("%s:%s", name, value);
1273     new_array = g_array_append_val (array, str);
1274     if (new_array)
1275       array = new_array;
1276     dbus_message_iter_next (&iter_array);;
1277   }
1278   return array;
1279 }
1280
1281 void
1282 _atspi_dbus_set_interfaces (AtspiAccessible *accessible, DBusMessageIter *iter)
1283 {
1284   DBusMessageIter iter_array;
1285
1286   accessible->interfaces = 0;
1287   dbus_message_iter_recurse (iter, &iter_array);
1288   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
1289   {
1290     const char *iface;
1291     gint n;
1292     dbus_message_iter_get_basic (&iter_array, &iface);
1293     if (!strcmp (iface, "org.freedesktop.DBus.Introspectable")) continue;
1294     n = _atspi_get_iface_num (iface);
1295     if (n == -1)
1296     {
1297       g_warning (_("AT-SPI: Unknown interface %s"), iface);
1298     }
1299     else
1300       accessible->interfaces |= (1 << n);
1301     dbus_message_iter_next (&iter_array);
1302   }
1303   accessible->cached_properties |= ATSPI_CACHE_INTERFACES;
1304 }
1305
1306 void
1307 _atspi_dbus_set_state (AtspiAccessible *accessible, DBusMessageIter *iter)
1308 {
1309   DBusMessageIter iter_array;
1310   gint count;
1311   dbus_uint32_t *states;
1312
1313   dbus_message_iter_recurse (iter, &iter_array);
1314   dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
1315   if (count != 2)
1316   {
1317     g_warning (_("AT-SPI: expected 2 values in states array; got %d\n"), count);
1318     if (!accessible->states)
1319       accessible->states = _atspi_state_set_new_internal (accessible, 0);
1320   }
1321   else
1322   {
1323     guint64 val = ((guint64)states [1]) << 32;
1324     val += states [0];
1325     if (!accessible->states)
1326       accessible->states = _atspi_state_set_new_internal (accessible, val);
1327     else
1328       accessible->states->states = val;
1329   }
1330   accessible->cached_properties |= ATSPI_CACHE_STATES;
1331 }
1332
1333 GQuark
1334 atspi_error_quark (void)
1335 {
1336   return g_quark_from_static_string ("atspi_error");
1337 }
1338