Integrate leasing scheme in-to atk-bridge.
[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 22
51 #define SPI_ATK_OBJECT_PATH_PREFIX  "/org/at_spi/accessible"
52 #define SPI_ATK_OBJECT_PATH_DESKTOP SPI_ATK_OBJECT_PATH_PREFIX "/desktop"
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 enum
61 {
62   OBJECT_REGISTERED,
63   OBJECT_DEREGISTERED,
64   LAST_SIGNAL
65 };
66 static guint register_signals[LAST_SIGNAL] = { 0 };
67
68 /*---------------------------------------------------------------------------*/
69
70 static void
71 spi_register_finalize (GObject * object);
72
73 static void
74 spi_register_dispose (GObject * object);
75
76 /*---------------------------------------------------------------------------*/
77
78 G_DEFINE_TYPE (SpiRegister, spi_register, G_TYPE_OBJECT)
79
80 static void spi_register_class_init (SpiRegisterClass * klass)
81 {
82   GObjectClass *object_class = (GObjectClass *) klass;
83
84   spi_register_parent_class = g_type_class_ref (G_TYPE_OBJECT);
85
86   object_class->finalize = spi_register_finalize;
87   object_class->dispose = spi_register_dispose;
88
89   register_signals [OBJECT_REGISTERED] =
90       g_signal_new ("object-registered",
91                     SPI_REGISTER_TYPE,
92                     G_SIGNAL_ACTION,
93                     0,
94                     NULL,
95                     NULL,
96                     g_cclosure_marshal_VOID__OBJECT,
97                     G_TYPE_NONE,
98                     1,
99                     G_TYPE_OBJECT);
100
101   register_signals [OBJECT_DEREGISTERED] =
102       g_signal_new ("object-deregistered",
103                     SPI_REGISTER_TYPE,
104                     G_SIGNAL_ACTION,
105                     0,
106                     NULL,
107                     NULL,
108                     g_cclosure_marshal_VOID__OBJECT,
109                     G_TYPE_NONE,
110                     1,
111                     G_TYPE_OBJECT);
112 }
113
114 static void
115 spi_register_init (SpiRegister * reg)
116 {
117   reg->ref2ptr = g_hash_table_new (g_direct_hash, g_direct_equal);
118   reg->reference_counter = 0;
119 }
120
121 static void
122 spi_register_finalize (GObject * object)
123 {
124   SpiRegister *reg = SPI_REGISTER (object);
125
126   g_free (reg->ref2ptr);
127
128   G_OBJECT_CLASS (spi_register_parent_class)->finalize (object);
129 }
130
131 static void
132 spi_register_dispose (GObject * object)
133 {
134   SpiRegister *reg = SPI_REGISTER (object);
135
136   G_OBJECT_CLASS (spi_register_parent_class)->dispose (object);
137 }
138
139 /*---------------------------------------------------------------------------*/
140
141 /*
142  * Each AtkObject must be asssigned a D-Bus path (Reference)
143  *
144  * This function provides an integer reference for a new
145  * AtkObject.
146  *
147  * TODO: Make this reference a little more unique, this is shoddy.
148  */
149 static guint
150 assign_reference (SpiRegister * reg)
151 {
152   reg->reference_counter++;
153   /* Reference of 0 not allowed as used as direct key in hash table */
154   if (reg->reference_counter == 0)
155     reg->reference_counter++;
156   return reg->reference_counter;
157 }
158
159 /*---------------------------------------------------------------------------*/
160
161 /*
162  * Returns the reference of the object, or 0 if it is not registered.
163  */
164 static guint
165 object_to_ref (GObject * gobj)
166 {
167   return GPOINTER_TO_INT (g_object_get_data (gobj, SPI_DBUS_ID));
168 }
169
170 /*
171  * Converts the Accessible object reference to its D-Bus object path
172  */
173 static gchar *
174 ref_to_path (guint ref)
175 {
176   return g_strdup_printf (SPI_ATK_OBJECT_REFERENCE_TEMPLATE, ref);
177 }
178
179 /*---------------------------------------------------------------------------*/
180
181 /*
182  * Callback for when a registered AtkObject is destroyed.
183  *
184  * Removes the AtkObject from the reference lookup tables, meaning
185  * it is no longer exposed over D-Bus.
186  */
187 static void
188 deregister_object (gpointer data, GObject * gobj)
189 {
190   SpiRegister *reg = SPI_REGISTER (data);
191   guint ref;
192
193   ref = object_to_ref (gobj);
194   if (ref != 0)
195     {
196       g_signal_emit (reg,
197                      register_signals [OBJECT_DEREGISTERED],
198                      0,
199                      gobj);
200       g_hash_table_remove (reg->ref2ptr, GINT_TO_POINTER (ref));
201     }
202 }
203
204 static void
205 register_object (SpiRegister * reg, GObject * gobj)
206 {
207   guint ref;
208   g_return_if_fail (G_IS_OBJECT (gobj));
209
210   ref = assign_reference (reg);
211
212   g_hash_table_insert (reg->ref2ptr, GINT_TO_POINTER (ref), gobj);
213   g_object_set_data (G_OBJECT (gobj), SPI_DBUS_ID, GINT_TO_POINTER (ref));
214   g_object_weak_ref (G_OBJECT (gobj), deregister_object, reg);
215
216   g_signal_emit (reg, register_signals [OBJECT_REGISTERED], 0, gobj);
217 }
218
219 /*---------------------------------------------------------------------------*/
220
221 /*
222  * Used to lookup an GObject from its D-Bus path.
223  * 
224  * If the D-Bus path is not found this function returns NULL.
225  */
226 GObject *
227 spi_register_path_to_object (SpiRegister * reg, const char *path)
228 {
229   guint index;
230   void *data;
231
232   g_return_val_if_fail (path, NULL);
233
234   if (strncmp (path, SPI_ATK_OBJECT_PATH_PREFIX, SPI_ATK_PATH_PREFIX_LENGTH)
235       != 0)
236     return NULL;
237
238   path += SPI_ATK_PATH_PREFIX_LENGTH;   /* Skip over the prefix */
239
240   if (!g_strcmp0 (SPI_ATK_OBJECT_PATH_DESKTOP, path))
241     return G_OBJECT (atk_get_root ());
242   if (path[0] != '/')
243     return NULL;
244
245   path++;
246   index = atoi (path);
247   data = g_hash_table_lookup (reg->ref2ptr, GINT_TO_POINTER (index));
248   if (data)
249     return G_OBJECT(data);
250   else
251     return NULL;
252 }
253
254 GObject *
255 spi_global_register_path_to_object (const char * path)
256 {
257   return spi_register_path_to_object (spi_global_register, path);
258 }
259
260 /*
261  * Used to lookup a D-Bus path from the GObject.
262  * 
263  * If the objects is not already registered, 
264  * this function will register it.
265  */
266 gchar *
267 spi_register_object_to_path (SpiRegister * reg, GObject * gobj)
268 {
269   guint ref;
270
271   ref = object_to_ref (gobj);
272   if (!ref)
273     {
274       register_object (reg, gobj);
275       ref = object_to_ref (gobj);
276     }
277
278   if (!ref)
279     return NULL;
280   else
281     return ref_to_path (ref);
282 }
283
284 guint
285 spi_register_object_to_ref (GObject * gobj)
286 {
287   return object_to_ref (gobj);
288 }
289   
290 /*
291  * Gets the path that indicates the accessible desktop object.
292  * This object is logically located on the registry daemon and not
293  * within any particular application.
294  */
295 gchar *
296 spi_register_desktop_object_path ()
297 {
298   return g_strdup (SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_DESKTOP);
299 }
300
301 /*END------------------------------------------------------------------------*/