14f256f2dae29d6eb48cd05916be2da860fe3eb9
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / accessible-register.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2008 Novell, Inc.
6  * Copyright 2008, 2009, 2010 Codethink Ltd.
7  *
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.
12  *
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.
17  *
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.
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "bridge.h"
29 #include "accessible-register.h"
30
31 /*
32  * This module is responsible for keeping track of all the AtkObjects in
33  * the application, so that they can be accessed remotely and placed in
34  * a client side cache.
35  *
36  * To access an AtkObject remotely we need to provide a D-Bus object 
37  * path for it. The D-Bus object paths used have a standard prefix
38  * (SPI_ATK_OBJECT_PATH_PREFIX). Appended to this prefix is a string
39  * representation of an integer reference. So to access an AtkObject 
40  * remotely we keep a Hashtable that maps the given reference to 
41  * the AtkObject pointer. An object in this hash table is said to be 'registered'.
42  *
43  * The architecture of AT-SPI dbus is such that AtkObjects are not
44  * remotely reference counted. This means that we need to keep track of
45  * object destruction. When an object is destroyed it must be 'deregistered'
46  * To do this lookup we keep a dbus-id attribute on each AtkObject.
47  *
48  */
49
50 #define SPI_ATK_PATH_PREFIX_LENGTH 27
51 #define SPI_ATK_OBJECT_PATH_PREFIX  "/org/a11y/atspi/accessible/"
52 #define SPI_ATK_OBJECT_PATH_ROOT "root"
53
54 #define SPI_ATK_OBJECT_REFERENCE_TEMPLATE SPI_ATK_OBJECT_PATH_PREFIX "%d"
55
56 #define SPI_DBUS_ID "spi-dbus-id"
57
58 SpiRegister *spi_global_register = NULL;
59
60 static const gchar * spi_register_root_path = SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT;
61
62 enum
63 {
64   OBJECT_REGISTERED,
65   OBJECT_DEREGISTERED,
66   LAST_SIGNAL
67 };
68 static guint register_signals[LAST_SIGNAL] = { 0 };
69
70 /*---------------------------------------------------------------------------*/
71
72 static void
73 spi_register_finalize (GObject * object);
74
75 static void
76 spi_register_dispose (GObject * object);
77
78 /*---------------------------------------------------------------------------*/
79
80 G_DEFINE_TYPE (SpiRegister, spi_register, G_TYPE_OBJECT)
81
82 static void spi_register_class_init (SpiRegisterClass * klass)
83 {
84   GObjectClass *object_class = (GObjectClass *) klass;
85
86   spi_register_parent_class = g_type_class_ref (G_TYPE_OBJECT);
87
88   object_class->finalize = spi_register_finalize;
89   object_class->dispose = spi_register_dispose;
90
91   register_signals [OBJECT_REGISTERED] =
92       g_signal_new ("object-registered",
93                     SPI_REGISTER_TYPE,
94                     G_SIGNAL_ACTION,
95                     0,
96                     NULL,
97                     NULL,
98                     g_cclosure_marshal_VOID__OBJECT,
99                     G_TYPE_NONE,
100                     1,
101                     G_TYPE_OBJECT);
102
103   register_signals [OBJECT_DEREGISTERED] =
104       g_signal_new ("object-deregistered",
105                     SPI_REGISTER_TYPE,
106                     G_SIGNAL_ACTION,
107                     0,
108                     NULL,
109                     NULL,
110                     g_cclosure_marshal_VOID__OBJECT,
111                     G_TYPE_NONE,
112                     1,
113                     G_TYPE_OBJECT);
114 }
115
116 static void
117 spi_register_init (SpiRegister * reg)
118 {
119   reg->ref2ptr = g_hash_table_new (g_direct_hash, g_direct_equal);
120   reg->reference_counter = 0;
121 }
122
123 static void
124 deregister_object (gpointer data, GObject * gobj)
125 {
126   SpiRegister *reg = SPI_REGISTER (data);
127
128   spi_register_deregister_object (reg, gobj, FALSE);
129 }
130
131 static void
132 spi_register_remove_weak_ref (gpointer key, gpointer val, gpointer reg)
133 {
134   g_object_weak_unref (val, deregister_object, reg);
135 }
136
137 static void
138 spi_register_finalize (GObject * object)
139 {
140   SpiRegister *reg = SPI_REGISTER (object);
141
142   g_hash_table_foreach (reg->ref2ptr, spi_register_remove_weak_ref, reg);
143   g_hash_table_unref (reg->ref2ptr);
144
145   G_OBJECT_CLASS (spi_register_parent_class)->finalize (object);
146 }
147
148 static void
149 spi_register_dispose (GObject * object)
150 {
151   SpiRegister *reg = SPI_REGISTER (object);
152
153   G_OBJECT_CLASS (spi_register_parent_class)->dispose (object);
154 }
155
156 /*---------------------------------------------------------------------------*/
157
158 /*
159  * Each AtkObject must be asssigned a D-Bus path (Reference)
160  *
161  * This function provides an integer reference for a new
162  * AtkObject.
163  *
164  * TODO: Make this reference a little more unique, this is shoddy.
165  */
166 static guint
167 assign_reference (SpiRegister * reg)
168 {
169   reg->reference_counter++;
170   /* Reference of 0 not allowed as used as direct key in hash table */
171   if (reg->reference_counter == 0)
172     reg->reference_counter++;
173   return reg->reference_counter;
174 }
175
176 /*---------------------------------------------------------------------------*/
177
178 /*
179  * Returns the reference of the object, or 0 if it is not registered.
180  */
181 static guint
182 object_to_ref (GObject * gobj)
183 {
184   return GPOINTER_TO_INT (g_object_get_data (gobj, SPI_DBUS_ID));
185 }
186
187 /*
188  * Converts the Accessible object reference to its D-Bus object path
189  */
190 static gchar *
191 ref_to_path (guint ref)
192 {
193   return g_strdup_printf (SPI_ATK_OBJECT_REFERENCE_TEMPLATE, ref);
194 }
195
196 /*---------------------------------------------------------------------------*/
197
198 /*
199  * Callback for when a registered AtkObject is destroyed.
200  *
201  * Removes the AtkObject from the reference lookup tables, meaning
202  * it is no longer exposed over D-Bus.
203  */
204 void
205 spi_register_deregister_object (SpiRegister *reg, GObject *gobj, gboolean unref)
206 {
207   guint ref;
208
209   ref = object_to_ref (gobj);
210   if (ref != 0)
211     {
212       g_signal_emit (reg,
213                      register_signals [OBJECT_DEREGISTERED],
214                      0,
215                      gobj);
216       if (unref)
217         g_object_weak_unref (gobj, deregister_object, reg);
218       g_hash_table_remove (reg->ref2ptr, GINT_TO_POINTER (ref));
219
220 #ifdef SPI_ATK_DEBUG
221       g_debug ("DEREG  - %d", ref);
222 #endif
223     }
224 }
225
226 static void
227 register_object (SpiRegister * reg, GObject * gobj)
228 {
229   guint ref;
230   g_return_if_fail (G_IS_OBJECT (gobj));
231
232   ref = assign_reference (reg);
233
234   g_hash_table_insert (reg->ref2ptr, GINT_TO_POINTER (ref), gobj);
235   g_object_set_data (G_OBJECT (gobj), SPI_DBUS_ID, GINT_TO_POINTER (ref));
236   g_object_weak_ref (G_OBJECT (gobj), deregister_object, reg);
237
238 #ifdef SPI_ATK_DEBUG
239   g_debug ("REG  - %d", ref);
240 #endif
241
242   g_signal_emit (reg, register_signals [OBJECT_REGISTERED], 0, gobj);
243 }
244
245 /*---------------------------------------------------------------------------*/
246
247 /*
248  * Used to lookup an GObject from its D-Bus path.
249  * 
250  * If the D-Bus path is not found this function returns NULL.
251  */
252 GObject *
253 spi_register_path_to_object (SpiRegister * reg, const char *path)
254 {
255   guint index;
256   void *data;
257
258   g_return_val_if_fail (path, NULL);
259
260   if (strncmp (path, SPI_ATK_OBJECT_PATH_PREFIX, SPI_ATK_PATH_PREFIX_LENGTH)
261       != 0)
262     return NULL;
263
264   path += SPI_ATK_PATH_PREFIX_LENGTH; /* Skip over the prefix */
265
266   /* Map the root path to the root object. */
267   if (!g_strcmp0 (SPI_ATK_OBJECT_PATH_ROOT, path))
268     return G_OBJECT (spi_global_app_data->root);
269
270   index = atoi (path);
271   data = g_hash_table_lookup (reg->ref2ptr, GINT_TO_POINTER (index));
272   if (data)
273     return G_OBJECT(data);
274   else
275     return NULL;
276 }
277
278 GObject *
279 spi_global_register_path_to_object (const char * path)
280 {
281   return spi_register_path_to_object (spi_global_register, path);
282 }
283
284 /*
285  * Used to lookup a D-Bus path from the GObject.
286  * 
287  * If the objects is not already registered, 
288  * this function will register it.
289  */
290 gchar *
291 spi_register_object_to_path (SpiRegister * reg, GObject * gobj)
292 {
293   guint ref;
294
295   if (gobj == NULL)
296     return NULL;
297
298   /* Map the root object to the root path. */
299   if ((void *)gobj == (void *)spi_global_app_data->root)
300     return g_strdup (spi_register_root_path);
301
302   ref = object_to_ref (gobj);
303   if (!ref)
304     {
305       register_object (reg, gobj);
306       ref = object_to_ref (gobj);
307     }
308
309   if (!ref)
310     return NULL;
311   else
312     return ref_to_path (ref);
313 }
314
315 guint
316 spi_register_object_to_ref (GObject * gobj)
317 {
318   return object_to_ref (gobj);
319 }
320   
321 /*
322  * Gets the path that indicates the accessible desktop object.
323  * This object is logically located on the registry daemon and not
324  * within any particular application.
325  */
326 gchar *
327 spi_register_root_object_path ()
328 {
329   return g_strdup (SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT);
330 }
331
332 /*END------------------------------------------------------------------------*/