2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2001, 2002 Sun Microsystems Inc.,
6 * Copyright 2001, 2002 Ximian, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
26 * Basic SPI initialization and event loop function prototypes
30 #include "atspi-private.h"
34 static DBusConnection *bus = NULL;
35 static GHashTable *apps = NULL;
36 static GHashTable *live_refs = NULL;
37 static GQueue *exception_handlers = NULL;
38 static DBusError exception;
40 const char *atspi_path_dec = ATSPI_DBUS_PATH_DEC;
41 const char *atspi_path_registry = ATSPI_DBUS_PATH_REGISTRY;
42 const char *atspi_path_root = ATSPI_DBUS_PATH_ROOT;
43 const char *atspi_bus_registry = ATSPI_DBUS_NAME_REGISTRY;
44 const char *atspi_interface_accessible = ATSPI_DBUS_INTERFACE_ACCESSIBLE;
45 const char *atspi_interface_action = ATSPI_DBUS_INTERFACE_ACTION;
46 const char *atspi_interface_application = ATSPI_DBUS_INTERFACE_APPLICATION;
47 const char *atspi_interface_component = ATSPI_DBUS_INTERFACE_COMPONENT;
48 const char *atspi_interface_dec = ATSPI_DBUS_INTERFACE_DEC;
49 const char *atspi_interface_device_event_listener = ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER;
50 const char *atspi_interface_document = ATSPI_DBUS_INTERFACE_DOCUMENT;
51 const char *atspi_interface_editable_text = ATSPI_DBUS_INTERFACE_EDITABLE_TEXT;
52 const char *atspi_interface_event_object = ATSPI_DBUS_INTERFACE_EVENT_OBJECT;
53 const char *atspi_interface_hyperlink = ATSPI_DBUS_INTERFACE_HYPERLINK;
54 const char *atspi_interface_hypertext = ATSPI_DBUS_INTERFACE_HYPERTEXT;
55 const char *atspi_interface_image = ATSPI_DBUS_INTERFACE_IMAGE;
56 const char *atspi_interface_registry = ATSPI_DBUS_INTERFACE_REGISTRY;
57 const char *atspi_interface_selection = ATSPI_DBUS_INTERFACE_SELECTION;
58 const char *atspi_interface_table = ATSPI_DBUS_INTERFACE_TABLE;
59 const char *atspi_interface_text = ATSPI_DBUS_INTERFACE_TEXT;
60 const char *atspi_interface_cache = ATSPI_DBUS_INTERFACE_CACHE;
61 const char *atspi_interface_value = ATSPI_DBUS_INTERFACE_VALUE;
63 static const char *interfaces[] =
65 ATSPI_DBUS_INTERFACE_ACCESSIBLE,
66 ATSPI_DBUS_INTERFACE_ACTION,
67 ATSPI_DBUS_INTERFACE_APPLICATION,
68 ATSPI_DBUS_INTERFACE_COLLECTION,
69 ATSPI_DBUS_INTERFACE_COMPONENT,
70 ATSPI_DBUS_INTERFACE_DOCUMENT,
71 ATSPI_DBUS_INTERFACE_EDITABLE_TEXT,
72 ATSPI_DBUS_INTERFACE_HYPERLINK,
73 ATSPI_DBUS_INTERFACE_HYPERTEXT,
74 ATSPI_DBUS_INTERFACE_IMAGE,
75 "org.a11y.atspi.LoginHelper",
76 ATSPI_DBUS_INTERFACE_SELECTION,
77 ATSPI_DBUS_INTERFACE_TABLE,
78 ATSPI_DBUS_INTERFACE_TEXT,
79 ATSPI_DBUS_INTERFACE_VALUE,
84 _atspi_get_iface_num (const char *iface)
86 /* TODO: Use a binary search or hash to improve performance */
89 for (i = 0; interfaces[i]; i++)
91 if (!strcmp(iface, interfaces[i])) return i;
101 live_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
106 /* TODO: Add an application parameter */
115 #define APP_IS_REGISTRY(app) (!strcmp (app->bus_name, atspi_bus_registry))
126 g_hash_table_destroy (refs);
130 static gboolean atspi_inited = FALSE;
132 static GHashTable *app_hash = NULL;
134 static AtspiApplication *
135 get_application (const char *bus_name)
137 AtspiApplication *app = NULL;
142 app_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_unref);
143 if (!app_hash) return NULL;
145 app = g_hash_table_lookup (app_hash, bus_name);
147 bus_name_dup = g_strdup (bus_name);
148 if (!bus_name_dup) return NULL;
149 // TODO: change below to something that will send state-change:defunct notification if necessary */
150 app = _atspi_application_new (bus_name);
151 if (!app) return NULL;
152 app->bus_name = bus_name_dup;
153 if (APP_IS_REGISTRY (app))
155 app->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
159 app->hash = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_object_unref);
161 g_hash_table_insert (app_hash, bus_name_dup, app);
165 static AtspiAccessible *
166 ref_accessible (const char *app_name, const char *path)
168 AtspiApplication *app = get_application (app_name);
171 if (!strcmp (path, "/org/a11y/atspi/accessible/root"))
175 app->root = atspi_accessible_new (app, atspi_path_root);
176 app->root->accessible_parent = atspi_get_desktop (0);
178 return g_object_ref (app->root);
181 a = g_hash_table_lookup (app->hash, path);
187 a = atspi_accessible_new (app, path);
190 g_hash_table_insert (app->hash, a->path, a);
191 g_object_ref (a); /* for the hash */
204 GArray *state_bitflags;
207 static DBusHandlerResult
208 handle_remove_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
210 const char *sender = dbus_message_get_sender (message);
211 AtspiApplication *app;
213 DBusMessageIter iter, iter_struct;
214 const char *signature = dbus_message_get_signature (message);
218 if (strcmp (signature, "(so)") != 0)
220 g_warning ("at-spi: Unknown signature %s for RemoveAccessible", signature);
221 return DBUS_HANDLER_RESULT_HANDLED;
224 dbus_message_iter_init (message, &iter);
225 dbus_message_iter_recurse (&iter, &iter_struct);
226 dbus_message_iter_get_basic (&iter_struct, &sender);
227 dbus_message_iter_get_basic (&iter_struct, &path);
228 app = get_application (sender);
229 a = ref_accessible (sender, path);
230 if (a->accessible_parent && g_list_find (a->accessible_parent->children, a))
232 a->accessible_parent->children = g_list_remove (a->accessible_parent->children, a);
235 g_hash_table_remove (app->hash, app->bus_name);
236 g_object_unref (a); /* unref our own ref */
237 return DBUS_HANDLER_RESULT_HANDLED;
241 add_app_to_desktop (AtspiAccessible *a, const char *bus_name)
246 dbus_error_init (&error);
247 AtspiAccessible *obj = ref_accessible (bus_name, atspi_path_root);
250 GList *new_list = g_list_append (a->children, obj);
253 a->children = new_list;
259 g_warning ("Error calling getRoot for %s: %s", bus_name, error.message);
265 send_children_changed (AtspiAccessible *parent, AtspiAccessible *child, gboolean add)
269 memset (&e, 0, sizeof(e));
270 e.type = (add? "object:children-changed:add": "object:children-changed:remove");
272 e.detail1 = g_list_index (parent->children, child);
273 _atspi_send_event (&e);
277 unref_object_and_descendants (AtspiAccessible *obj)
281 for (l = obj->children; l; l = l->next)
283 unref_object_and_descendants (l->data);
285 g_object_unref (obj);
289 remove_app_from_desktop (AtspiAccessible *a, const char *bus_name)
292 AtspiAccessible *child;
294 for (l = a->children; l; l = l->next)
297 if (!strcmp (bus_name, child->app->bus_name)) break;
301 g_warning ("Removing unregistered app %s; doing nothing\n", bus_name);
304 send_children_changed (a, child, FALSE);
305 a->children = g_list_remove (a->children, child);
306 unref_object_and_descendants (child);
310 static AtspiAccessible *desktop;
313 get_reference_from_iter (DBusMessageIter *iter, const char **app_name, const char **path)
315 DBusMessageIter iter_struct;
317 dbus_message_iter_recurse (iter, &iter_struct);
318 dbus_message_iter_get_basic (&iter_struct, app_name);
319 dbus_message_iter_next (&iter_struct);
320 dbus_message_iter_get_basic (&iter_struct, path);
321 dbus_message_iter_next (iter);
325 add_accessible_from_iter (DBusMessageIter *iter)
329 DBusMessageIter iter_struct, iter_array;
330 const char *app_name, *path;
331 AtspiApplication *app;
332 AtspiAccessible *accessible;
333 const char *name, *description;
335 dbus_uint32_t *states;
338 dbus_message_iter_recurse (iter, &iter_struct);
341 get_reference_from_iter (&iter_struct, &app_name, &path);
342 accessible = ref_accessible (app_name, path);
344 /* Get application: TODO */
345 dbus_message_iter_next (&iter_struct);
348 get_reference_from_iter (&iter_struct, &app_name, &path);
349 accessible->accessible_parent = ref_accessible (app_name, path);
352 dbus_message_iter_recurse (&iter_struct, &iter_array);
353 while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
355 AtspiAccessible *child;
356 get_reference_from_iter (&iter_array, &app_name, &path);
357 child = ref_accessible (app_name, path);
358 new_list = g_list_append (accessible->children, child);
359 if (new_list) accessible->children = new_list;
363 accessible->interfaces = 0;
364 dbus_message_iter_next (&iter_struct);
365 dbus_message_iter_recurse (&iter_struct, &iter_array);
366 while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
370 dbus_message_iter_get_basic (&iter_array, &iface);
371 if (!strcmp (iface, "org.freedesktop.DBus.Introspectable")) continue;
372 n = _atspi_get_iface_num (iface);
375 g_warning ("Unknown interface %s", iface);
377 else accessible->interfaces |= (1 << n);
378 dbus_message_iter_next (&iter_array);
380 dbus_message_iter_next (&iter_struct);
383 dbus_message_iter_get_basic (&iter_struct, &name);
384 accessible->name = g_strdup (name);
385 dbus_message_iter_next (&iter_struct);
388 dbus_message_iter_get_basic (&iter_struct, &role);
389 accessible->role = role;
390 dbus_message_iter_next (&iter_struct);
393 dbus_message_iter_get_basic (&iter_struct, &description);
394 accessible->description = g_strdup (description);
395 dbus_message_iter_next (&iter_struct);
397 dbus_message_iter_recurse (&iter_struct, &iter_array);
398 dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
401 g_warning ("at-spi: expected 2 values in states array; got %d\n", count);
402 accessible->states = atspi_state_set_new (accessible, 0);
406 guint64 val = ((guint64)states [1]) << 32;
408 accessible->states = atspi_state_set_new (accessible, val);
410 dbus_message_iter_next (&iter_struct);
412 /* This is a bit of a hack since the cache holds a ref, so we don't need
413 * the one provided for us anymore */
414 g_object_unref (accessible);
418 add_accessibles (const char *app_name)
421 DBusMessage *message, *reply;
422 DBusMessageIter iter, iter_array;
424 AtspiApplication *app = get_application (app_name);
425 /* TODO: Move this functionality into app initializer? */
426 dbus_error_init (&error);
427 message = dbus_message_new_method_call (app_name, "/org/a11y/atspi/cache", atspi_interface_cache, "GetItems");
428 reply = _atspi_dbus_send_with_reply_and_block (message);
429 if (!reply || strcmp (dbus_message_get_signature (reply), "a((so)(so)(so)a(so)assusau)") != 0)
431 g_warning ("at-spi: Error in GetItems");
434 dbus_message_unref (reply);
436 dbus_message_iter_init (reply, &iter);
437 dbus_message_iter_recurse (&iter, &iter_array);
438 while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
440 add_accessible_from_iter (&iter_array);
441 dbus_message_iter_next (&iter_array);
443 dbus_message_unref (reply);
446 /* TODO: Do we stil need this function? */
447 static AtspiAccessible *
448 ref_accessible_desktop (AtspiApplication *app)
454 DBusMessage *message, *reply;
455 DBusMessageIter iter, iter_array;
459 g_object_ref (desktop);
462 desktop = atspi_accessible_new (app, atspi_path_root);
467 g_hash_table_insert (app->hash, desktop->path, desktop);
468 g_object_ref (desktop); /* for the hash */
469 desktop->name = g_strdup ("main");
470 dbus_error_init (&error);
471 message = dbus_message_new_method_call (atspi_bus_registry,
473 atspi_interface_accessible,
477 reply = _atspi_dbus_send_with_reply_and_block (message);
478 if (!reply || strcmp (dbus_message_get_signature (reply), "a(so)") != 0)
480 g_error ("Couldn't get application list: %s", error.message);
482 dbus_message_unref (reply);
485 dbus_message_iter_init (reply, &iter);
486 dbus_message_iter_recurse (&iter, &iter_array);
487 while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
489 const char *app_name, *path;
490 get_reference_from_iter (&iter_array, &app_name, &path);
491 add_accessibles (app_name);
492 add_app_to_desktop (desktop, app_name);
494 dbus_message_unref (reply);
499 _atspi_ref_accessible (const char *app, const char *path)
501 AtspiApplication *a = get_application (app);
503 if ( APP_IS_REGISTRY(a))
505 return ref_accessible_desktop (a);
507 return ref_accessible (app, path);
511 _atspi_dbus_return_accessible_from_message (DBusMessage *message)
513 DBusMessageIter iter;
514 AtspiAccessible *retval = NULL;
515 const char *signature = dbus_message_get_signature (message);
517 if (!strcmp (signature, "(so)"))
519 dbus_message_iter_init (message, &iter);
520 retval = _atspi_dbus_return_accessible_from_iter (&iter);
524 g_warning ("Atspi: Called __atspi_dbus_return_accessible_from_message with strange signature %s", signature);
526 dbus_message_unref (message);
531 _atspi_dbus_return_accessible_from_iter (DBusMessageIter *iter)
533 const char *app_name, *path;
535 get_reference_from_iter (iter, &app_name, &path);
536 return ref_accessible (app_name, path);
539 /* TODO: Remove this function. We should not need it anymore.
540 * If we do, it's a bug.
543 _atspi_ref_related_accessible (AtspiAccessible *obj, const AtspiReference *ref)
545 const char *app = (ref->name && ref->name[0]? ref->name: obj->app->bus_name);
546 return ref_accessible (app, obj->path);
549 const char *cache_signal_type = "((so)(so)(so)a(so)assusau)";
551 static DBusHandlerResult
552 handle_add_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
554 DBusMessageIter iter;
555 const char *sender = dbus_message_get_sender (message);
556 AtspiApplication *app = get_application (sender);
557 const char *type = cache_signal_type;
559 if (strcmp (dbus_message_get_signature (message), cache_signal_type) != 0)
561 g_warning ("atspi: AddAccessible with unknown signature %s\n", dbus_message_get_signature (message));
565 dbus_message_iter_init (message, &iter);
566 add_accessible_from_iter (&iter);
569 static DBusHandlerResult
570 atspi_dbus_filter (DBusConnection *bus, DBusMessage *message, void *data)
572 int type = dbus_message_get_type (message);
573 const char *interface = dbus_message_get_interface (message);
574 const char *member = dbus_message_get_member (message);
578 if (type == DBUS_MESSAGE_TYPE_SIGNAL &&
579 !strncmp (interface, "org.a11y.atspi.Event.", 21))
581 return atspi_dbus_handle_event (bus, message, data);
583 if (dbus_message_is_method_call (message, atspi_interface_device_event_listener, "notifyEvent"))
585 g_warning ("atspi: TODO: DeviceEvent");
586 //return handle_device_event (bus, message, data);
588 if (dbus_message_is_signal (message, atspi_interface_cache, "AddAccessible"))
590 return handle_add_accessible (bus, message, data);
592 if (dbus_message_is_signal (message, atspi_interface_cache, "RemoveAccessible"))
594 return handle_remove_accessible (bus, message, data);
596 /* TODO: Handle ChildrenChanged, StateChanged, PropertyChanged */
597 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
600 static const char *signal_interfaces[] =
602 "org.a11y.atspi.Event.Object",
603 "org.a11y.atspi.Event.Window",
604 "org.a11y.atspi.Event.Mouse",
605 "org.a11y.atspi.Event.Terminal",
606 "org.a11y.atspi.Event.Document",
607 "org.a11y.atspi.Event.Focus",
612 * Returns a 'canonicalized' value for DISPLAY,
613 * with the screen number stripped off if present.
615 * TODO: Avoid having duplicate functions for this here and in at-spi2-atk
618 spi_display_name (void)
620 static const char *canonical_display_name = NULL;
621 if (!canonical_display_name)
623 const gchar *display_env = g_getenv ("AT_SPI_DISPLAY");
626 display_env = g_getenv ("DISPLAY");
627 if (!display_env || !display_env[0])
628 canonical_display_name = ":0";
631 gchar *display_p, *screen_p;
632 canonical_display_name = g_strdup (display_env);
633 display_p = strrchr (canonical_display_name, ':');
634 screen_p = strrchr (canonical_display_name, '.');
635 if (screen_p && display_p && (screen_p > display_p))
643 canonical_display_name = display_env;
646 return canonical_display_name;
649 /* TODO: Avoid having duplicate functions for this here and in at-spi2-atk */
650 static DBusConnection *
651 get_accessibility_bus ()
655 Display *bridge_display;
657 unsigned char *data = NULL;
658 unsigned long nitems;
659 unsigned long leftover;
661 DBusConnection *bus = NULL;
664 bridge_display = XOpenDisplay (spi_display_name ());
667 g_warning ("AT_SPI: Could not get the display\n");
671 AT_SPI_BUS = XInternAtom (bridge_display, "AT_SPI_BUS", False);
672 XGetWindowProperty (bridge_display,
673 XDefaultRootWindow (bridge_display),
675 (long) BUFSIZ, False,
676 (Atom) 31, &actual_type, &actual_format,
677 &nitems, &leftover, &data);
679 dbus_error_init (&error);
684 ("AT-SPI: Accessibility bus not found - Using session bus.\n");
685 bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
688 g_warning ("AT-SPI: Couldn't connect to bus: %s\n", error.message);
694 bus = dbus_connection_open (data, &error);
697 g_warning ("AT-SPI: Couldn't connect to bus: %s\n", error.message);
702 if (!dbus_bus_register (bus, &error))
704 g_warning ("AT-SPI: Couldn't register with bus: %s\n", error.message);
716 * Connects to the accessibility registry and initializes the SPI.
718 * Returns: 0 on success, otherwise an integer error code.
738 dbus_error_init (&error);
739 bus = get_accessibility_bus ();
742 g_error ("Couldn't get session bus");
745 dbus_bus_register (bus, &error);
746 dbus_connection_setup_with_g_main(bus, g_main_context_default());
747 dbus_connection_add_filter (bus, atspi_dbus_filter, NULL, NULL);
748 match = g_strdup_printf ("type='signal',interface='%s',member='AddAccessible'", atspi_interface_cache);
749 dbus_error_init (&error);
750 dbus_bus_add_match (bus, match, &error);
752 match = g_strdup_printf ("type='signal',interface='%s',member='RemoveAccessible'", atspi_interface_cache);
753 dbus_bus_add_match (bus, match, &error);
755 match = g_strdup_printf ("type='signal',interface='%s',member='ChildrenChanged'", atspi_interface_event_object);
756 dbus_bus_add_match (bus, match, &error);
758 match = g_strdup_printf ("type='signal',interface='%s',member='PropertyChange'", atspi_interface_event_object);
759 dbus_bus_add_match (bus, match, &error);
761 match = g_strdup_printf ("type='signal',interface='%s',member='StateChanged'", atspi_interface_event_object);
762 dbus_bus_add_match (bus, match, &error);
767 static GMainLoop *mainloop;
772 * Starts/enters the main event loop for the AT-SPI services.
774 * (NOTE: This method does not return control, it is exited via a call to
775 * atspi_event_quit () from within an event handler).
779 atspi_event_main (void)
781 mainloop = g_main_loop_new (NULL, FALSE);
782 g_main_loop_run (mainloop);
788 * Quits the last main event loop for the SPI services,
789 * see atspi_event_main
792 atspi_event_quit (void)
794 g_main_loop_quit (mainloop);
800 * Disconnects from the Accessibility Registry and releases
801 * any floating resources. Call only once at exit.
803 * Returns: 0 if there were no leaks, otherwise non zero.
815 atspi_inited = FALSE;
819 leaked = g_hash_table_size (live_refs);
832 _atspi_dbus_call (AtspiAccessible *obj, const char *interface, const char *method, GError **error, const char *type, ...)
838 dbus_error_init (&err);
839 va_start (args, type);
840 retval = dbind_method_call_reentrant_va (_atspi_bus(), obj->app->bus_name, obj->path, interface, method, &err, type, args);
842 if (dbus_error_is_set (&err))
844 /* TODO: Set gerror */
845 dbus_error_free (&err);
851 _atspi_dbus_call_partial (AtspiAccessible *obj, const char *interface, const char *method, GError **error, const char *type, ...)
856 DBusMessage *msg = NULL, *reply = NULL;
857 DBusMessageIter iter;
860 dbus_error_init (&err);
861 va_start (args, type);
863 msg = dbus_message_new_method_call (obj->app->bus_name, obj->path, interface, method);
868 dbus_message_iter_init_append (msg, &iter);
869 dbind_any_marshal_va (&iter, &p, args);
871 reply = dbind_send_and_allow_reentry (_atspi_bus(), msg, &err);
874 if (dbus_error_is_set (&err))
876 /* TODO: Set gerror */
877 dbus_error_free (&err);
883 _atspi_dbus_get_property (AtspiAccessible *obj, const char *interface, const char *name, GError **error, const char *type, void *data)
885 DBusMessage *message, *reply;
886 DBusMessageIter iter, iter_variant;
888 dbus_bool_t retval = FALSE;
890 message = dbus_message_new_method_call (obj->app->bus_name, obj->path, "org.freedesktop.DBus.Properties", "Get");
893 // TODO: throw exception
896 dbus_message_append_args (message, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
897 dbus_error_init (&err);
898 reply = dbus_connection_send_with_reply_and_block (_atspi_bus(), message, 1000, &err);
899 dbus_message_unref (message);
902 // TODO: throw exception
905 dbus_message_iter_init (reply, &iter);
906 dbus_message_iter_recurse (&iter, &iter_variant);
907 if (dbus_message_iter_get_arg_type (&iter_variant) != type[0])
909 g_warning ("atspi_dbus_get_property: Wrong type: expected %s, got %c\n", type, dbus_message_iter_get_arg_type (&iter_variant));
912 dbus_message_iter_get_basic (&iter_variant, data);
913 dbus_message_unref (reply);
914 if (type[0] == 's') *(char **)data = g_strdup (*(char **)data);
921 _atspi_dbus_send_with_reply_and_block (DBusMessage *message)
926 dbus_error_init (&err);
927 g_warning ("TODO: Write _atspi_dbus_send_with_reply_and_block");
928 reply = dbus_connection_send_with_reply_and_block (_atspi_bus(), message, 1000, &err);
929 dbus_message_unref (message);
934 _atspi_dbus_hash_from_message (DBusMessage *message)
936 GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal);
937 DBusMessageIter iter, iter_array, iter_dict;
938 const char *signature;
940 signature = dbus_message_get_signature (message);
942 if (strcmp (signature, "a{ss}") != 0)
944 g_warning ("Trying to get hash from message of unexpected type %s\n", signature);
948 dbus_message_iter_init (message, &iter);
949 dbus_message_iter_recurse (&iter, &iter_array);
950 while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
952 const char *name, *value;
953 dbus_message_iter_recurse (&iter_array, &iter_dict);
954 dbus_message_iter_get_basic (&iter_dict, &name);
955 dbus_message_iter_get_basic (&iter_dict, &value);
956 g_hash_table_insert (hash, g_strdup (name), g_strdup (value));
957 dbus_message_iter_next (&iter_array);
963 _atspi_dbus_attribute_array_from_message (DBusMessage *message)
965 GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
966 DBusMessageIter iter, iter_array, iter_dict;
967 const char *signature;
970 signature = dbus_message_get_signature (message);
972 if (strcmp (signature, "a{ss}") != 0)
974 g_warning ("Trying to get hash from message of unexpected type %s\n", signature);
978 dbus_message_iter_init (message, &iter);
980 dbus_message_iter_recurse (&iter, &iter_array);
981 while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
983 const char *name, *value;
986 dbus_message_iter_recurse (&iter_array, &iter_dict);
987 dbus_message_iter_get_basic (&iter_dict, &name);
988 dbus_message_iter_get_basic (&iter_dict, &value);
989 str = g_strdup_printf ("%s:;%s", name, value);
990 new_array = g_array_append_val (array, str);
993 dbus_message_iter_next (&iter);;