Don't display warnings when connecting to an app that no longer exists
[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  * Copyright 2010, 2011 Novell, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /*
26  *
27  * Basic SPI initialization and event loop function prototypes
28  *
29  */
30
31 #include "atspi-private.h"
32 #ifdef HAVE_X11
33 #include "X11/Xlib.h"
34 #endif
35 #include "atspi-gmain.h"
36 #include <stdio.h>
37 #include <string.h>
38
39 static void handle_get_items (DBusPendingCall *pending, void *user_data);
40
41 static DBusConnection *bus = NULL;
42 static GHashTable *live_refs = NULL;
43 static gint method_call_timeout = 800;
44 static gint app_startup_time = 15000;
45 static gboolean allow_sync = TRUE;
46
47 GMainLoop *atspi_main_loop;
48 GMainContext *atspi_main_context;
49 gboolean atspi_no_cache;
50
51 const char *atspi_path_dec = ATSPI_DBUS_PATH_DEC;
52 const char *atspi_path_registry = ATSPI_DBUS_PATH_REGISTRY;
53 const char *atspi_path_root = ATSPI_DBUS_PATH_ROOT;
54 const char *atspi_bus_registry = ATSPI_DBUS_NAME_REGISTRY;
55 const char *atspi_interface_accessible = ATSPI_DBUS_INTERFACE_ACCESSIBLE;
56 const char *atspi_interface_action = ATSPI_DBUS_INTERFACE_ACTION;
57 const char *atspi_interface_application = ATSPI_DBUS_INTERFACE_APPLICATION;
58 const char *atspi_interface_collection = ATSPI_DBUS_INTERFACE_COLLECTION;
59 const char *atspi_interface_component = ATSPI_DBUS_INTERFACE_COMPONENT;
60 const char *atspi_interface_dec = ATSPI_DBUS_INTERFACE_DEC;
61 const char *atspi_interface_device_event_listener = ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER;
62 const char *atspi_interface_document = ATSPI_DBUS_INTERFACE_DOCUMENT;
63 const char *atspi_interface_editable_text = ATSPI_DBUS_INTERFACE_EDITABLE_TEXT;
64 const char *atspi_interface_event_object = ATSPI_DBUS_INTERFACE_EVENT_OBJECT;
65 const char *atspi_interface_hyperlink = ATSPI_DBUS_INTERFACE_HYPERLINK;
66 const char *atspi_interface_hypertext = ATSPI_DBUS_INTERFACE_HYPERTEXT;
67 const char *atspi_interface_image = ATSPI_DBUS_INTERFACE_IMAGE;
68 const char *atspi_interface_registry = ATSPI_DBUS_INTERFACE_REGISTRY;
69 const char *atspi_interface_selection = ATSPI_DBUS_INTERFACE_SELECTION;
70 const char *atspi_interface_table = ATSPI_DBUS_INTERFACE_TABLE;
71 const char *atspi_interface_table_cell = ATSPI_DBUS_INTERFACE_TABLE_CELL;
72 const char *atspi_interface_text = ATSPI_DBUS_INTERFACE_TEXT;
73 const char *atspi_interface_cache = ATSPI_DBUS_INTERFACE_CACHE;
74 const char *atspi_interface_value = ATSPI_DBUS_INTERFACE_VALUE;
75
76 static const char *interfaces[] =
77 {
78   ATSPI_DBUS_INTERFACE_ACCESSIBLE,
79   ATSPI_DBUS_INTERFACE_ACTION,
80   ATSPI_DBUS_INTERFACE_APPLICATION,
81   ATSPI_DBUS_INTERFACE_COLLECTION,
82   ATSPI_DBUS_INTERFACE_COMPONENT,
83   ATSPI_DBUS_INTERFACE_DOCUMENT,
84   ATSPI_DBUS_INTERFACE_EDITABLE_TEXT,
85   ATSPI_DBUS_INTERFACE_HYPERLINK,
86   ATSPI_DBUS_INTERFACE_HYPERTEXT,
87   ATSPI_DBUS_INTERFACE_IMAGE,
88   "org.a11y.atspi.LoginHelper",
89   ATSPI_DBUS_INTERFACE_SELECTION,
90   ATSPI_DBUS_INTERFACE_TABLE,
91   ATSPI_DBUS_INTERFACE_TABLE_CELL,
92   ATSPI_DBUS_INTERFACE_TEXT,
93   ATSPI_DBUS_INTERFACE_VALUE,
94   NULL
95 };
96
97 gint
98 _atspi_get_iface_num (const char *iface)
99 {
100   /* TODO: Use a binary search or hash to improve performance */
101   int i;
102
103   for (i = 0; interfaces[i]; i++)
104   {
105     if (!strcmp(iface, interfaces[i])) return i;
106   }
107   return -1;
108 }
109
110 GHashTable *
111 _atspi_get_live_refs (void)
112 {
113   if (!live_refs) 
114     {
115       live_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
116     }
117   return live_refs;
118 }
119
120 /* TODO: Add an application parameter */
121 DBusConnection *
122 _atspi_bus ()
123 {
124   if (!bus)
125     atspi_init ();
126   if (!bus)
127     g_error ("AT-SPI: Couldn't connect to accessibility bus. Is at-spi-bus-launcher running?");
128   return bus;
129 }
130
131 #define APP_IS_REGISTRY(app) (!strcmp (app->bus_name, atspi_bus_registry))
132
133 static AtspiAccessible *desktop;
134
135 static void
136 cleanup ()
137 {
138   GHashTable *refs;
139   gint i;
140
141   refs = live_refs;
142   live_refs = NULL;
143   if (refs)
144     {
145       g_hash_table_destroy (refs);
146     }
147
148   if (bus)
149     {
150       dbus_connection_close (bus);
151       dbus_connection_unref (bus);
152       bus = NULL;
153     }
154
155   if (!desktop)
156     return;
157
158   /* TODO: Do we need this code, or should we just dispose the desktop? */
159   for (i = desktop->children->len - 1; i >= 0; i--)
160   {
161     AtspiAccessible *child = g_ptr_array_index (desktop->children, i);
162     g_object_run_dispose (G_OBJECT (child->parent.app));
163     g_object_run_dispose (G_OBJECT (child));
164   }
165
166   g_object_run_dispose (G_OBJECT (desktop->parent.app));
167   g_object_unref (desktop);
168   desktop = NULL;
169 }
170
171 static gboolean atspi_inited = FALSE;
172
173 static GHashTable *app_hash = NULL;
174
175 static void
176 handle_get_bus_address (DBusPendingCall *pending, void *user_data)
177 {
178   AtspiApplication *app = user_data;
179   DBusMessage *reply = dbus_pending_call_steal_reply (pending);
180   DBusMessage *message;
181   const char *address;
182   DBusPendingCall *new_pending;
183
184   if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
185   {
186     if (dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &address,
187                                DBUS_TYPE_INVALID) && address [0])
188     {
189       DBusError error;
190       DBusConnection *bus;
191
192       dbus_error_init (&error);
193       bus = dbus_connection_open_private (address, &error);
194       if (bus)
195       {
196         if (app->bus)
197           {
198             dbus_connection_unref (app->bus);
199           }
200         app->bus = bus;
201       }
202       else
203       {
204         if (!strcmp (error.name, DBUS_ERROR_FILE_NOT_FOUND))
205           g_warning ("Unable to open bus connection: %s", error.message);
206         dbus_error_free (&error);
207       }
208     }
209   }
210   dbus_message_unref (reply);
211   dbus_pending_call_unref (pending);
212
213   if (!app->bus)
214     return; /* application has gone away / been disposed */
215
216   message = dbus_message_new_method_call (app->bus_name,
217                                           "/org/a11y/atspi/cache",
218                                           atspi_interface_cache, "GetItems");
219
220   dbus_connection_send_with_reply (app->bus, message, &new_pending, 2000);
221   dbus_message_unref (message);
222   if (!new_pending)
223     return;
224   dbus_pending_call_set_notify (new_pending, handle_get_items, app, NULL);
225 }
226
227 static AtspiApplication *
228 get_application (const char *bus_name)
229 {
230   AtspiApplication *app = NULL;
231   char *bus_name_dup;
232   DBusMessage *message;
233   DBusPendingCall *pending = NULL;
234
235   if (!app_hash)
236   {
237     app_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_object_unref);
238     if (!app_hash) return NULL;
239   }
240   app = g_hash_table_lookup (app_hash, bus_name);
241   if (app) return app;
242   bus_name_dup = g_strdup (bus_name);
243   if (!bus_name_dup) return NULL;
244   // TODO: change below to something that will send state-change:defunct notification if necessary */
245   app = _atspi_application_new (bus_name);
246   app->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
247   app->bus = dbus_connection_ref (_atspi_bus ());
248   gettimeofday (&app->time_added, NULL);
249   app->cache = ATSPI_CACHE_UNDEFINED;
250   g_hash_table_insert (app_hash, bus_name_dup, app);
251   message = dbus_message_new_method_call (bus_name, atspi_path_root,
252                                           atspi_interface_application, "GetApplicationBusAddress");
253
254   dbus_connection_send_with_reply (app->bus, message, &pending, 2000);
255   dbus_message_unref (message);
256   if (!pending)
257   {
258     g_hash_table_remove (app_hash, bus_name_dup);
259     return NULL;
260   }
261   dbus_pending_call_set_notify (pending, handle_get_bus_address, app, NULL);
262   return app;
263 }
264
265 static AtspiAccessible *
266 ref_accessible (const char *app_name, const char *path)
267 {
268   AtspiApplication *app;
269   AtspiAccessible *a;
270
271   if (!strcmp (path, ATSPI_DBUS_PATH_NULL))
272     return NULL;
273
274   app = get_application (app_name);
275
276   if (!strcmp (path, "/org/a11y/atspi/accessible/root"))
277   {
278     if (!app->root)
279     {
280       app->root = _atspi_accessible_new (app, atspi_path_root);
281       app->root->accessible_parent = atspi_get_desktop (0);
282       g_ptr_array_add (app->root->accessible_parent->children, g_object_ref (app->root));
283     }
284     return g_object_ref (app->root);
285   }
286
287   a = g_hash_table_lookup (app->hash, path);
288   if (a)
289   {
290     return g_object_ref (a);
291   }
292   a = _atspi_accessible_new (app, path);
293   if (!a)
294     return NULL;
295   g_hash_table_insert (app->hash, g_strdup (a->parent.path), g_object_ref (a));
296   return a;
297 }
298
299 static AtspiHyperlink *
300 ref_hyperlink (const char *app_name, const char *path)
301 {
302   AtspiApplication *app = get_application (app_name);
303   AtspiHyperlink *hyperlink;
304
305   if (!strcmp (path, ATSPI_DBUS_PATH_NULL))
306     return NULL;
307
308   hyperlink = g_hash_table_lookup (app->hash, path);
309   if (hyperlink)
310   {
311     return g_object_ref (hyperlink);
312   }
313   hyperlink = _atspi_hyperlink_new (app, path);
314   g_hash_table_insert (app->hash, g_strdup (hyperlink->parent.path), hyperlink);
315   /* TODO: This should be a weak ref */
316   g_object_ref (hyperlink);     /* for the hash */
317   return hyperlink;
318 }
319
320 typedef struct
321 {
322   char *path;
323   char *parent;
324   GArray *children;
325   GArray *interfaces;
326   char *name;
327   dbus_uint32_t role;
328   char *description;
329   GArray *state_bitflags;
330 } CACHE_ADDITION;
331
332 static DBusHandlerResult
333 handle_remove_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
334 {
335   const char *sender = dbus_message_get_sender (message);
336   AtspiApplication *app;
337   const char *path;
338   DBusMessageIter iter, iter_struct;
339   const char *signature = dbus_message_get_signature (message);
340   AtspiAccessible *a;
341
342   if (strcmp (signature, "(so)") != 0)
343   {
344     g_warning ("AT-SPI: Unknown signature %s for RemoveAccessible", signature);
345     return DBUS_HANDLER_RESULT_HANDLED;
346   }
347
348   dbus_message_iter_init (message, &iter);
349   dbus_message_iter_recurse (&iter, &iter_struct);
350   dbus_message_iter_get_basic (&iter_struct, &sender);
351   dbus_message_iter_next (&iter_struct);
352   dbus_message_iter_get_basic (&iter_struct, &path);
353   app = get_application (sender);
354   a = ref_accessible (sender, path);
355   if (!a)
356     return DBUS_HANDLER_RESULT_HANDLED;
357   g_object_run_dispose (G_OBJECT (a));
358   g_hash_table_remove (app->hash, a->parent.path);
359   g_object_unref (a);   /* unref our own ref */
360   return DBUS_HANDLER_RESULT_HANDLED;
361 }
362
363 static DBusHandlerResult
364 handle_name_owner_changed (DBusConnection *bus, DBusMessage *message, void *user_data)
365 {
366   const char *name, *new, *old;
367   static gboolean registry_lost = FALSE;
368
369   if (!dbus_message_get_args (message, NULL,
370                               DBUS_TYPE_STRING, &name,
371                               DBUS_TYPE_STRING, &old,
372                               DBUS_TYPE_STRING, &new,
373                               DBUS_TYPE_INVALID))
374   {
375     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
376   }
377
378   if (!strcmp (name, "org.a11y.atspi.Registry"))
379   {
380     if (registry_lost && !old[0])
381     {
382       _atspi_reregister_event_listeners ();
383       _atspi_reregister_device_listeners ();
384       registry_lost = FALSE;
385     }
386     else if (!new[0])
387       registry_lost = TRUE;
388   }
389   else if (app_hash)
390   {
391     AtspiApplication *app = g_hash_table_lookup (app_hash, old);
392     if (app)
393       g_object_run_dispose (G_OBJECT (app));
394   }
395   return DBUS_HANDLER_RESULT_HANDLED;
396 }
397
398 static gboolean
399 add_app_to_desktop (AtspiAccessible *a, const char *bus_name)
400 {
401   AtspiAccessible *obj = ref_accessible (bus_name, atspi_path_root);
402   /* The app will be added to the desktop as a side-effect of calling
403    * ref_accessible */
404   g_object_unref (obj);
405   return (obj != NULL);
406 }
407
408 void
409 get_reference_from_iter (DBusMessageIter *iter, const char **app_name, const char **path)
410 {
411   DBusMessageIter iter_struct;
412
413   dbus_message_iter_recurse (iter, &iter_struct);
414   dbus_message_iter_get_basic (&iter_struct, app_name);
415   dbus_message_iter_next (&iter_struct);
416   dbus_message_iter_get_basic (&iter_struct, path);
417   dbus_message_iter_next (iter);
418 }
419
420 static void
421 add_accessible_from_iter (DBusMessageIter *iter)
422 {
423   DBusMessageIter iter_struct, iter_array;
424   const char *app_name, *path;
425   AtspiAccessible *accessible;
426   const char *name, *description;
427   dbus_uint32_t role;
428   gboolean children_cached = FALSE;
429   dbus_int32_t count, index;
430
431   dbus_message_iter_recurse (iter, &iter_struct);
432
433   /* get accessible */
434   get_reference_from_iter (&iter_struct, &app_name, &path);
435   accessible = ref_accessible (app_name, path);
436   if (!accessible)
437     return;
438
439   /* Get application: TODO */
440   dbus_message_iter_next (&iter_struct);
441
442   /* get parent */
443   get_reference_from_iter (&iter_struct, &app_name, &path);
444   if (accessible->accessible_parent)
445     g_object_unref (accessible->accessible_parent);
446   accessible->accessible_parent = ref_accessible (app_name, path);
447
448   if (dbus_message_iter_get_arg_type (&iter_struct) == 'i')
449   {
450     /* Get index in parent */
451     dbus_message_iter_get_basic (&iter_struct, &index);
452     if (index >= 0 && accessible->accessible_parent)
453     {
454       if (index >= accessible->accessible_parent->children->len)
455         g_ptr_array_set_size (accessible->accessible_parent->children, index + 1);
456       g_ptr_array_index (accessible->accessible_parent->children, index) = g_object_ref (accessible);
457     }
458
459     /* get child count */
460     dbus_message_iter_next (&iter_struct);
461     dbus_message_iter_get_basic (&iter_struct, &count);
462     if (count >= 0)
463     {
464       g_ptr_array_set_size (accessible->children, count);
465       children_cached = TRUE;
466     }
467   }
468   else if (dbus_message_iter_get_arg_type (&iter_struct) == 'a')
469   {
470     /* It's the old API with a list of children */
471     /* TODO: Perhaps remove this code eventually */
472     dbus_message_iter_recurse (&iter_struct, &iter_array);
473     while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
474     {
475       AtspiAccessible *child;
476       get_reference_from_iter (&iter_array, &app_name, &path);
477       child = ref_accessible (app_name, path);
478       g_ptr_array_remove (accessible->children, child);
479       g_ptr_array_add (accessible->children, child);
480     }
481     children_cached = TRUE;
482   }
483
484   /* interfaces */
485   dbus_message_iter_next (&iter_struct);
486   _atspi_dbus_set_interfaces (accessible, &iter_struct);
487   dbus_message_iter_next (&iter_struct);
488
489   /* name */
490   if (accessible->name)
491     g_free (accessible->name);
492   dbus_message_iter_get_basic (&iter_struct, &name);
493   accessible->name = g_strdup (name);
494   dbus_message_iter_next (&iter_struct);
495
496   /* role */
497   dbus_message_iter_get_basic (&iter_struct, &role);
498   accessible->role = role;
499   dbus_message_iter_next (&iter_struct);
500
501   /* description */
502   if (accessible->description)
503     g_free (accessible->description);
504   dbus_message_iter_get_basic (&iter_struct, &description);
505   accessible->description = g_strdup (description);
506   dbus_message_iter_next (&iter_struct);
507
508   _atspi_dbus_set_state (accessible, &iter_struct);
509   dbus_message_iter_next (&iter_struct);
510
511   _atspi_accessible_add_cache (accessible, ATSPI_CACHE_NAME | ATSPI_CACHE_ROLE |
512                                ATSPI_CACHE_PARENT | ATSPI_CACHE_DESCRIPTION);
513   if (!atspi_state_set_contains (accessible->states,
514                                        ATSPI_STATE_MANAGES_DESCENDANTS) &&
515                                        children_cached)
516     _atspi_accessible_add_cache (accessible, ATSPI_CACHE_CHILDREN);
517
518   /* This is a bit of a hack since the cache holds a ref, so we don't need
519    * the one provided for us anymore */
520   g_object_unref (accessible);
521 }
522
523 static void
524 handle_get_items (DBusPendingCall *pending, void *user_data)
525 {
526   DBusMessage *reply = dbus_pending_call_steal_reply (pending);
527   DBusMessageIter iter, iter_array;
528
529   if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
530   {
531     const char *sender = dbus_message_get_sender (reply);
532     const char *error = NULL;
533     if (!strcmp (dbus_message_get_error_name (reply),
534                  DBUS_ERROR_SERVICE_UNKNOWN))
535     {
536     }
537     else
538     {
539       dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &error,
540                              DBUS_TYPE_INVALID);
541       g_warning ("AT-SPI: Error in GetItems, sender=%s, error=%s", sender, error);
542     }
543     dbus_message_unref (reply);
544     dbus_pending_call_unref (pending);
545     return;
546   }
547
548   dbus_message_iter_init (reply, &iter);
549   dbus_message_iter_recurse (&iter, &iter_array);
550   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
551   {
552     add_accessible_from_iter (&iter_array);
553     dbus_message_iter_next (&iter_array);
554   }
555   dbus_message_unref (reply);
556   dbus_pending_call_unref (pending);
557 }
558
559 /* TODO: Do we stil need this function? */
560 static AtspiAccessible *
561 ref_accessible_desktop (AtspiApplication *app)
562 {
563   GError *error;
564   DBusMessage *message, *reply;
565   DBusMessageIter iter, iter_array;
566   gchar *bus_name_dup;
567
568   if (desktop)
569   {
570     g_object_ref (desktop);
571     return desktop;
572   }
573   desktop = _atspi_accessible_new (app, atspi_path_root);
574   if (!desktop)
575   {
576     return NULL;
577   }
578   g_hash_table_insert (app->hash, g_strdup (desktop->parent.path),
579                        g_object_ref (desktop));
580   app->root = g_object_ref (desktop);
581   desktop->name = g_strdup ("main");
582   message = dbus_message_new_method_call (atspi_bus_registry,
583         atspi_path_root,
584         atspi_interface_accessible,
585         "GetChildren");
586   if (!message)
587     return NULL;
588   error = NULL;
589   reply = _atspi_dbus_send_with_reply_and_block (message, &error);
590   if (!reply || strcmp (dbus_message_get_signature (reply), "a(so)") != 0)
591   {
592     if (error != NULL)
593     {
594       g_warning ("Couldn't get application list: %s", error->message);
595       g_clear_error (&error);
596     }
597     if (reply)
598       dbus_message_unref (reply);
599     return NULL;
600   }
601   dbus_message_iter_init (reply, &iter);
602   dbus_message_iter_recurse (&iter, &iter_array);
603   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
604   {
605     const char *app_name, *path;
606     get_reference_from_iter (&iter_array, &app_name, &path);
607     add_app_to_desktop (desktop, app_name);
608   }
609   dbus_message_unref (reply);
610
611   /* Record the alternate name as an alias for org.a11y.atspi.Registry */
612   bus_name_dup = g_strdup (dbus_message_get_sender (reply));
613   if (bus_name_dup)
614     g_hash_table_insert (app_hash, bus_name_dup, app);
615
616   return g_object_ref (desktop);
617 }
618
619 AtspiAccessible *
620 _atspi_ref_accessible (const char *app, const char *path)
621 {
622   AtspiApplication *a = get_application (app);
623   if (!a)
624     return NULL;
625   if ( APP_IS_REGISTRY(a))
626   {
627     if (!a->root)
628       g_object_unref (ref_accessible_desktop (a));      /* sets a->root */
629     return g_object_ref (a->root);
630   }
631   return ref_accessible (app, path);
632 }
633
634 AtspiAccessible *
635 _atspi_dbus_return_accessible_from_message (DBusMessage *message)
636 {
637   DBusMessageIter iter;
638   AtspiAccessible *retval = NULL;
639   const char *signature;
640
641   if (!message)
642     return NULL;
643
644   signature = dbus_message_get_signature (message);
645   if (!strcmp (signature, "(so)"))
646   {
647     dbus_message_iter_init (message, &iter);
648     retval =  _atspi_dbus_return_accessible_from_iter (&iter);
649   }
650   else
651   {
652     g_warning ("AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange signature %s", signature);
653   }
654   dbus_message_unref (message);
655   return retval;
656 }
657
658 AtspiAccessible *
659 _atspi_dbus_return_accessible_from_iter (DBusMessageIter *iter)
660 {
661   const char *app_name, *path;
662
663   get_reference_from_iter (iter, &app_name, &path);
664   return ref_accessible (app_name, path);
665 }
666
667 AtspiHyperlink *
668 _atspi_dbus_return_hyperlink_from_message (DBusMessage *message)
669 {
670   DBusMessageIter iter;
671   AtspiHyperlink *retval = NULL;
672   const char *signature;
673    
674   if (!message)
675     return NULL;
676
677   signature = dbus_message_get_signature (message);
678   if (!strcmp (signature, "(so)"))
679   {
680     dbus_message_iter_init (message, &iter);
681     retval =  _atspi_dbus_return_hyperlink_from_iter (&iter);
682   }
683   else
684   {
685     g_warning ("AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange signature %s", signature);
686   }
687   dbus_message_unref (message);
688   return retval;
689 }
690
691 AtspiHyperlink *
692 _atspi_dbus_return_hyperlink_from_iter (DBusMessageIter *iter)
693 {
694   const char *app_name, *path;
695
696   get_reference_from_iter (iter, &app_name, &path);
697   return ref_hyperlink (app_name, path);
698 }
699
700 const char *cache_signal_type = "((so)(so)(so)iiassusau)";
701 const char *old_cache_signal_type = "((so)(so)(so)a(so)assusau)";
702
703 static DBusHandlerResult
704 handle_add_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
705 {
706   DBusMessageIter iter;
707   const char *signature = dbus_message_get_signature (message);
708
709   if (strcmp (signature, cache_signal_type) != 0 &&
710       strcmp (signature, old_cache_signal_type) != 0)
711   {
712     g_warning ("AT-SPI: AddAccessible with unknown signature %s\n", signature);
713     return DBUS_HANDLER_RESULT_HANDLED;
714   }
715
716   dbus_message_iter_init (message, &iter);
717   add_accessible_from_iter (&iter);
718   return DBUS_HANDLER_RESULT_HANDLED;
719 }
720
721 typedef struct
722 {
723   DBusConnection *bus;
724   DBusMessage *message;
725   void *data;
726 } BusDataClosure;
727
728 static GSource *process_deferred_messages_source = NULL;
729
730 static void
731 process_deferred_message (BusDataClosure *closure)
732 {
733   int type = dbus_message_get_type (closure->message);
734   const char *interface = dbus_message_get_interface (closure->message);
735
736   if (type == DBUS_MESSAGE_TYPE_SIGNAL &&
737       !strncmp (interface, "org.a11y.atspi.Event.", 21))
738   {
739     _atspi_dbus_handle_event (closure->bus, closure->message, closure->data);
740   }
741   if (dbus_message_is_method_call (closure->message, atspi_interface_device_event_listener, "NotifyEvent"))
742   {
743     _atspi_dbus_handle_DeviceEvent (closure->bus,
744                                    closure->message, closure->data);
745   }
746   if (dbus_message_is_signal (closure->message, atspi_interface_cache, "AddAccessible"))
747   {
748     handle_add_accessible (closure->bus, closure->message, closure->data);
749   }
750   if (dbus_message_is_signal (closure->message, atspi_interface_cache, "RemoveAccessible"))
751   {
752     handle_remove_accessible (closure->bus, closure->message, closure->data);
753   }
754   if (dbus_message_is_signal (closure->message, "org.freedesktop.DBus", "NameOwnerChanged"))
755   {
756     handle_name_owner_changed (closure->bus, closure->message, closure->data);
757   }
758 }
759
760 static GQueue *deferred_messages = NULL;
761
762 static gboolean
763 process_deferred_messages (void)
764 {
765   static int in_process_deferred_messages = 0;
766   BusDataClosure *closure;
767
768   if (in_process_deferred_messages)
769     return TRUE;
770   in_process_deferred_messages = 1;
771   while ((closure = g_queue_pop_head (deferred_messages)))
772   {
773     process_deferred_message (closure);
774     dbus_message_unref (closure->message);
775     dbus_connection_unref (closure->bus);
776     g_free (closure);
777   }
778   in_process_deferred_messages = 0;
779   return FALSE;
780 }
781
782 static gboolean
783 process_deferred_messages_callback (gpointer data)
784 {
785   if (process_deferred_messages ())
786     return G_SOURCE_CONTINUE;
787
788   process_deferred_messages_source = NULL;
789   return G_SOURCE_REMOVE;
790 }
791
792 static DBusHandlerResult
793 defer_message (DBusConnection *connection, DBusMessage *message, void *user_data)
794 {
795   BusDataClosure *closure = g_new (BusDataClosure, 1);
796
797   closure->bus = dbus_connection_ref (bus);
798   closure->message = dbus_message_ref (message);
799   closure->data = user_data;
800
801   g_queue_push_tail (deferred_messages, closure);
802
803   if (process_deferred_messages_source == NULL)
804   {
805     process_deferred_messages_source = g_idle_source_new ();
806     g_source_set_callback (process_deferred_messages_source,
807                            process_deferred_messages_callback, NULL, NULL);
808     g_source_attach (process_deferred_messages_source, atspi_main_context);
809     g_source_unref (process_deferred_messages_source);
810   }
811
812   return DBUS_HANDLER_RESULT_HANDLED;
813 }
814
815 static DBusHandlerResult
816 atspi_dbus_filter (DBusConnection *bus, DBusMessage *message, void *data)
817 {
818   int type = dbus_message_get_type (message);
819   const char *interface = dbus_message_get_interface (message);
820
821   if (type == DBUS_MESSAGE_TYPE_SIGNAL &&
822       !strncmp (interface, "org.a11y.atspi.Event.", 21))
823   {
824     return defer_message (bus, message, data);
825   }
826   if (dbus_message_is_method_call (message, atspi_interface_device_event_listener, "NotifyEvent"))
827   {
828     return defer_message (bus, message, data);
829   }
830   if (dbus_message_is_signal (message, atspi_interface_cache, "AddAccessible"))
831   {
832     return defer_message (bus, message, data);
833   }
834   if (dbus_message_is_signal (message, atspi_interface_cache, "RemoveAccessible"))
835   {
836     return defer_message (bus, message, data);
837   }
838   if (dbus_message_is_signal (message, "org.freedesktop.DBus", "NameOwnerChanged"))
839   {
840     defer_message (bus, message, data);
841     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
842   }
843   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
844 }
845
846 /*
847  * Returns a 'canonicalized' value for DISPLAY,
848  * with the screen number stripped off if present.
849  *
850  * TODO: Avoid having duplicate functions for this here and in at-spi2-atk
851  */
852 static gchar *
853 spi_display_name (void)
854 {
855   char *canonical_display_name = NULL;
856   const gchar *display_env = g_getenv ("AT_SPI_DISPLAY");
857
858   if (!display_env)
859     {
860       display_env = g_getenv ("DISPLAY");
861       if (!display_env || !display_env[0])
862         return NULL;
863       else
864         {
865           gchar *display_p, *screen_p;
866           canonical_display_name = g_strdup (display_env);
867           display_p = g_utf8_strrchr (canonical_display_name, -1, ':');
868           screen_p = g_utf8_strrchr (canonical_display_name, -1, '.');
869           if (screen_p && display_p && (screen_p > display_p))
870             {
871               *screen_p = '\0';
872             }
873         }
874     }
875   else
876     {
877       canonical_display_name = g_strdup (display_env);
878     }
879
880   return canonical_display_name;
881 }
882
883 /**
884  * atspi_init:
885  *
886  * Connects to the accessibility registry and initializes the SPI.
887  *
888  * Returns: 0 on success, 1 if already initialized, or an integer error code.  
889  **/
890 int
891 atspi_init (void)
892 {
893   char *match;
894   const gchar *no_cache;
895
896   if (atspi_inited)
897     {
898       return 1;
899     }
900
901   atspi_inited = TRUE;
902
903   _atspi_get_live_refs();
904
905   bus = atspi_get_a11y_bus ();
906   if (!bus)
907     return 2;
908   dbus_bus_register (bus, NULL);
909   atspi_dbus_connection_setup_with_g_main(bus, g_main_context_default());
910   dbus_connection_add_filter (bus, atspi_dbus_filter, NULL, NULL);
911   match = g_strdup_printf ("type='signal',interface='%s',member='AddAccessible'", atspi_interface_cache);
912   dbus_bus_add_match (bus, match, NULL);
913   g_free (match);
914   match = g_strdup_printf ("type='signal',interface='%s',member='RemoveAccessible'", atspi_interface_cache);
915   dbus_bus_add_match (bus, match, NULL);
916   g_free (match);
917   match = g_strdup_printf ("type='signal',interface='%s',member='ChildrenChanged'", atspi_interface_event_object);
918   dbus_bus_add_match (bus, match, NULL);
919   g_free (match);
920   match = g_strdup_printf ("type='signal',interface='%s',member='PropertyChange'", atspi_interface_event_object);
921   dbus_bus_add_match (bus, match, NULL);
922   g_free (match);
923   match = g_strdup_printf ("type='signal',interface='%s',member='StateChanged'", atspi_interface_event_object);
924   dbus_bus_add_match (bus, match, NULL);
925   g_free (match);
926
927   dbus_bus_add_match (bus,
928                       "type='signal', interface='org.freedesktop.DBus', member='NameOwnerChanged'",
929                       NULL);
930
931   no_cache = g_getenv ("ATSPI_NO_CACHE");
932   if (no_cache && g_strcmp0 (no_cache, "0") != 0)
933     atspi_no_cache = TRUE;
934
935   deferred_messages = g_queue_new ();
936
937   return 0;
938 }
939
940 /**
941  * atspi_is_initialized:
942  *
943  * Indicates whether AT-SPI has been initialized.
944  *
945  * Returns: %True if initialized; %False otherwise.
946  */
947 gboolean
948 atspi_is_initialized ()
949 {
950   return atspi_inited;
951 }
952
953 /**
954  * atspi_event_main:
955  *
956  * Starts/enters the main event loop for the AT-SPI services.
957  *
958  * NOTE: This method does not return control; it is exited via a call to
959  * #atspi_event_quit from within an event handler.
960  *
961  **/
962 void
963 atspi_event_main (void)
964 {
965   atspi_main_loop = g_main_loop_new (NULL, FALSE);
966   g_main_loop_run (atspi_main_loop);
967   atspi_main_loop = NULL;
968 }
969
970 /**
971  * atspi_event_quit:
972  *
973  * Quits the last main event loop for the AT-SPI services,
974  * See: #atspi_event_main
975  **/
976 void
977 atspi_event_quit (void)
978 {
979   g_main_loop_quit (atspi_main_loop);
980 }
981
982 /**
983  * atspi_exit:
984  *
985  * Disconnects from #AtspiRegistry instances and releases 
986  * any floating resources. Call only once at exit.
987  *
988  * Returns: 0 if there were no leaks, otherwise other integer values.
989  **/
990 int
991 atspi_exit (void)
992 {
993   int leaked;
994
995   if (!atspi_inited)
996     {
997       return 0;
998     }
999
1000   atspi_inited = FALSE;
1001
1002   if (live_refs)
1003     {
1004       leaked = g_hash_table_size (live_refs);
1005     }
1006   else
1007     {
1008       leaked = 0;
1009     }
1010
1011   cleanup ();
1012
1013   return leaked;
1014 }
1015
1016 static GSList *hung_processes;
1017
1018 static void
1019 remove_hung_process (DBusPendingCall *pending, void *data)
1020 {
1021
1022   hung_processes = g_slist_remove (hung_processes, data);
1023   g_free (data);
1024   dbus_pending_call_unref (pending);
1025 }
1026
1027 static void
1028 check_for_hang (DBusMessage *message, DBusError *error, DBusConnection *bus, const char *bus_name)
1029 {
1030   if (!message && error->name &&
1031       !strcmp (error->name, "org.freedesktop.DBus.Error.NoReply"))
1032   {
1033     GSList *l;
1034     DBusMessage *message;
1035     gchar *bus_name_dup;
1036     DBusPendingCall *pending = NULL;
1037     for (l = hung_processes; l; l = l->next)
1038       if (!strcmp (l->data, bus_name))
1039         return;
1040     message = dbus_message_new_method_call (bus_name, "/",
1041                                             "org.freedesktop.DBus.Peer",
1042                                             "Ping");
1043     if (!message)
1044       return;
1045     dbus_connection_send_with_reply (bus, message, &pending, -1);
1046     dbus_message_unref (message);
1047     if (!pending)
1048       return;
1049     bus_name_dup = g_strdup (bus_name);
1050     hung_processes = g_slist_append (hung_processes, bus_name_dup);
1051     dbus_pending_call_set_notify (pending, remove_hung_process, bus_name_dup, NULL);
1052   }
1053 }
1054
1055 static gboolean
1056 connection_is_hung (const char *bus_name)
1057 {
1058   GSList *l;
1059
1060   for (l = hung_processes; l; l = l->next)
1061     if (!strcmp (l->data, bus_name))
1062       return TRUE;
1063   return FALSE;
1064 }
1065
1066 static gboolean
1067 check_app (AtspiApplication *app, GError **error)
1068 {
1069   if (!app || !app->bus)
1070   {
1071     g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_APPLICATION_GONE,
1072                           _("The application no longer exists"));
1073     return FALSE;
1074   }
1075
1076   if (atspi_main_loop && connection_is_hung (app->bus_name))
1077   {
1078       g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC,
1079                            "The process appears to be hung.");
1080     return FALSE;
1081   }
1082
1083   return TRUE;
1084 }
1085
1086 static void
1087 set_timeout (AtspiApplication *app)
1088 {
1089   struct timeval tv;
1090   int diff;
1091
1092   if (app && app_startup_time > 0)
1093   {
1094     gettimeofday (&tv, NULL);
1095     diff = (tv.tv_sec - app->time_added.tv_sec) * 1000 + (tv.tv_usec - app->time_added.tv_usec) / 1000;
1096     dbind_set_timeout (MAX(method_call_timeout, app_startup_time - diff));
1097   }
1098   else
1099     dbind_set_timeout (method_call_timeout);
1100 }
1101
1102 dbus_bool_t
1103 _atspi_dbus_call (gpointer obj, const char *interface, const char *method, GError **error, const char *type, ...)
1104 {
1105   va_list args;
1106   dbus_bool_t retval;
1107   DBusError err;
1108   AtspiObject *aobj = ATSPI_OBJECT (obj);
1109
1110   if (!check_app (aobj->app, error))
1111     return FALSE;
1112
1113   if (!allow_sync)
1114   {
1115     _atspi_set_error_no_sync (error);
1116     return FALSE;
1117   }
1118
1119   va_start (args, type);
1120   dbus_error_init (&err);
1121   set_timeout (aobj->app);
1122   retval = dbind_method_call_reentrant_va (aobj->app->bus, aobj->app->bus_name,
1123                                            aobj->path, interface, method, &err,
1124                                            type, args);
1125   va_end (args);
1126   check_for_hang (NULL, &err, aobj->app->bus, aobj->app->bus_name);
1127   process_deferred_messages ();
1128   if (dbus_error_is_set (&err))
1129   {
1130     g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "%s", err.message);
1131     dbus_error_free (&err);
1132   }
1133   return retval;
1134 }
1135
1136 DBusMessage *
1137 _atspi_dbus_call_partial (gpointer obj,
1138                           const char *interface,
1139                           const char *method,
1140                           GError **error,
1141                           const char *type, ...)
1142 {
1143   va_list args;
1144
1145   va_start (args, type);
1146   return _atspi_dbus_call_partial_va (obj, interface, method, error, type, args);
1147 }
1148
1149
1150 DBusMessage *
1151 _atspi_dbus_call_partial_va (gpointer obj,
1152                           const char *interface,
1153                           const char *method,
1154                           GError **error,
1155                           const char *type,
1156                           va_list args)
1157 {
1158   AtspiObject *aobj = ATSPI_OBJECT (obj);
1159   DBusError err;
1160     DBusMessage *msg = NULL, *reply = NULL;
1161     DBusMessageIter iter;
1162     const char *p;
1163
1164   dbus_error_init (&err);
1165
1166   if (!check_app (aobj->app, error))
1167     goto out;
1168
1169   msg = dbus_message_new_method_call (aobj->app->bus_name, aobj->path, interface, method);
1170   if (!msg)
1171     goto out;
1172
1173   p = type;
1174   dbus_message_iter_init_append (msg, &iter);
1175   dbind_any_marshal_va (&iter, &p, args);
1176
1177   set_timeout (aobj->app);
1178   reply = dbind_send_and_allow_reentry (aobj->app->bus, msg, &err);
1179   check_for_hang (reply, &err, aobj->app->bus, aobj->app->bus_name);
1180 out:
1181   va_end (args);
1182   if (msg)
1183     dbus_message_unref (msg);
1184   process_deferred_messages ();
1185   if (dbus_error_is_set (&err))
1186   {
1187     /* TODO: Set gerror */
1188     dbus_error_free (&err);
1189   }
1190
1191   if (reply && dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
1192   {
1193     const char *err_str = NULL;
1194     dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &err_str, DBUS_TYPE_INVALID);
1195     if (err_str)
1196       g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err_str);
1197     dbus_message_unref (reply);
1198     return NULL;
1199   }
1200
1201   return reply;
1202 }
1203
1204 dbus_bool_t
1205 _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, GError **error, const char *type, void *data)
1206 {
1207   DBusMessage *message, *reply;
1208   DBusMessageIter iter, iter_variant;
1209   DBusError err;
1210   dbus_bool_t retval = FALSE;
1211   AtspiObject *aobj = ATSPI_OBJECT (obj);
1212   char expected_type = (type [0] == '(' ? 'r' : type [0]);
1213
1214   if (!aobj)
1215     return FALSE;
1216
1217   if (!check_app (aobj->app, error))
1218     return FALSE;
1219
1220   if (!allow_sync)
1221   {
1222     _atspi_set_error_no_sync (error);
1223     return FALSE;
1224   }
1225
1226   message = dbus_message_new_method_call (aobj->app->bus_name,
1227                                           aobj->path,
1228                                           "org.freedesktop.DBus.Properties",
1229                                           "Get");
1230   if (!message)
1231   {
1232     // TODO: throw exception
1233     return FALSE;
1234   }
1235   dbus_message_append_args (message, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
1236   dbus_error_init (&err);
1237   set_timeout (aobj->app);
1238   reply = dbind_send_and_allow_reentry (aobj->app->bus, message, &err);
1239   check_for_hang (reply, &err, aobj->app->bus, aobj->app->bus_name);
1240   dbus_message_unref (message);
1241   process_deferred_messages ();
1242   if (!reply)
1243   {
1244     // TODO: throw exception
1245     goto done;
1246   }
1247
1248   if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
1249   {
1250     const char *err_str = NULL;
1251     dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &err_str, DBUS_TYPE_INVALID);
1252     if (err_str)
1253       g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err_str);
1254     goto done;
1255   }
1256
1257   dbus_message_iter_init (reply, &iter);
1258   if (dbus_message_iter_get_arg_type (&iter) != 'v')
1259   {
1260     g_warning ("AT-SPI: expected a variant when fetching %s from interface %s; got %s\n", name, interface, dbus_message_get_signature (reply));
1261     goto done;
1262   }
1263   dbus_message_iter_recurse (&iter, &iter_variant);
1264   if (dbus_message_iter_get_arg_type (&iter_variant) != expected_type)
1265   {
1266     g_warning ("atspi_dbus_get_property: Wrong type: expected %s, got %c\n", type, dbus_message_iter_get_arg_type (&iter_variant));
1267     goto done;
1268   }
1269   if (!strcmp (type, "(so)"))
1270   {
1271     *((AtspiAccessible **)data) = _atspi_dbus_return_accessible_from_iter (&iter_variant);
1272   }
1273   else
1274   {
1275     dbus_message_iter_get_basic (&iter_variant, data);
1276     if (type [0] == 's')
1277       *(char **)data = g_strdup (*(char **)data);
1278   }
1279   retval = TRUE;
1280 done:
1281   dbus_error_free (&err);
1282   if (reply)
1283     dbus_message_unref (reply);
1284   return retval;
1285 }
1286
1287 DBusMessage *
1288 _atspi_dbus_send_with_reply_and_block (DBusMessage *message, GError **error)
1289 {
1290   DBusMessage *reply;
1291   DBusError err;
1292   AtspiApplication *app;
1293   DBusConnection *bus;
1294
1295   app = get_application (dbus_message_get_destination (message));
1296
1297   if (app && !app->bus)
1298     return NULL;        /* will fail anyway; app has been disposed */
1299
1300   bus = (app ? app->bus : _atspi_bus());
1301   dbus_error_init (&err);
1302   set_timeout (app);
1303   reply = dbind_send_and_allow_reentry (bus, message, &err);
1304   process_deferred_messages ();
1305   dbus_message_unref (message);
1306   if (dbus_error_is_set (&err))
1307   {
1308     if (error)
1309       g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err.message);
1310     dbus_error_free (&err);
1311   }
1312   return reply;
1313 }
1314
1315 GHashTable *
1316 _atspi_dbus_return_hash_from_message (DBusMessage *message)
1317 {
1318   DBusMessageIter iter;
1319   GHashTable *ret;
1320
1321   if (!message)
1322     return NULL;
1323
1324   _ATSPI_DBUS_CHECK_SIG (message, "a{ss}", NULL, NULL);
1325
1326   dbus_message_iter_init (message, &iter);
1327   ret = _atspi_dbus_hash_from_iter (&iter);
1328   dbus_message_unref (message);
1329   return ret;
1330 }
1331
1332 GHashTable *
1333 _atspi_dbus_hash_from_iter (DBusMessageIter *iter)
1334 {
1335   GHashTable *hash = g_hash_table_new_full (g_str_hash, g_str_equal,
1336                                             (GDestroyNotify) g_free,
1337                                             (GDestroyNotify) g_free);
1338   DBusMessageIter iter_array, iter_dict;
1339
1340   dbus_message_iter_recurse (iter, &iter_array);
1341   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
1342   {
1343     const char *name, *value;
1344     dbus_message_iter_recurse (&iter_array, &iter_dict);
1345     dbus_message_iter_get_basic (&iter_dict, &name);
1346     dbus_message_iter_next (&iter_dict);
1347     dbus_message_iter_get_basic (&iter_dict, &value);
1348     g_hash_table_insert (hash, g_strdup (name), g_strdup (value));
1349     dbus_message_iter_next (&iter_array);
1350   }
1351   return hash;
1352 }
1353
1354 GArray *
1355 _atspi_dbus_return_attribute_array_from_message (DBusMessage *message)
1356 {
1357   DBusMessageIter iter;
1358   GArray *ret;
1359
1360   if (!message)
1361     return NULL;
1362
1363   _ATSPI_DBUS_CHECK_SIG (message, "a{ss}", NULL, NULL);
1364
1365   dbus_message_iter_init (message, &iter);
1366
1367   ret = _atspi_dbus_attribute_array_from_iter (&iter);
1368   dbus_message_unref (message);
1369   return ret;
1370 }
1371
1372 GArray *
1373 _atspi_dbus_attribute_array_from_iter (DBusMessageIter *iter)
1374 {
1375   DBusMessageIter iter_array, iter_dict;
1376   GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
1377
1378   dbus_message_iter_recurse (iter, &iter_array);
1379   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
1380   {
1381     const char *name, *value;
1382     gchar *str;
1383     dbus_message_iter_recurse (&iter_array, &iter_dict);
1384     dbus_message_iter_get_basic (&iter_dict, &name);
1385     dbus_message_iter_next (&iter_dict);
1386     dbus_message_iter_get_basic (&iter_dict, &value);
1387     str = g_strdup_printf ("%s:%s", name, value);
1388     array = g_array_append_val (array, str);
1389     dbus_message_iter_next (&iter_array);;
1390   }
1391   return array;
1392 }
1393
1394 void
1395 _atspi_dbus_set_interfaces (AtspiAccessible *accessible, DBusMessageIter *iter)
1396 {
1397   DBusMessageIter iter_array;
1398   char *iter_sig = dbus_message_iter_get_signature (iter);
1399
1400   accessible->interfaces = 0;
1401   if (strcmp (iter_sig, "as") != 0)
1402   {
1403     g_warning ("_atspi_dbus_set_interfaces: Passed iterator with invalid signature %s", dbus_message_iter_get_signature (iter));
1404     dbus_free (iter_sig);
1405     return;
1406   }
1407   dbus_free (iter_sig);
1408   dbus_message_iter_recurse (iter, &iter_array);
1409   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
1410   {
1411     const char *iface;
1412     gint n;
1413     dbus_message_iter_get_basic (&iter_array, &iface);
1414     if (!strcmp (iface, "org.freedesktop.DBus.Introspectable")) continue;
1415     n = _atspi_get_iface_num (iface);
1416     if (n == -1)
1417     {
1418       g_warning ("AT-SPI: Unknown interface %s", iface);
1419     }
1420     else
1421       accessible->interfaces |= (1 << n);
1422     dbus_message_iter_next (&iter_array);
1423   }
1424   _atspi_accessible_add_cache (accessible, ATSPI_CACHE_INTERFACES);
1425 }
1426
1427 void
1428 _atspi_dbus_set_state (AtspiAccessible *accessible, DBusMessageIter *iter)
1429 {
1430   DBusMessageIter iter_array;
1431   gint count;
1432   dbus_uint32_t *states;
1433
1434   dbus_message_iter_recurse (iter, &iter_array);
1435   dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
1436   if (count != 2)
1437   {
1438     g_warning ("AT-SPI: expected 2 values in states array; got %d\n", count);
1439     if (!accessible->states)
1440       accessible->states = _atspi_state_set_new_internal (accessible, 0);
1441   }
1442   else
1443   {
1444     guint64 val = ((guint64)states [1]) << 32;
1445     val += states [0];
1446     if (!accessible->states)
1447       accessible->states = _atspi_state_set_new_internal (accessible, val);
1448     else
1449       accessible->states->states = val;
1450   }
1451   _atspi_accessible_add_cache (accessible, ATSPI_CACHE_STATES);
1452 }
1453
1454 GQuark
1455 _atspi_error_quark (void)
1456 {
1457   return g_quark_from_static_string ("atspi_error");
1458 }
1459
1460 /*
1461  * Gets the IOR from the XDisplay.
1462  */
1463 #ifdef HAVE_X11
1464 static char *
1465 get_accessibility_bus_address_x11 (void)
1466 {
1467   Atom AT_SPI_BUS;
1468   Atom actual_type;
1469   Display *bridge_display = NULL;
1470   int actual_format;
1471   char *data;
1472   unsigned char *data_x11 = NULL;
1473   unsigned long nitems;
1474   unsigned long leftover;
1475   char *display_name;
1476
1477   display_name = spi_display_name ();
1478   if (!display_name)
1479     return NULL;
1480
1481   bridge_display = XOpenDisplay (display_name);
1482   g_free (display_name);
1483
1484   if (!bridge_display)
1485     {
1486       g_warning ("Could not open X display");
1487       return NULL;
1488     }
1489       
1490   AT_SPI_BUS = XInternAtom (bridge_display, "AT_SPI_BUS", False);
1491   XGetWindowProperty (bridge_display,
1492                       XDefaultRootWindow (bridge_display),
1493                       AT_SPI_BUS, 0L,
1494                       (long) BUFSIZ, False,
1495                       (Atom) 31, &actual_type, &actual_format,
1496                       &nitems, &leftover, &data_x11);
1497   XCloseDisplay (bridge_display);
1498
1499   data = g_strdup ((gchar *)data_x11);
1500   XFree (data_x11);
1501   return data;
1502 }
1503 #endif
1504
1505 static char *
1506 get_accessibility_bus_address_dbus (void)
1507 {
1508   DBusConnection *session_bus = NULL;
1509   DBusMessage *message;
1510   DBusMessage *reply;
1511   DBusError error;
1512   char *address = NULL;
1513
1514   session_bus = dbus_bus_get (DBUS_BUS_SESSION, NULL);
1515   if (!session_bus)
1516     return NULL;
1517
1518   message = dbus_message_new_method_call ("org.a11y.Bus",
1519                                           "/org/a11y/bus",
1520                                           "org.a11y.Bus",
1521                                           "GetAddress");
1522
1523   dbus_error_init (&error);
1524   reply = dbus_connection_send_with_reply_and_block (session_bus,
1525                                                      message,
1526                                                      -1,
1527                                                      &error);
1528   dbus_message_unref (message);
1529
1530   if (!reply)
1531   {
1532     g_warning ("Error retrieving accessibility bus address: %s: %s",
1533                error.name, error.message);
1534     dbus_error_free (&error);
1535     return NULL;
1536   }
1537   
1538   {
1539     const char *tmp_address;
1540     if (!dbus_message_get_args (reply,
1541                                 NULL,
1542                                 DBUS_TYPE_STRING,
1543                                 &tmp_address,
1544                                 DBUS_TYPE_INVALID))
1545       {
1546         dbus_message_unref (reply);
1547         return NULL;
1548       }
1549     address = g_strdup (tmp_address);
1550     dbus_message_unref (reply);
1551   }
1552   
1553   return address;
1554 }
1555
1556 static DBusConnection *a11y_bus;
1557 static dbus_int32_t a11y_dbus_slot = -1;
1558
1559 static void
1560 a11y_bus_free (void *data)
1561 {
1562   if (data == a11y_bus)
1563     {
1564       a11y_bus = NULL;
1565       dbus_connection_free_data_slot (&a11y_dbus_slot);
1566     }
1567 }
1568
1569 /**
1570  * atspi_get_a11y_bus: (skip)
1571  */
1572 DBusConnection *
1573 atspi_get_a11y_bus (void)
1574 {
1575   DBusError error;
1576   char *address = NULL;
1577
1578   if (a11y_bus && dbus_connection_get_is_connected (a11y_bus))
1579     return a11y_bus;
1580
1581   if (a11y_dbus_slot == -1)
1582     if (!dbus_connection_allocate_data_slot (&a11y_dbus_slot))
1583       g_warning ("at-spi: Unable to allocate D-Bus slot");
1584
1585 #ifdef HAVE_X11
1586   address = get_accessibility_bus_address_x11 ();
1587 #endif
1588   if (!address)
1589     address = get_accessibility_bus_address_dbus ();
1590   if (!address)
1591     return NULL;
1592
1593   dbus_error_init (&error);
1594   a11y_bus = dbus_connection_open_private (address, &error);
1595   g_free (address);
1596
1597   if (!a11y_bus)
1598     {
1599       if (!g_getenv("SSH_CONNECTION"))
1600         g_warning ("Couldn't connect to accessibility bus: %s", error.message);
1601       dbus_error_free (&error);
1602       return NULL;
1603     }
1604   else
1605     {
1606       if (!dbus_bus_register (a11y_bus, &error))
1607         {
1608           g_warning ("Couldn't register with accessibility bus: %s", error.message);
1609           dbus_error_free (&error);
1610           dbus_connection_close (a11y_bus);
1611           dbus_connection_unref (a11y_bus);
1612           a11y_bus = NULL;
1613           return NULL;
1614         }
1615     }
1616   
1617   /* Simulate a weak ref on the bus */
1618   dbus_connection_set_data (a11y_bus, a11y_dbus_slot, a11y_bus, a11y_bus_free);
1619
1620   return a11y_bus;
1621 }
1622
1623 /**
1624  * atspi_set_timeout:
1625  * @val: The timeout value, in milliseconds, or -1 to disable the timeout.
1626  * @startup_time: The amount of time, in milliseconds, to allow to pass
1627  * before enforcing timeouts on an application. Can be used to prevent
1628  * timeout exceptions if an application is likely to block for an extended
1629  * period of time on initialization. -1 can be passed to disable this
1630  * behavior.
1631  *
1632  * Set the timeout used for method calls. If this is not set explicitly,
1633  * a default of 0.8 ms is used.
1634  * Note that at-spi2-registryd currently uses a timeout of 3 seconds when
1635  * sending a keyboard event notification. This means that, if an AT makes
1636  * a call in response to the keyboard notification and the application
1637  * being called does not respond before the timeout is reached,
1638  * at-spi2-registryd will time out on the keyboard event notification and
1639  * pass the key onto the application (ie, reply to indicate that the key
1640  * was not consumed), so this may make it undesirable to set a timeout
1641  * larger than 3 seconds.
1642  *
1643  * By default, the normal timeout is set to 800 ms, and the application startup
1644  * timeout is set to 15 seconds.
1645  */
1646 void
1647 atspi_set_timeout (gint val, gint startup_time)
1648 {
1649   method_call_timeout = val;
1650   app_startup_time = startup_time;
1651 }
1652
1653 /**
1654  * atspi_set_main_context:
1655  * @cnx: The #GMainContext to use.
1656  *
1657  * Sets the main loop context that AT-SPI should assume is in use when
1658  * setting an idle callback.
1659  * This function should be called by application-side implementors (ie,
1660  * at-spi2-atk) when it is desirable to re-enter the main loop.
1661  */
1662 void
1663 atspi_set_main_context (GMainContext *cnx)
1664 {
1665   if (atspi_main_context == cnx)
1666     return;
1667   if (process_deferred_messages_source != NULL)
1668   {
1669     g_source_destroy (process_deferred_messages_source);
1670     process_deferred_messages_source = g_idle_source_new ();
1671     g_source_set_callback (process_deferred_messages_source,
1672                            process_deferred_messages_callback, NULL, NULL);
1673     g_source_attach (process_deferred_messages_source, cnx);
1674     g_source_unref (process_deferred_messages_source);
1675   }
1676   atspi_main_context = cnx;
1677   atspi_dbus_connection_setup_with_g_main (atspi_get_a11y_bus (), cnx);
1678 }
1679
1680 #ifdef DEBUG_REF_COUNTS
1681 static void
1682 print_disposed (gpointer key, gpointer value, gpointer data)
1683 {
1684   AtspiAccessible *accessible = key;
1685   if (accessible->parent.app)
1686     return;
1687   g_print ("disposed: %s %d\n", accessible->name, accessible->role);
1688 }
1689
1690 void
1691 debug_disposed ()
1692 {
1693   g_hash_table_foreach (live_refs, print_disposed, NULL);
1694 }
1695 #endif
1696
1697 gchar *
1698 _atspi_name_compat (gchar *name)
1699 {
1700   gchar *p = name;
1701
1702   while (*p)
1703   {
1704     if (*p == '-')
1705       *p = ' ';
1706     p++;
1707   }
1708   return name;
1709 }
1710
1711 /**
1712  * atspi_role_get_name:
1713  * @role: an #AtspiRole object to query.
1714  *
1715  * Gets a localizable string that indicates the name of an #AtspiRole.
1716  * <em>DEPRECATED.</em>
1717  *
1718  * Returns: a localizable string name for an #AtspiRole enumerated type.
1719  **/
1720 gchar *
1721 atspi_role_get_name (AtspiRole role)
1722 {
1723   gchar *retval = NULL;
1724   GTypeClass *type_class;
1725   GEnumValue *value;
1726
1727   type_class = g_type_class_ref (ATSPI_TYPE_ROLE);
1728   g_return_val_if_fail (G_IS_ENUM_CLASS (type_class), NULL);
1729
1730   value = g_enum_get_value (G_ENUM_CLASS (type_class), role);
1731
1732   if (value)
1733     {
1734       retval = g_strdup (value->value_nick);
1735     }
1736
1737   g_type_class_unref (type_class);
1738
1739   if (retval)
1740     return _atspi_name_compat (retval);
1741
1742   return NULL;
1743 }
1744
1745 GHashTable *
1746 _atspi_dbus_update_cache_from_dict (AtspiAccessible *accessible, DBusMessageIter *iter)
1747 {
1748   GHashTable *cache = _atspi_accessible_ref_cache (accessible);
1749   DBusMessageIter iter_dict, iter_dict_entry, iter_struct, iter_variant;
1750
1751   dbus_message_iter_recurse (iter, &iter_dict);
1752   while (dbus_message_iter_get_arg_type (&iter_dict) != DBUS_TYPE_INVALID)
1753   {
1754     const char *key;
1755     GValue *val = NULL;
1756     dbus_message_iter_recurse (&iter_dict, &iter_dict_entry);
1757     dbus_message_iter_get_basic (&iter_dict_entry, &key);
1758     dbus_message_iter_next (&iter_dict_entry);
1759     dbus_message_iter_recurse (&iter_dict_entry, &iter_variant);
1760     if (!strcmp (key, "interfaces"))
1761     {
1762       _atspi_dbus_set_interfaces (accessible, &iter_variant);
1763     }
1764     else if (!strcmp (key, "Attributes"))
1765     {
1766       char *iter_sig = dbus_message_iter_get_signature (&iter_variant);
1767       val = g_new0 (GValue, 1);;
1768       g_value_init (val, G_TYPE_HASH_TABLE);
1769       if (strcmp (iter_sig, "a{ss}") != 0)
1770       {
1771         dbus_free (iter_sig);
1772         break;
1773       }
1774       dbus_free (iter_sig);
1775       g_value_take_boxed (val, _atspi_dbus_hash_from_iter (&iter_variant));
1776     }
1777     else if (!strcmp (key, "Component.ScreenExtents"))
1778     {
1779       dbus_int32_t d_int;
1780       AtspiRect extents;
1781       char *iter_sig = dbus_message_iter_get_signature (&iter_variant);
1782       val = g_new0 (GValue, 1);;
1783       g_value_init (val, ATSPI_TYPE_RECT);
1784       if (strcmp (iter_sig, "(iiii)") != 0)
1785       {
1786         dbus_free (iter_sig);
1787         break;
1788       }
1789       dbus_free (iter_sig);
1790       dbus_message_iter_recurse (&iter_variant, &iter_struct);
1791       dbus_message_iter_get_basic (&iter_struct, &d_int);
1792       extents.x = d_int;
1793       dbus_message_iter_next (&iter_struct);
1794       dbus_message_iter_get_basic (&iter_struct, &d_int);
1795       extents.y = d_int;
1796       dbus_message_iter_next (&iter_struct);
1797       dbus_message_iter_get_basic (&iter_struct, &d_int);
1798       extents.width = d_int;
1799       dbus_message_iter_next (&iter_struct);
1800       dbus_message_iter_get_basic (&iter_struct, &d_int);
1801       extents.height = d_int;
1802       g_value_set_boxed (val, &extents);
1803     }
1804     if (val)
1805       g_hash_table_insert (cache, g_strdup (key), val); 
1806     dbus_message_iter_next (&iter_dict);
1807   }
1808
1809   return cache;
1810 }
1811
1812 gboolean
1813 _atspi_get_allow_sync ()
1814 {
1815   return allow_sync;
1816 }
1817
1818 gboolean
1819 _atspi_set_allow_sync (gboolean val)
1820 {
1821   gboolean ret = allow_sync;
1822
1823   allow_sync = val;
1824   return ret;
1825 }
1826
1827 void
1828 _atspi_set_error_no_sync (GError **error)
1829 {
1830   g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_SYNC_NOT_ALLOWED,
1831                         _("Attempted synchronous call where prohibited"));
1832 }