2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2008 Novell, Inc.
6 * Copyright 2001, 2002 Sun Microsystems Inc.,
7 * Copyright 2001, 2002 Ximian, Inc.
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.
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.
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.
26 #include <droute/introspect-loader.h>
28 #include "accessible.h"
30 #define get_object(message) spi_dbus_get_object(dbus_message_get_path(message))
33 append_update (DBusMessageIter * iter_array, AtkObject * obj,
34 dbus_bool_t include_children, DRouteData * data)
36 DBusMessageIter iter_struct, iter_sub_array;
39 const char *name, *desc;
42 AtkObject *parent = NULL;
47 g_assert(data != NULL);
49 parent = atk_object_get_parent(obj);
52 path_parent = g_strdup("/");
56 path_parent = spi_dbus_get_path (parent);
59 dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL,
61 path = spi_dbus_get_path (obj);
62 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
63 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path_parent);
65 dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "o",
67 childcount = atk_object_get_n_accessible_children (obj);
68 for (i = 0; i < childcount; i++)
70 AtkObject *child = atk_object_ref_accessible_child (obj, i);
71 char *child_path = spi_dbus_get_path (child);
74 dbus_message_iter_append_basic (&iter_sub_array,
75 DBUS_TYPE_OBJECT_PATH, &child_path);
79 g_object_unref (child);
81 if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
83 dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
85 if (data) for (l = data->interfaces; l; l = g_slist_next (l))
87 DRouteInterface *iface_def = (DRouteInterface *) l->data;
89 if (iface_def->get_datum)
91 datum = (*iface_def->get_datum) (path, data->user_data);
95 dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_STRING,
97 if (iface_def->free_datum)
98 (*iface_def->free_datum) (datum);
100 if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
102 name = atk_object_get_name (obj);
105 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
106 role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
107 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
108 desc = atk_object_get_description (obj);
111 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
112 if (!dbus_message_iter_close_container (iter_array, &iter_struct))
114 if (!include_children) childcount = 0;
115 for (i = 0; i < childcount; i++)
117 AtkObject *child = atk_object_ref_accessible_child (obj, i);
121 result = append_update (iter_array, child, TRUE, data);
122 g_object_unref (child);
129 if (path) g_free(path);
134 spi_dbus_append_tree (DBusMessage * message, AtkObject * obj,
137 DBusMessageIter iter, iter_array;
140 g_assert(data != NULL);
142 dbus_message_iter_init_append (message, &iter);
143 dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)",
145 result = append_update (&iter_array, obj, TRUE, data);
147 result = dbus_message_iter_close_container (&iter, &iter_array);
152 impl_getRoot (DBusConnection * bus, DBusMessage * message, void *user_data)
154 AtkObject *root = atk_get_root();
158 if (root) path = spi_dbus_get_path(root);
160 return spi_dbus_general_error (message);
161 reply = dbus_message_new_method_return (message);
162 dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path,
169 impl_getTree (DBusConnection * bus, DBusMessage * message, void *user_data)
172 AtkObject *root = atk_get_root();
174 if (!root) return spi_dbus_general_error(message);
175 reply = dbus_message_new_method_return (message);
176 spi_dbus_append_tree (reply, root, (DRouteData *) user_data);
181 impl_introspect (DBusConnection *bus, DBusMessage *message, void *user_data)
189 path = dbus_message_get_path(message);
191 output = g_string_new(spi_introspection_header);
193 g_string_append_printf(output, spi_introspection_node_element, path);
195 spi_append_interface(output, SPI_DBUS_INTERFACE_TREE);
197 g_string_append(output, spi_introspection_footer);
198 final = g_string_free(output, FALSE);
200 reply = dbus_message_new_method_return (message);
201 g_assert(reply != NULL);
202 dbus_message_append_args(reply, DBUS_TYPE_STRING, &final,
209 static DBusHandlerResult
210 message_handler (DBusConnection *bus, DBusMessage *message, void *user_data)
212 const char *iface = dbus_message_get_interface (message);
213 const char *member = dbus_message_get_member (message);
215 DBusMessage *reply = NULL;
217 g_return_val_if_fail(iface != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
219 if (!strcmp(iface, SPI_DBUS_INTERFACE_TREE))
221 if (!strcmp(member, "getRoot"))
223 reply = impl_getRoot(bus, message, user_data);
226 if (!strcmp(member, "getTree"))
228 reply = impl_getTree(bus, message, user_data);
232 if (!strcmp(iface, "org.freedesktop.DBus.Introspectable"))
234 if (!strcmp(member, "Introspect"))
236 reply = impl_introspect(bus, message, user_data);
242 dbus_connection_send (bus, reply, NULL);
243 dbus_message_unref (reply);
246 return DBUS_HANDLER_RESULT_HANDLED;
249 static DBusObjectPathVTable tree_vtable =
253 NULL, NULL, NULL, NULL
257 spi_register_tree_object(DBusConnection *bus,
261 dbus_bool_t mem = FALSE;
262 mem = dbus_connection_register_object_path(bus, path, &tree_vtable, data);
263 g_assert(mem == TRUE);
266 static GHashTable *cache_list;
269 #define UPDATE_REFRESH 2
270 #define UPDATE_REMOVE 3
272 static int update_pending = 0;
273 static gint update_pending_id;
277 DBusMessageIter iter;
282 static void handle_cache_item(char *path, guint action, CacheIterData *d)
291 if (d->removing) return;
292 obj = spi_dbus_get_object(path);
293 //printf("update %s\n", path);
294 append_update(&d->iter, obj, FALSE, d->droute);
297 //printf("remove: %s\n", path);
298 if (!d->removing) return;
299 dbus_message_iter_append_basic(&d->iter, DBUS_TYPE_OBJECT_PATH, &path);
302 g_hash_table_remove(cache_list, path);
306 spi_dbus_update_cache(DRouteData *data)
308 DBusMessage *message;
309 DBusMessageIter iter;
311 static gboolean in_update_cache = FALSE;
313 if (in_update_cache) return TRUE;
314 g_assert(data != NULL);
316 if (update_pending == 0) return FALSE;
317 //printf("Sending cache\n");
318 message = dbus_message_new_signal ("/org/freedesktop/atspi/tree", SPI_DBUS_INTERFACE_TREE, "updateTree");
319 if (!message) goto done;
320 dbus_message_iter_init_append (message, &iter);
321 dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)",
325 in_update_cache = TRUE;
328 /* This loop is needed because appending an item may cause new children
329 * to be registered and consequently added to the hash, so they, too,
330 * will need to be sent with the update */
332 g_hash_table_foreach(cache_list, (GHFunc)handle_cache_item, &d);
333 } while (update_pending);
334 dbus_message_iter_close_container(&iter, &d.iter);
335 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &d.iter);
337 g_hash_table_foreach(cache_list, (GHFunc)handle_cache_item, &d);
338 in_update_cache = FALSE;
339 dbus_message_iter_close_container(&iter, &d.iter);
340 dbus_connection_send(data->bus, message, NULL);
345 void spi_dbus_notify_change(AtkObject *obj, gboolean new, DRouteData *data)
347 guint action = (new? UPDATE_NEW: UPDATE_REFRESH);
348 char *path = spi_dbus_get_path(obj);
352 cache_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
359 if (g_hash_table_lookup(cache_list, path))
364 //printf("change: %s\n", path);
365 g_hash_table_insert(cache_list, path, (gpointer)action);
366 if (update_pending != 2 && data)
368 update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
371 else if (!update_pending) update_pending = 1;
374 void spi_dbus_notify_remove(AtkObject *obj, DRouteData *data)
376 guint action = UPDATE_REMOVE;
378 gchar *path = spi_dbus_get_path(obj);
380 //printf("notify remove: %s\n", path);
386 cur_action = (guint)g_hash_table_lookup(cache_list, path);
387 if (cur_action == UPDATE_NEW)
389 /* No one knew that this object ever existed, so just remove it */
390 //printf("Removing object from send queue\n");
391 g_hash_table_remove(cache_list, path);
396 g_hash_table_insert(cache_list, path, (gpointer)action);
397 if (update_pending != 2 && data)
399 update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
402 else if (!update_pending) update_pending = 1;
406 gboolean spi_dbus_object_is_known(AtkObject *obj)
409 char *path = spi_dbus_get_path(obj);
410 if (!path) return FALSE;
411 cur_action = (guint)g_hash_table_lookup(cache_list, path);
413 return (cur_action != UPDATE_NEW);