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