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 Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 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
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the
20
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21
 * Boston, MA 02110-1301, USA.
22
 */
23

            
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27

            
28
#include "accessible-register.h"
29
#include "bridge.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
/*---------------------------------------------------------------------------*/
76

            
77
161
G_DEFINE_TYPE (SpiRegister, spi_register, G_TYPE_OBJECT)
78

            
79
static void
80
161
spi_register_class_init (SpiRegisterClass *klass)
81
{
82
161
  GObjectClass *object_class = (GObjectClass *) klass;
83

            
84
161
  spi_register_parent_class = g_type_class_ref (G_TYPE_OBJECT);
85

            
86
161
  object_class->finalize = spi_register_finalize;
87

            
88
161
  register_signals[OBJECT_REGISTERED] =
89
161
      g_signal_new ("object-registered",
90
                    SPI_REGISTER_TYPE,
91
                    G_SIGNAL_ACTION,
92
                    0,
93
                    NULL,
94
                    NULL,
95
                    g_cclosure_marshal_VOID__OBJECT,
96
                    G_TYPE_NONE,
97
                    1,
98
                    G_TYPE_OBJECT);
99

            
100
161
  register_signals[OBJECT_DEREGISTERED] =
101
161
      g_signal_new ("object-deregistered",
102
                    SPI_REGISTER_TYPE,
103
                    G_SIGNAL_ACTION,
104
                    0,
105
                    NULL,
106
                    NULL,
107
                    g_cclosure_marshal_VOID__OBJECT,
108
                    G_TYPE_NONE,
109
                    1,
110
                    G_TYPE_OBJECT);
111
161
}
112

            
113
static void
114
161
spi_register_init (SpiRegister *reg)
115
{
116
161
  reg->ref2ptr = g_hash_table_new (g_direct_hash, g_direct_equal);
117
161
  reg->reference_counter = 0;
118
161
}
119

            
120
static void
121
deregister_object (gpointer data, GObject *gobj)
122
{
123
  SpiRegister *reg = SPI_REGISTER (data);
124

            
125
  spi_register_deregister_object (reg, gobj, FALSE);
126
}
127

            
128
static void
129
1444
spi_register_remove_weak_ref (gpointer key, gpointer val, gpointer reg)
130
{
131
1444
  g_object_weak_unref (val, deregister_object, reg);
132
1444
}
133

            
134
static void
135
161
spi_register_finalize (GObject *object)
136
{
137
161
  SpiRegister *reg = SPI_REGISTER (object);
138

            
139
161
  g_hash_table_foreach (reg->ref2ptr, spi_register_remove_weak_ref, reg);
140
161
  g_hash_table_unref (reg->ref2ptr);
141

            
142
161
  G_OBJECT_CLASS (spi_register_parent_class)->finalize (object);
143
161
}
144

            
145
/*---------------------------------------------------------------------------*/
146

            
147
/*
148
 * Each AtkObject must be asssigned a D-Bus path (Reference)
149
 *
150
 * This function provides an integer reference for a new
151
 * AtkObject.
152
 *
153
 * TODO: Make this reference a little more unique, this is shoddy.
154
 */
155
static guint
156
1444
assign_reference (SpiRegister *reg)
157
{
158
1444
  reg->reference_counter++;
159
  /* Reference of 0 not allowed as used as direct key in hash table */
160
1444
  if (reg->reference_counter == 0)
161
    reg->reference_counter++;
162
1444
  return reg->reference_counter;
163
}
164

            
165
/*---------------------------------------------------------------------------*/
166

            
167
/*
168
 * Returns the reference of the object, or 0 if it is not registered.
169
 */
170
static guint
171
5658
object_to_ref (GObject *gobj)
172
{
173
5658
  return GPOINTER_TO_INT (g_object_get_data (gobj, SPI_DBUS_ID));
174
}
175

            
176
/*
177
 * Converts the Accessible object reference to its D-Bus object path
178
 */
179
static gchar *
180
4214
ref_to_path (guint ref)
181
{
182
4214
  return g_strdup_printf (SPI_ATK_OBJECT_REFERENCE_TEMPLATE, ref);
183
}
184

            
185
/*---------------------------------------------------------------------------*/
186

            
187
/*
188
 * Callback for when a registered AtkObject is destroyed.
189
 *
190
 * Removes the AtkObject from the reference lookup tables, meaning
191
 * it is no longer exposed over D-Bus.
192
 */
193
void
194
spi_register_deregister_object (SpiRegister *reg, GObject *gobj, gboolean unref)
195
{
196
  guint ref;
197

            
198
  ref = object_to_ref (gobj);
199
  if (ref != 0)
200
    {
201
      g_signal_emit (reg,
202
                     register_signals[OBJECT_DEREGISTERED],
203
                     0,
204
                     gobj);
205
      if (unref)
206
        g_object_weak_unref (gobj, deregister_object, reg);
207
      g_hash_table_remove (reg->ref2ptr, GINT_TO_POINTER (ref));
208

            
209
#ifdef SPI_ATK_DEBUG
210
      g_debug ("DEREG  - %d", ref);
211
#endif
212
    }
213
}
214

            
215
static void
216
1444
register_object (SpiRegister *reg, GObject *gobj)
217
{
218
  guint ref;
219
1444
  g_return_if_fail (G_IS_OBJECT (gobj));
220

            
221
1444
  ref = assign_reference (reg);
222

            
223
1444
  g_hash_table_insert (reg->ref2ptr, GINT_TO_POINTER (ref), gobj);
224
1444
  g_object_set_data (G_OBJECT (gobj), SPI_DBUS_ID, GINT_TO_POINTER (ref));
225
1444
  g_object_weak_ref (G_OBJECT (gobj), deregister_object, reg);
226

            
227
#ifdef SPI_ATK_DEBUG
228
  g_debug ("REG  - %d", ref);
229
#endif
230

            
231
1444
  g_signal_emit (reg, register_signals[OBJECT_REGISTERED], 0, gobj);
232
}
233

            
234
/*---------------------------------------------------------------------------*/
235

            
236
/*
237
 * Used to lookup an GObject from its D-Bus path.
238
 *
239
 * If the D-Bus path is not found this function returns NULL.
240
 */
241
GObject *
242
992
spi_register_path_to_object (SpiRegister *reg, const char *path)
243
{
244
  guint index;
245
  void *data;
246

            
247
992
  g_return_val_if_fail (path, NULL);
248

            
249
992
  if (strncmp (path, SPI_ATK_OBJECT_PATH_PREFIX, SPI_ATK_PATH_PREFIX_LENGTH) != 0)
250
    return NULL;
251

            
252
992
  path += SPI_ATK_PATH_PREFIX_LENGTH; /* Skip over the prefix */
253

            
254
  /* Map the root path to the root object. */
255
992
  if (!g_strcmp0 (SPI_ATK_OBJECT_PATH_ROOT, path))
256
659
    return G_OBJECT (spi_global_app_data->root);
257

            
258
333
  index = atoi (path);
259
333
  data = g_hash_table_lookup (reg->ref2ptr, GINT_TO_POINTER (index));
260
333
  if (data)
261
333
    return G_OBJECT (data);
262
  else
263
    return NULL;
264
}
265

            
266
GObject *
267
983
spi_global_register_path_to_object (const char *path)
268
{
269
983
  return spi_register_path_to_object (spi_global_register, path);
270
}
271

            
272
/*
273
 * Used to lookup a D-Bus path from the GObject.
274
 *
275
 * If the objects is not already registered,
276
 * this function will register it.
277
 */
278
gchar *
279
6296
spi_register_object_to_path (SpiRegister *reg, GObject *gobj)
280
{
281
  guint ref;
282

            
283
6296
  if (gobj == NULL)
284
    return NULL;
285

            
286
  /* Map the root object to the root path. */
287
6296
  if ((void *) gobj == (void *) spi_global_app_data->root)
288
4164
    return g_strdup (spi_register_root_path);
289

            
290
4214
  ref = object_to_ref (gobj);
291
4214
  if (!ref)
292
    {
293
1444
      register_object (reg, gobj);
294
1444
      ref = object_to_ref (gobj);
295
    }
296

            
297
4214
  if (!ref)
298
    return NULL;
299
  else
300
4214
    return ref_to_path (ref);
301
}
302

            
303
guint
304
spi_register_object_to_ref (GObject *gobj)
305
{
306
  return object_to_ref (gobj);
307
}
308

            
309
/*
310
 * Gets the path that indicates the accessible desktop object.
311
 * This object is logically located on the registry daemon and not
312
 * within any particular application.
313
 */
314
gchar *
315
spi_register_root_object_path ()
316
{
317
  return g_strdup (SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT);
318
}
319

            
320
/*END------------------------------------------------------------------------*/