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.
25 #include "accessible.h"
27 #define get_object(message) spi_dbus_get_object(dbus_message_get_path(message))
29 #define TREE_UPDATE_ACCESSIBLE 0
30 #define TREE_REMOVE_ACCESSIBLE 1
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;
40 dbus_uint16_t updating = TREE_UPDATE_ACCESSIBLE;
47 dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL,
49 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT16, &updating);
50 path = spi_dbus_get_path (obj);
51 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
52 path_parent = spi_dbus_get_path (atk_object_get_parent(obj));
53 if (!path_parent) path_parent = g_strdup("/");
54 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path_parent);
56 dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "o",
58 childcount = atk_object_get_n_accessible_children (obj);
59 for (i = 0; i < childcount; i++)
61 AtkObject *child = atk_object_ref_accessible_child (obj, i);
62 char *child_path = spi_dbus_get_path (child);
65 dbus_message_iter_append_basic (&iter_sub_array,
66 DBUS_TYPE_OBJECT_PATH, &child_path);
70 g_object_unref (child);
72 if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
74 dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
76 for (l = data->interfaces; l; l = g_slist_next (l))
78 DRouteInterface *iface_def = (DRouteInterface *) l->data;
80 if (iface_def->get_datum)
82 datum = (*iface_def->get_datum) (path, data->user_data);
86 dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_STRING,
88 if (iface_def->free_datum)
89 (*iface_def->free_datum) (datum);
91 if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
93 name = atk_object_get_name (obj);
96 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
97 role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
98 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
99 desc = atk_object_get_description (obj);
102 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
103 if (!dbus_message_iter_close_container (iter_array, &iter_struct))
105 if (!include_children) childcount = 0;
106 for (i = 0; i < childcount; i++)
108 AtkObject *child = atk_object_ref_accessible_child (obj, i);
112 result = append_update (iter_array, child, TRUE, data);
113 g_object_unref (child);
120 if (path) g_free(path);
125 append_remove (DBusMessageIter * iter_array, const char *path,
128 DBusMessageIter iter_struct, iter_sub_array;
130 const char *name, *desc;
131 dbus_uint16_t updating = TREE_REMOVE_ACCESSIBLE;
134 dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL,
136 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT16, &updating);
137 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
139 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path_parent);
140 dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "o",
142 if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
144 dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
146 if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
149 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
151 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
153 dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
154 if (!dbus_message_iter_close_container (iter_array, &iter_struct))
162 spi_dbus_append_tree (DBusMessage * message, AtkObject * obj,
165 DBusMessageIter iter, iter_array;
168 dbus_message_iter_init_append (message, &iter);
169 dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(qooaoassus)",
171 result = append_update (&iter_array, obj, TRUE, data);
173 result = dbus_message_iter_close_container (&iter, &iter_array);
178 impl_getRoot (DBusConnection * bus, DBusMessage * message, void *user_data)
180 AtkObject *root = atk_get_root();
184 if (root) path = spi_dbus_get_path(root);
186 return spi_dbus_general_error (message);
187 reply = dbus_message_new_method_return (message);
188 dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path,
195 impl_getTree (DBusConnection * bus, DBusMessage * message, void *user_data)
198 AtkObject *root = atk_get_root();
200 if (!root) return spi_dbus_general_error(message);
201 reply = dbus_message_new_method_return (message);
202 spi_dbus_append_tree (reply, root, (DRouteData *) user_data);
206 static DRouteMethod methods[] = {
207 {DROUTE_METHOD, impl_getRoot, "getRoot", "o,root,o" },
208 {DROUTE_METHOD, impl_getTree, "getTree", "a(qooaoassus),tree,o", TRUE},
209 {0, NULL, NULL, NULL}
213 spi_initialize_tree (DRouteData * data)
215 droute_add_interface (data, "org.freedesktop.atspi.Tree",
216 methods, NULL, NULL, NULL);
219 static GHashTable *cache_list;
222 #define UPDATE_REFRESH 2
223 #define UPDATE_REMOVE 3
225 static int update_pending = 0;
226 static gint update_pending_id;
230 DBusMessageIter iter;
234 static void handle_cache_item(char *path, guint action, CacheIterData *d)
243 obj = spi_dbus_get_object(path);
244 //printf("update %s\n", path);
245 append_update(&d->iter, obj, FALSE, d->droute);
248 //printf("remove: %s\n", path);
249 append_remove(&d->iter, path, d->droute);
252 g_hash_table_remove(cache_list, path);
255 gboolean spi_dbus_update_cache(DRouteData *data)
257 DBusMessage *message;
258 DBusMessageIter iter;
261 if (update_pending == 0) return FALSE;
262 //printf("Sending cache\n");
263 message = dbus_message_new_signal("/org/freedesktop/atspi/tree", "org.freedesktop.atspi.Tree", "UpdateTree");
264 if (!message) goto done;
265 dbus_message_iter_init_append (message, &iter);
266 dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(qooaoassus)",
271 /* This loop is needed because appending an item may cause new children
272 * to be registered and consequently added to the hash, so they, too,
273 * will need to be sent with the update */
275 g_hash_table_foreach(cache_list, (GHFunc)handle_cache_item, &d);
276 } while (update_pending);
277 dbus_message_iter_close_container(&iter, &d.iter);
278 dbus_connection_send(data->bus, message, NULL);
283 void spi_dbus_notify_change(AtkObject *obj, gboolean new, DRouteData *data)
285 guint action = (new? UPDATE_NEW: UPDATE_REFRESH);
286 char *path = spi_dbus_get_path(obj);
290 cache_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
297 if (g_hash_table_lookup(cache_list, path))
302 //printf("change: %s\n", path);
303 g_hash_table_insert(cache_list, path, (gpointer)action);
304 if (update_pending != 2 && data)
306 update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
309 else if (!update_pending) update_pending = 1;
312 void spi_dbus_notify_remove(AtkObject *obj, DRouteData *data)
314 guint action = UPDATE_REMOVE;
316 gchar *path = spi_dbus_get_path(obj);
318 //printf("notify remove: %s\n", path);
324 cur_action = (guint)g_hash_table_lookup(cache_list, path);
325 if (cur_action == UPDATE_NEW)
327 /* No one knew that this object ever existed, so just remove it */
328 //printf("Removing object from send queue\n");
329 g_hash_table_remove(cache_list, path);
334 g_hash_table_insert(cache_list, path, (gpointer)action);
335 if (update_pending != 2 && data)
337 update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
340 else if (!update_pending) update_pending = 1;