~elementary-os/elementaryos/os-patch-at-spi2-core-xenial

« back to all changes in this revision

Viewing changes to atspi/atspi-misc.c

  • Committer: RabbitBot
  • Date: 2016-11-16 09:38:52 UTC
  • Revision ID: rabbitbot@elementary.io-20161116093852-xn6hcgpg5y25xooo
Initial import, version 2.18.3-4ubuntu1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * AT-SPI - Assistive Technology Service Provider Interface
 
3
 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
 
4
 *
 
5
 * Copyright 2001, 2002 Sun Microsystems Inc.,
 
6
 * Copyright 2001, 2002 Ximian, Inc.
 
7
 * Copyright 2010, 2011 Novell, Inc.
 
8
 *
 
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.
 
13
 *
 
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.
 
18
 *
 
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.
 
23
 */
 
24
 
 
25
/*
 
26
 *
 
27
 * Basic SPI initialization and event loop function prototypes
 
28
 *
 
29
 */
 
30
 
 
31
#include "atspi-private.h"
 
32
#ifdef HAVE_X11
 
33
#include "X11/Xlib.h"
 
34
#endif
 
35
#include "atspi-gmain.h"
 
36
#include <stdio.h>
 
37
#include <string.h>
 
38
 
 
39
static void handle_get_items (DBusPendingCall *pending, void *user_data);
 
40
 
 
41
static DBusConnection *bus = NULL;
 
42
static GHashTable *live_refs = NULL;
 
43
static gint method_call_timeout = 800;
 
44
static gint app_startup_time = 15000;
 
45
static gboolean allow_sync = TRUE;
 
46
 
 
47
GMainLoop *atspi_main_loop;
 
48
GMainContext *atspi_main_context;
 
49
gboolean atspi_no_cache;
 
50
 
 
51
const char *atspi_path_dec = ATSPI_DBUS_PATH_DEC;
 
52
const char *atspi_path_registry = ATSPI_DBUS_PATH_REGISTRY;
 
53
const char *atspi_path_root = ATSPI_DBUS_PATH_ROOT;
 
54
const char *atspi_bus_registry = ATSPI_DBUS_NAME_REGISTRY;
 
55
const char *atspi_interface_accessible = ATSPI_DBUS_INTERFACE_ACCESSIBLE;
 
56
const char *atspi_interface_action = ATSPI_DBUS_INTERFACE_ACTION;
 
57
const char *atspi_interface_application = ATSPI_DBUS_INTERFACE_APPLICATION;
 
58
const char *atspi_interface_collection = ATSPI_DBUS_INTERFACE_COLLECTION;
 
59
const char *atspi_interface_component = ATSPI_DBUS_INTERFACE_COMPONENT;
 
60
const char *atspi_interface_dec = ATSPI_DBUS_INTERFACE_DEC;
 
61
const char *atspi_interface_device_event_listener = ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER;
 
62
const char *atspi_interface_document = ATSPI_DBUS_INTERFACE_DOCUMENT;
 
63
const char *atspi_interface_editable_text = ATSPI_DBUS_INTERFACE_EDITABLE_TEXT;
 
64
const char *atspi_interface_event_object = ATSPI_DBUS_INTERFACE_EVENT_OBJECT;
 
65
const char *atspi_interface_hyperlink = ATSPI_DBUS_INTERFACE_HYPERLINK;
 
66
const char *atspi_interface_hypertext = ATSPI_DBUS_INTERFACE_HYPERTEXT;
 
67
const char *atspi_interface_image = ATSPI_DBUS_INTERFACE_IMAGE;
 
68
const char *atspi_interface_registry = ATSPI_DBUS_INTERFACE_REGISTRY;
 
69
const char *atspi_interface_selection = ATSPI_DBUS_INTERFACE_SELECTION;
 
70
const char *atspi_interface_table = ATSPI_DBUS_INTERFACE_TABLE;
 
71
const char *atspi_interface_table_cell = ATSPI_DBUS_INTERFACE_TABLE_CELL;
 
72
const char *atspi_interface_text = ATSPI_DBUS_INTERFACE_TEXT;
 
73
const char *atspi_interface_cache = ATSPI_DBUS_INTERFACE_CACHE;
 
74
const char *atspi_interface_value = ATSPI_DBUS_INTERFACE_VALUE;
 
75
 
 
76
static const char *interfaces[] =
 
77
{
 
78
  ATSPI_DBUS_INTERFACE_ACCESSIBLE,
 
79
  ATSPI_DBUS_INTERFACE_ACTION,
 
80
  ATSPI_DBUS_INTERFACE_APPLICATION,
 
81
  ATSPI_DBUS_INTERFACE_COLLECTION,
 
82
  ATSPI_DBUS_INTERFACE_COMPONENT,
 
83
  ATSPI_DBUS_INTERFACE_DOCUMENT,
 
84
  ATSPI_DBUS_INTERFACE_EDITABLE_TEXT,
 
85
  ATSPI_DBUS_INTERFACE_HYPERLINK,
 
86
  ATSPI_DBUS_INTERFACE_HYPERTEXT,
 
87
  ATSPI_DBUS_INTERFACE_IMAGE,
 
88
  "org.a11y.atspi.LoginHelper",
 
89
  ATSPI_DBUS_INTERFACE_SELECTION,
 
90
  ATSPI_DBUS_INTERFACE_TABLE,
 
91
  ATSPI_DBUS_INTERFACE_TABLE_CELL,
 
92
  ATSPI_DBUS_INTERFACE_TEXT,
 
93
  ATSPI_DBUS_INTERFACE_VALUE,
 
94
  NULL
 
95
};
 
96
 
 
97
gint
 
98
_atspi_get_iface_num (const char *iface)
 
99
{
 
100
  /* TODO: Use a binary search or hash to improve performance */
 
101
  int i;
 
102
 
 
103
  for (i = 0; interfaces[i]; i++)
 
104
  {
 
105
    if (!strcmp(iface, interfaces[i])) return i;
 
106
  }
 
107
  return -1;
 
108
}
 
109
 
 
110
GHashTable *
 
111
_atspi_get_live_refs (void)
 
112
{
 
113
  if (!live_refs) 
 
114
    {
 
115
      live_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
 
116
    }
 
117
  return live_refs;
 
118
}
 
119
 
 
120
/* TODO: Add an application parameter */
 
121
DBusConnection *
 
122
_atspi_bus ()
 
123
{
 
124
  if (!bus)
 
125
    atspi_init ();
 
126
  if (!bus)
 
127
    g_error ("AT-SPI: Couldn't connect to accessibility bus. Is at-spi-bus-launcher running?");
 
128
  return bus;
 
129
}
 
130
 
 
131
#define APP_IS_REGISTRY(app) (!strcmp (app->bus_name, atspi_bus_registry))
 
132
 
 
133
static AtspiAccessible *desktop;
 
134
 
 
135
static void
 
136
cleanup ()
 
137
{
 
138
  GHashTable *refs;
 
139
  gint i;
 
140
 
 
141
  refs = live_refs;
 
142
  live_refs = NULL;
 
143
  if (refs)
 
144
    {
 
145
      g_hash_table_destroy (refs);
 
146
    }
 
147
 
 
148
  if (bus)
 
149
    {
 
150
      dbus_connection_close (bus);
 
151
      dbus_connection_unref (bus);
 
152
      bus = NULL;
 
153
    }
 
154
 
 
155
  if (!desktop)
 
156
    return;
 
157
 
 
158
  /* TODO: Do we need this code, or should we just dispose the desktop? */
 
159
  for (i = desktop->children->len - 1; i >= 0; i--)
 
160
  {
 
161
    AtspiAccessible *child = g_ptr_array_index (desktop->children, i);
 
162
    g_object_run_dispose (G_OBJECT (child->parent.app));
 
163
    g_object_run_dispose (G_OBJECT (child));
 
164
  }
 
165
 
 
166
  g_object_run_dispose (G_OBJECT (desktop->parent.app));
 
167
  g_object_unref (desktop);
 
168
  desktop = NULL;
 
169
}
 
170
 
 
171
static gboolean atspi_inited = FALSE;
 
172
 
 
173
static GHashTable *app_hash = NULL;
 
174
 
 
175
static void
 
176
handle_get_bus_address (DBusPendingCall *pending, void *user_data)
 
177
{
 
178
  AtspiApplication *app = user_data;
 
179
  DBusMessage *reply = dbus_pending_call_steal_reply (pending);
 
180
  DBusMessage *message;
 
181
  const char *address;
 
182
  DBusPendingCall *new_pending;
 
183
 
 
184
  if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
 
185
  {
 
186
    if (dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &address,
 
187
                               DBUS_TYPE_INVALID) && address [0])
 
188
    {
 
189
      DBusError error;
 
190
      DBusConnection *bus;
 
191
 
 
192
      dbus_error_init (&error);
 
193
      bus = dbus_connection_open_private (address, &error);
 
194
      if (bus)
 
195
      {
 
196
        if (app->bus)
 
197
          {
 
198
            dbus_connection_unref (app->bus);
 
199
          }
 
200
        app->bus = bus;
 
201
      }
 
202
      else
 
203
      {
 
204
        g_warning ("Unable to open bus connection: %s", error.message);
 
205
        dbus_error_free (&error);
 
206
      }
 
207
    }
 
208
  }
 
209
  dbus_message_unref (reply);
 
210
  dbus_pending_call_unref (pending);
 
211
 
 
212
  if (!app->bus)
 
213
    return; /* application has gone away / been disposed */
 
214
 
 
215
  message = dbus_message_new_method_call (app->bus_name,
 
216
                                          "/org/a11y/atspi/cache",
 
217
                                          atspi_interface_cache, "GetItems");
 
218
 
 
219
  dbus_connection_send_with_reply (app->bus, message, &new_pending, 2000);
 
220
  dbus_message_unref (message);
 
221
  if (!new_pending)
 
222
    return;
 
223
  dbus_pending_call_set_notify (new_pending, handle_get_items, app, NULL);
 
224
}
 
225
 
 
226
static AtspiApplication *
 
227
get_application (const char *bus_name)
 
228
{
 
229
  AtspiApplication *app = NULL;
 
230
  char *bus_name_dup;
 
231
  DBusMessage *message;
 
232
  DBusPendingCall *pending = NULL;
 
233
 
 
234
  if (!app_hash)
 
235
  {
 
236
    app_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_object_unref);
 
237
    if (!app_hash) return NULL;
 
238
  }
 
239
  app = g_hash_table_lookup (app_hash, bus_name);
 
240
  if (app) return app;
 
241
  bus_name_dup = g_strdup (bus_name);
 
242
  if (!bus_name_dup) return NULL;
 
243
  // TODO: change below to something that will send state-change:defunct notification if necessary */
 
244
  app = _atspi_application_new (bus_name);
 
245
  app->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 
246
  app->bus = dbus_connection_ref (_atspi_bus ());
 
247
  gettimeofday (&app->time_added, NULL);
 
248
  app->cache = ATSPI_CACHE_UNDEFINED;
 
249
  g_hash_table_insert (app_hash, bus_name_dup, app);
 
250
  message = dbus_message_new_method_call (bus_name, atspi_path_root,
 
251
                                          atspi_interface_application, "GetApplicationBusAddress");
 
252
 
 
253
  dbus_connection_send_with_reply (app->bus, message, &pending, 2000);
 
254
  dbus_message_unref (message);
 
255
  if (!pending)
 
256
  {
 
257
    g_hash_table_remove (app_hash, bus_name_dup);
 
258
    return NULL;
 
259
  }
 
260
  dbus_pending_call_set_notify (pending, handle_get_bus_address, app, NULL);
 
261
  return app;
 
262
}
 
263
 
 
264
static AtspiAccessible *
 
265
ref_accessible (const char *app_name, const char *path)
 
266
{
 
267
  AtspiApplication *app;
 
268
  AtspiAccessible *a;
 
269
 
 
270
  if (!strcmp (path, ATSPI_DBUS_PATH_NULL))
 
271
    return NULL;
 
272
 
 
273
  app = get_application (app_name);
 
274
 
 
275
  if (!strcmp (path, "/org/a11y/atspi/accessible/root"))
 
276
  {
 
277
    if (!app->root)
 
278
    {
 
279
      app->root = _atspi_accessible_new (app, atspi_path_root);
 
280
      app->root->accessible_parent = atspi_get_desktop (0);
 
281
      g_ptr_array_add (app->root->accessible_parent->children, g_object_ref (app->root));
 
282
    }
 
283
    return g_object_ref (app->root);
 
284
  }
 
285
 
 
286
  a = g_hash_table_lookup (app->hash, path);
 
287
  if (a)
 
288
  {
 
289
    return g_object_ref (a);
 
290
  }
 
291
  a = _atspi_accessible_new (app, path);
 
292
  if (!a)
 
293
    return NULL;
 
294
  g_hash_table_insert (app->hash, g_strdup (a->parent.path), g_object_ref (a));
 
295
  return a;
 
296
}
 
297
 
 
298
static AtspiHyperlink *
 
299
ref_hyperlink (const char *app_name, const char *path)
 
300
{
 
301
  AtspiApplication *app = get_application (app_name);
 
302
  AtspiHyperlink *hyperlink;
 
303
 
 
304
  if (!strcmp (path, ATSPI_DBUS_PATH_NULL))
 
305
    return NULL;
 
306
 
 
307
  hyperlink = g_hash_table_lookup (app->hash, path);
 
308
  if (hyperlink)
 
309
  {
 
310
    return g_object_ref (hyperlink);
 
311
  }
 
312
  hyperlink = _atspi_hyperlink_new (app, path);
 
313
  g_hash_table_insert (app->hash, g_strdup (hyperlink->parent.path), hyperlink);
 
314
  /* TODO: This should be a weak ref */
 
315
  g_object_ref (hyperlink);     /* for the hash */
 
316
  return hyperlink;
 
317
}
 
318
 
 
319
typedef struct
 
320
{
 
321
  char *path;
 
322
  char *parent;
 
323
  GArray *children;
 
324
  GArray *interfaces;
 
325
  char *name;
 
326
  dbus_uint32_t role;
 
327
  char *description;
 
328
  GArray *state_bitflags;
 
329
} CACHE_ADDITION;
 
330
 
 
331
static DBusHandlerResult
 
332
handle_remove_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
 
333
{
 
334
  const char *sender = dbus_message_get_sender (message);
 
335
  AtspiApplication *app;
 
336
  const char *path;
 
337
  DBusMessageIter iter, iter_struct;
 
338
  const char *signature = dbus_message_get_signature (message);
 
339
  AtspiAccessible *a;
 
340
 
 
341
  if (strcmp (signature, "(so)") != 0)
 
342
  {
 
343
    g_warning ("AT-SPI: Unknown signature %s for RemoveAccessible", signature);
 
344
    return DBUS_HANDLER_RESULT_HANDLED;
 
345
  }
 
346
 
 
347
  dbus_message_iter_init (message, &iter);
 
348
  dbus_message_iter_recurse (&iter, &iter_struct);
 
349
  dbus_message_iter_get_basic (&iter_struct, &sender);
 
350
  dbus_message_iter_next (&iter_struct);
 
351
  dbus_message_iter_get_basic (&iter_struct, &path);
 
352
  app = get_application (sender);
 
353
  a = ref_accessible (sender, path);
 
354
  if (!a)
 
355
    return DBUS_HANDLER_RESULT_HANDLED;
 
356
  g_object_run_dispose (G_OBJECT (a));
 
357
  g_hash_table_remove (app->hash, a->parent.path);
 
358
  g_object_unref (a);   /* unref our own ref */
 
359
  return DBUS_HANDLER_RESULT_HANDLED;
 
360
}
 
361
 
 
362
static DBusHandlerResult
 
363
handle_name_owner_changed (DBusConnection *bus, DBusMessage *message, void *user_data)
 
364
{
 
365
  const char *name, *new, *old;
 
366
  static gboolean registry_lost = FALSE;
 
367
 
 
368
  if (!dbus_message_get_args (message, NULL,
 
369
                              DBUS_TYPE_STRING, &name,
 
370
                              DBUS_TYPE_STRING, &old,
 
371
                              DBUS_TYPE_STRING, &new,
 
372
                              DBUS_TYPE_INVALID))
 
373
  {
 
374
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
375
  }
 
376
 
 
377
  if (!strcmp (name, "org.a11y.atspi.Registry"))
 
378
  {
 
379
    if (registry_lost && !old[0])
 
380
    {
 
381
      _atspi_reregister_event_listeners ();
 
382
      _atspi_reregister_device_listeners ();
 
383
      registry_lost = FALSE;
 
384
    }
 
385
    else if (!new[0])
 
386
      registry_lost = TRUE;
 
387
  }
 
388
  else if (app_hash)
 
389
  {
 
390
    AtspiApplication *app = g_hash_table_lookup (app_hash, old);
 
391
    if (app)
 
392
      g_object_run_dispose (G_OBJECT (app));
 
393
  }
 
394
  return DBUS_HANDLER_RESULT_HANDLED;
 
395
}
 
396
 
 
397
static gboolean
 
398
add_app_to_desktop (AtspiAccessible *a, const char *bus_name)
 
399
{
 
400
  AtspiAccessible *obj = ref_accessible (bus_name, atspi_path_root);
 
401
  /* The app will be added to the desktop as a side-effect of calling
 
402
   * ref_accessible */
 
403
  g_object_unref (obj);
 
404
  return (obj != NULL);
 
405
}
 
406
 
 
407
void
 
408
get_reference_from_iter (DBusMessageIter *iter, const char **app_name, const char **path)
 
409
{
 
410
  DBusMessageIter iter_struct;
 
411
 
 
412
  dbus_message_iter_recurse (iter, &iter_struct);
 
413
  dbus_message_iter_get_basic (&iter_struct, app_name);
 
414
  dbus_message_iter_next (&iter_struct);
 
415
  dbus_message_iter_get_basic (&iter_struct, path);
 
416
  dbus_message_iter_next (iter);
 
417
}
 
418
 
 
419
static void
 
420
add_accessible_from_iter (DBusMessageIter *iter)
 
421
{
 
422
  DBusMessageIter iter_struct, iter_array;
 
423
  const char *app_name, *path;
 
424
  AtspiAccessible *accessible;
 
425
  const char *name, *description;
 
426
  dbus_uint32_t role;
 
427
  gboolean children_cached = FALSE;
 
428
  dbus_int32_t count, index;
 
429
 
 
430
  dbus_message_iter_recurse (iter, &iter_struct);
 
431
 
 
432
  /* get accessible */
 
433
  get_reference_from_iter (&iter_struct, &app_name, &path);
 
434
  accessible = ref_accessible (app_name, path);
 
435
  if (!accessible)
 
436
    return;
 
437
 
 
438
  /* Get application: TODO */
 
439
  dbus_message_iter_next (&iter_struct);
 
440
 
 
441
  /* get parent */
 
442
  get_reference_from_iter (&iter_struct, &app_name, &path);
 
443
  if (accessible->accessible_parent)
 
444
    g_object_unref (accessible->accessible_parent);
 
445
  accessible->accessible_parent = ref_accessible (app_name, path);
 
446
 
 
447
  if (dbus_message_iter_get_arg_type (&iter_struct) == 'i')
 
448
  {
 
449
    /* Get index in parent */
 
450
    dbus_message_iter_get_basic (&iter_struct, &index);
 
451
    if (index >= 0 && accessible->accessible_parent)
 
452
    {
 
453
      if (index >= accessible->accessible_parent->children->len)
 
454
        g_ptr_array_set_size (accessible->accessible_parent->children, index + 1);
 
455
      g_ptr_array_index (accessible->accessible_parent->children, index) = g_object_ref (accessible);
 
456
    }
 
457
 
 
458
    /* get child count */
 
459
    dbus_message_iter_next (&iter_struct);
 
460
    dbus_message_iter_get_basic (&iter_struct, &count);
 
461
    if (count >= 0)
 
462
    {
 
463
      g_ptr_array_set_size (accessible->children, count);
 
464
      children_cached = TRUE;
 
465
    }
 
466
  }
 
467
  else if (dbus_message_iter_get_arg_type (&iter_struct) == 'a')
 
468
  {
 
469
    /* It's the old API with a list of children */
 
470
    /* TODO: Perhaps remove this code eventually */
 
471
    dbus_message_iter_recurse (&iter_struct, &iter_array);
 
472
    while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
 
473
    {
 
474
      AtspiAccessible *child;
 
475
      get_reference_from_iter (&iter_array, &app_name, &path);
 
476
      child = ref_accessible (app_name, path);
 
477
      g_ptr_array_remove (accessible->children, child);
 
478
      g_ptr_array_add (accessible->children, child);
 
479
    }
 
480
    children_cached = TRUE;
 
481
  }
 
482
 
 
483
  /* interfaces */
 
484
  dbus_message_iter_next (&iter_struct);
 
485
  _atspi_dbus_set_interfaces (accessible, &iter_struct);
 
486
  dbus_message_iter_next (&iter_struct);
 
487
 
 
488
  /* name */
 
489
  if (accessible->name)
 
490
    g_free (accessible->name);
 
491
  dbus_message_iter_get_basic (&iter_struct, &name);
 
492
  accessible->name = g_strdup (name);
 
493
  dbus_message_iter_next (&iter_struct);
 
494
 
 
495
  /* role */
 
496
  dbus_message_iter_get_basic (&iter_struct, &role);
 
497
  accessible->role = role;
 
498
  dbus_message_iter_next (&iter_struct);
 
499
 
 
500
  /* description */
 
501
  if (accessible->description)
 
502
    g_free (accessible->description);
 
503
  dbus_message_iter_get_basic (&iter_struct, &description);
 
504
  accessible->description = g_strdup (description);
 
505
  dbus_message_iter_next (&iter_struct);
 
506
 
 
507
  _atspi_dbus_set_state (accessible, &iter_struct);
 
508
  dbus_message_iter_next (&iter_struct);
 
509
 
 
510
  _atspi_accessible_add_cache (accessible, ATSPI_CACHE_NAME | ATSPI_CACHE_ROLE |
 
511
                               ATSPI_CACHE_PARENT | ATSPI_CACHE_DESCRIPTION);
 
512
  if (!atspi_state_set_contains (accessible->states,
 
513
                                       ATSPI_STATE_MANAGES_DESCENDANTS) &&
 
514
                                       children_cached)
 
515
    _atspi_accessible_add_cache (accessible, ATSPI_CACHE_CHILDREN);
 
516
 
 
517
  /* This is a bit of a hack since the cache holds a ref, so we don't need
 
518
   * the one provided for us anymore */
 
519
  g_object_unref (accessible);
 
520
}
 
521
 
 
522
static void
 
523
handle_get_items (DBusPendingCall *pending, void *user_data)
 
524
{
 
525
  DBusMessage *reply = dbus_pending_call_steal_reply (pending);
 
526
  DBusMessageIter iter, iter_array;
 
527
 
 
528
  if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
 
529
  {
 
530
    const char *sender = dbus_message_get_sender (reply);
 
531
    const char *error = NULL;
 
532
    dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &error,
 
533
                           DBUS_TYPE_INVALID);
 
534
    g_warning ("AT-SPI: Error in GetItems, sender=%s, error=%s", sender, error);
 
535
    dbus_message_unref (reply);
 
536
    dbus_pending_call_unref (pending);
 
537
    return;
 
538
  }
 
539
 
 
540
  dbus_message_iter_init (reply, &iter);
 
541
  dbus_message_iter_recurse (&iter, &iter_array);
 
542
  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
 
543
  {
 
544
    add_accessible_from_iter (&iter_array);
 
545
    dbus_message_iter_next (&iter_array);
 
546
  }
 
547
  dbus_message_unref (reply);
 
548
  dbus_pending_call_unref (pending);
 
549
}
 
550
 
 
551
/* TODO: Do we stil need this function? */
 
552
static AtspiAccessible *
 
553
ref_accessible_desktop (AtspiApplication *app)
 
554
{
 
555
  GError *error;
 
556
  DBusMessage *message, *reply;
 
557
  DBusMessageIter iter, iter_array;
 
558
  gchar *bus_name_dup;
 
559
 
 
560
  if (desktop)
 
561
  {
 
562
    g_object_ref (desktop);
 
563
    return desktop;
 
564
  }
 
565
  desktop = _atspi_accessible_new (app, atspi_path_root);
 
566
  if (!desktop)
 
567
  {
 
568
    return NULL;
 
569
  }
 
570
  g_hash_table_insert (app->hash, g_strdup (desktop->parent.path),
 
571
                       g_object_ref (desktop));
 
572
  app->root = g_object_ref (desktop);
 
573
  desktop->name = g_strdup ("main");
 
574
  message = dbus_message_new_method_call (atspi_bus_registry,
 
575
        atspi_path_root,
 
576
        atspi_interface_accessible,
 
577
        "GetChildren");
 
578
  if (!message)
 
579
    return NULL;
 
580
  error = NULL;
 
581
  reply = _atspi_dbus_send_with_reply_and_block (message, &error);
 
582
  if (!reply || strcmp (dbus_message_get_signature (reply), "a(so)") != 0)
 
583
  {
 
584
    if (error != NULL)
 
585
    {
 
586
      g_warning ("Couldn't get application list: %s", error->message);
 
587
      g_clear_error (&error);
 
588
    }
 
589
    if (reply)
 
590
      dbus_message_unref (reply);
 
591
    return NULL;
 
592
  }
 
593
  dbus_message_iter_init (reply, &iter);
 
594
  dbus_message_iter_recurse (&iter, &iter_array);
 
595
  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
 
596
  {
 
597
    const char *app_name, *path;
 
598
    get_reference_from_iter (&iter_array, &app_name, &path);
 
599
    add_app_to_desktop (desktop, app_name);
 
600
  }
 
601
  dbus_message_unref (reply);
 
602
 
 
603
  /* Record the alternate name as an alias for org.a11y.atspi.Registry */
 
604
  bus_name_dup = g_strdup (dbus_message_get_sender (reply));
 
605
  if (bus_name_dup)
 
606
    g_hash_table_insert (app_hash, bus_name_dup, app);
 
607
 
 
608
  return g_object_ref (desktop);
 
609
}
 
610
 
 
611
AtspiAccessible *
 
612
_atspi_ref_accessible (const char *app, const char *path)
 
613
{
 
614
  AtspiApplication *a = get_application (app);
 
615
  if (!a)
 
616
    return NULL;
 
617
  if ( APP_IS_REGISTRY(a))
 
618
  {
 
619
    if (!a->root)
 
620
      g_object_unref (ref_accessible_desktop (a));      /* sets a->root */
 
621
    return g_object_ref (a->root);
 
622
  }
 
623
  return ref_accessible (app, path);
 
624
}
 
625
 
 
626
AtspiAccessible *
 
627
_atspi_dbus_return_accessible_from_message (DBusMessage *message)
 
628
{
 
629
  DBusMessageIter iter;
 
630
  AtspiAccessible *retval = NULL;
 
631
  const char *signature;
 
632
 
 
633
  if (!message)
 
634
    return NULL;
 
635
 
 
636
  signature = dbus_message_get_signature (message);
 
637
  if (!strcmp (signature, "(so)"))
 
638
  {
 
639
    dbus_message_iter_init (message, &iter);
 
640
    retval =  _atspi_dbus_return_accessible_from_iter (&iter);
 
641
  }
 
642
  else
 
643
  {
 
644
    g_warning ("AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange signature %s", signature);
 
645
  }
 
646
  dbus_message_unref (message);
 
647
  return retval;
 
648
}
 
649
 
 
650
AtspiAccessible *
 
651
_atspi_dbus_return_accessible_from_iter (DBusMessageIter *iter)
 
652
{
 
653
  const char *app_name, *path;
 
654
 
 
655
  get_reference_from_iter (iter, &app_name, &path);
 
656
  return ref_accessible (app_name, path);
 
657
}
 
658
 
 
659
AtspiHyperlink *
 
660
_atspi_dbus_return_hyperlink_from_message (DBusMessage *message)
 
661
{
 
662
  DBusMessageIter iter;
 
663
  AtspiHyperlink *retval = NULL;
 
664
  const char *signature;
 
665
   
 
666
  if (!message)
 
667
    return NULL;
 
668
 
 
669
  signature = dbus_message_get_signature (message);
 
670
  if (!strcmp (signature, "(so)"))
 
671
  {
 
672
    dbus_message_iter_init (message, &iter);
 
673
    retval =  _atspi_dbus_return_hyperlink_from_iter (&iter);
 
674
  }
 
675
  else
 
676
  {
 
677
    g_warning ("AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange signature %s", signature);
 
678
  }
 
679
  dbus_message_unref (message);
 
680
  return retval;
 
681
}
 
682
 
 
683
AtspiHyperlink *
 
684
_atspi_dbus_return_hyperlink_from_iter (DBusMessageIter *iter)
 
685
{
 
686
  const char *app_name, *path;
 
687
 
 
688
  get_reference_from_iter (iter, &app_name, &path);
 
689
  return ref_hyperlink (app_name, path);
 
690
}
 
691
 
 
692
const char *cache_signal_type = "((so)(so)(so)iiassusau)";
 
693
const char *old_cache_signal_type = "((so)(so)(so)a(so)assusau)";
 
694
 
 
695
static DBusHandlerResult
 
696
handle_add_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
 
697
{
 
698
  DBusMessageIter iter;
 
699
  const char *signature = dbus_message_get_signature (message);
 
700
 
 
701
  if (strcmp (signature, cache_signal_type) != 0 &&
 
702
      strcmp (signature, old_cache_signal_type) != 0)
 
703
  {
 
704
    g_warning ("AT-SPI: AddAccessible with unknown signature %s\n", signature);
 
705
    return DBUS_HANDLER_RESULT_HANDLED;
 
706
  }
 
707
 
 
708
  dbus_message_iter_init (message, &iter);
 
709
  add_accessible_from_iter (&iter);
 
710
  return DBUS_HANDLER_RESULT_HANDLED;
 
711
}
 
712
 
 
713
typedef struct
 
714
{
 
715
  DBusConnection *bus;
 
716
  DBusMessage *message;
 
717
  void *data;
 
718
} BusDataClosure;
 
719
 
 
720
static GSource *process_deferred_messages_source = NULL;
 
721
 
 
722
static void
 
723
process_deferred_message (BusDataClosure *closure)
 
724
{
 
725
  int type = dbus_message_get_type (closure->message);
 
726
  const char *interface = dbus_message_get_interface (closure->message);
 
727
 
 
728
  if (type == DBUS_MESSAGE_TYPE_SIGNAL &&
 
729
      !strncmp (interface, "org.a11y.atspi.Event.", 21))
 
730
  {
 
731
    _atspi_dbus_handle_event (closure->bus, closure->message, closure->data);
 
732
  }
 
733
  if (dbus_message_is_method_call (closure->message, atspi_interface_device_event_listener, "NotifyEvent"))
 
734
  {
 
735
    _atspi_dbus_handle_DeviceEvent (closure->bus,
 
736
                                   closure->message, closure->data);
 
737
  }
 
738
  if (dbus_message_is_signal (closure->message, atspi_interface_cache, "AddAccessible"))
 
739
  {
 
740
    handle_add_accessible (closure->bus, closure->message, closure->data);
 
741
  }
 
742
  if (dbus_message_is_signal (closure->message, atspi_interface_cache, "RemoveAccessible"))
 
743
  {
 
744
    handle_remove_accessible (closure->bus, closure->message, closure->data);
 
745
  }
 
746
  if (dbus_message_is_signal (closure->message, "org.freedesktop.DBus", "NameOwnerChanged"))
 
747
  {
 
748
    handle_name_owner_changed (closure->bus, closure->message, closure->data);
 
749
  }
 
750
}
 
751
 
 
752
static GQueue *deferred_messages = NULL;
 
753
 
 
754
static gboolean
 
755
process_deferred_messages (void)
 
756
{
 
757
  static int in_process_deferred_messages = 0;
 
758
  BusDataClosure *closure;
 
759
 
 
760
  if (in_process_deferred_messages)
 
761
    return TRUE;
 
762
  in_process_deferred_messages = 1;
 
763
  while ((closure = g_queue_pop_head (deferred_messages)))
 
764
  {
 
765
    process_deferred_message (closure);
 
766
    dbus_message_unref (closure->message);
 
767
    dbus_connection_unref (closure->bus);
 
768
    g_free (closure);
 
769
  }
 
770
  in_process_deferred_messages = 0;
 
771
  return FALSE;
 
772
}
 
773
 
 
774
static gboolean
 
775
process_deferred_messages_callback (gpointer data)
 
776
{
 
777
  if (process_deferred_messages ())
 
778
    return G_SOURCE_CONTINUE;
 
779
 
 
780
  process_deferred_messages_source = NULL;
 
781
  return G_SOURCE_REMOVE;
 
782
}
 
783
 
 
784
static DBusHandlerResult
 
785
defer_message (DBusConnection *connection, DBusMessage *message, void *user_data)
 
786
{
 
787
  BusDataClosure *closure = g_new (BusDataClosure, 1);
 
788
 
 
789
  closure->bus = dbus_connection_ref (bus);
 
790
  closure->message = dbus_message_ref (message);
 
791
  closure->data = user_data;
 
792
 
 
793
  g_queue_push_tail (deferred_messages, closure);
 
794
 
 
795
  if (process_deferred_messages_source == NULL)
 
796
  {
 
797
    process_deferred_messages_source = g_idle_source_new ();
 
798
    g_source_set_callback (process_deferred_messages_source,
 
799
                           process_deferred_messages_callback, NULL, NULL);
 
800
    g_source_attach (process_deferred_messages_source, atspi_main_context);
 
801
    g_source_unref (process_deferred_messages_source);
 
802
  }
 
803
 
 
804
  return DBUS_HANDLER_RESULT_HANDLED;
 
805
}
 
806
 
 
807
static DBusHandlerResult
 
808
atspi_dbus_filter (DBusConnection *bus, DBusMessage *message, void *data)
 
809
{
 
810
  int type = dbus_message_get_type (message);
 
811
  const char *interface = dbus_message_get_interface (message);
 
812
 
 
813
  if (type == DBUS_MESSAGE_TYPE_SIGNAL &&
 
814
      !strncmp (interface, "org.a11y.atspi.Event.", 21))
 
815
  {
 
816
    return defer_message (bus, message, data);
 
817
  }
 
818
  if (dbus_message_is_method_call (message, atspi_interface_device_event_listener, "NotifyEvent"))
 
819
  {
 
820
    return defer_message (bus, message, data);
 
821
  }
 
822
  if (dbus_message_is_signal (message, atspi_interface_cache, "AddAccessible"))
 
823
  {
 
824
    return defer_message (bus, message, data);
 
825
  }
 
826
  if (dbus_message_is_signal (message, atspi_interface_cache, "RemoveAccessible"))
 
827
  {
 
828
    return defer_message (bus, message, data);
 
829
  }
 
830
  if (dbus_message_is_signal (message, "org.freedesktop.DBus", "NameOwnerChanged"))
 
831
  {
 
832
    defer_message (bus, message, data);
 
833
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
834
  }
 
835
  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
836
}
 
837
 
 
838
/*
 
839
 * Returns a 'canonicalized' value for DISPLAY,
 
840
 * with the screen number stripped off if present.
 
841
 *
 
842
 * TODO: Avoid having duplicate functions for this here and in at-spi2-atk
 
843
 */
 
844
static gchar *
 
845
spi_display_name (void)
 
846
{
 
847
  char *canonical_display_name = NULL;
 
848
  const gchar *display_env = g_getenv ("AT_SPI_DISPLAY");
 
849
 
 
850
  if (!display_env)
 
851
    {
 
852
      display_env = g_getenv ("DISPLAY");
 
853
      if (!display_env || !display_env[0])
 
854
        return NULL;
 
855
      else
 
856
        {
 
857
          gchar *display_p, *screen_p;
 
858
          canonical_display_name = g_strdup (display_env);
 
859
          display_p = g_utf8_strrchr (canonical_display_name, -1, ':');
 
860
          screen_p = g_utf8_strrchr (canonical_display_name, -1, '.');
 
861
          if (screen_p && display_p && (screen_p > display_p))
 
862
            {
 
863
              *screen_p = '\0';
 
864
            }
 
865
        }
 
866
    }
 
867
  else
 
868
    {
 
869
      canonical_display_name = g_strdup (display_env);
 
870
    }
 
871
 
 
872
  return canonical_display_name;
 
873
}
 
874
 
 
875
/**
 
876
 * atspi_init:
 
877
 *
 
878
 * Connects to the accessibility registry and initializes the SPI.
 
879
 *
 
880
 * Returns: 0 on success, 1 if already initialized, or an integer error code.  
 
881
 **/
 
882
int
 
883
atspi_init (void)
 
884
{
 
885
  char *match;
 
886
  const gchar *no_cache;
 
887
 
 
888
  if (atspi_inited)
 
889
    {
 
890
      return 1;
 
891
    }
 
892
 
 
893
  atspi_inited = TRUE;
 
894
 
 
895
  _atspi_get_live_refs();
 
896
 
 
897
  bus = atspi_get_a11y_bus ();
 
898
  if (!bus)
 
899
    return 2;
 
900
  dbus_bus_register (bus, NULL);
 
901
  atspi_dbus_connection_setup_with_g_main(bus, g_main_context_default());
 
902
  dbus_connection_add_filter (bus, atspi_dbus_filter, NULL, NULL);
 
903
  match = g_strdup_printf ("type='signal',interface='%s',member='AddAccessible'", atspi_interface_cache);
 
904
  dbus_bus_add_match (bus, match, NULL);
 
905
  g_free (match);
 
906
  match = g_strdup_printf ("type='signal',interface='%s',member='RemoveAccessible'", atspi_interface_cache);
 
907
  dbus_bus_add_match (bus, match, NULL);
 
908
  g_free (match);
 
909
  match = g_strdup_printf ("type='signal',interface='%s',member='ChildrenChanged'", atspi_interface_event_object);
 
910
  dbus_bus_add_match (bus, match, NULL);
 
911
  g_free (match);
 
912
  match = g_strdup_printf ("type='signal',interface='%s',member='PropertyChange'", atspi_interface_event_object);
 
913
  dbus_bus_add_match (bus, match, NULL);
 
914
  g_free (match);
 
915
  match = g_strdup_printf ("type='signal',interface='%s',member='StateChanged'", atspi_interface_event_object);
 
916
  dbus_bus_add_match (bus, match, NULL);
 
917
  g_free (match);
 
918
 
 
919
  dbus_bus_add_match (bus,
 
920
                      "type='signal', interface='org.freedesktop.DBus', member='NameOwnerChanged'",
 
921
                      NULL);
 
922
 
 
923
  no_cache = g_getenv ("ATSPI_NO_CACHE");
 
924
  if (no_cache && g_strcmp0 (no_cache, "0") != 0)
 
925
    atspi_no_cache = TRUE;
 
926
 
 
927
  deferred_messages = g_queue_new ();
 
928
 
 
929
  return 0;
 
930
}
 
931
 
 
932
/**
 
933
 * atspi_is_initialized:
 
934
 *
 
935
 * Indicates whether AT-SPI has been initialized.
 
936
 *
 
937
 * Returns: %True if initialized; %False otherwise.
 
938
 */
 
939
gboolean
 
940
atspi_is_initialized ()
 
941
{
 
942
  return atspi_inited;
 
943
}
 
944
 
 
945
/**
 
946
 * atspi_event_main:
 
947
 *
 
948
 * Starts/enters the main event loop for the AT-SPI services.
 
949
 *
 
950
 * NOTE: This method does not return control; it is exited via a call to
 
951
 * #atspi_event_quit from within an event handler.
 
952
 *
 
953
 **/
 
954
void
 
955
atspi_event_main (void)
 
956
{
 
957
  atspi_main_loop = g_main_loop_new (NULL, FALSE);
 
958
  g_main_loop_run (atspi_main_loop);
 
959
  atspi_main_loop = NULL;
 
960
}
 
961
 
 
962
/**
 
963
 * atspi_event_quit:
 
964
 *
 
965
 * Quits the last main event loop for the AT-SPI services,
 
966
 * See: #atspi_event_main
 
967
 **/
 
968
void
 
969
atspi_event_quit (void)
 
970
{
 
971
  g_main_loop_quit (atspi_main_loop);
 
972
}
 
973
 
 
974
/**
 
975
 * atspi_exit:
 
976
 *
 
977
 * Disconnects from #AtspiRegistry instances and releases 
 
978
 * any floating resources. Call only once at exit.
 
979
 *
 
980
 * Returns: 0 if there were no leaks, otherwise other integer values.
 
981
 **/
 
982
int
 
983
atspi_exit (void)
 
984
{
 
985
  int leaked;
 
986
 
 
987
  if (!atspi_inited)
 
988
    {
 
989
      return 0;
 
990
    }
 
991
 
 
992
  atspi_inited = FALSE;
 
993
 
 
994
  if (live_refs)
 
995
    {
 
996
      leaked = g_hash_table_size (live_refs);
 
997
    }
 
998
  else
 
999
    {
 
1000
      leaked = 0;
 
1001
    }
 
1002
 
 
1003
  cleanup ();
 
1004
 
 
1005
  return leaked;
 
1006
}
 
1007
 
 
1008
static GSList *hung_processes;
 
1009
 
 
1010
static void
 
1011
remove_hung_process (DBusPendingCall *pending, void *data)
 
1012
{
 
1013
 
 
1014
  hung_processes = g_slist_remove (hung_processes, data);
 
1015
  g_free (data);
 
1016
  dbus_pending_call_unref (pending);
 
1017
}
 
1018
 
 
1019
static void
 
1020
check_for_hang (DBusMessage *message, DBusError *error, DBusConnection *bus, const char *bus_name)
 
1021
{
 
1022
  if (!message && error->name &&
 
1023
      !strcmp (error->name, "org.freedesktop.DBus.Error.NoReply"))
 
1024
  {
 
1025
    GSList *l;
 
1026
    DBusMessage *message;
 
1027
    gchar *bus_name_dup;
 
1028
    DBusPendingCall *pending = NULL;
 
1029
    for (l = hung_processes; l; l = l->next)
 
1030
      if (!strcmp (l->data, bus_name))
 
1031
        return;
 
1032
    message = dbus_message_new_method_call (bus_name, "/",
 
1033
                                            "org.freedesktop.DBus.Peer",
 
1034
                                            "Ping");
 
1035
    if (!message)
 
1036
      return;
 
1037
    dbus_connection_send_with_reply (bus, message, &pending, -1);
 
1038
    dbus_message_unref (message);
 
1039
    if (!pending)
 
1040
      return;
 
1041
    bus_name_dup = g_strdup (bus_name);
 
1042
    hung_processes = g_slist_append (hung_processes, bus_name_dup);
 
1043
    dbus_pending_call_set_notify (pending, remove_hung_process, bus_name_dup, NULL);
 
1044
  }
 
1045
}
 
1046
 
 
1047
static gboolean
 
1048
connection_is_hung (const char *bus_name)
 
1049
{
 
1050
  GSList *l;
 
1051
 
 
1052
  for (l = hung_processes; l; l = l->next)
 
1053
    if (!strcmp (l->data, bus_name))
 
1054
      return TRUE;
 
1055
  return FALSE;
 
1056
}
 
1057
 
 
1058
static gboolean
 
1059
check_app (AtspiApplication *app, GError **error)
 
1060
{
 
1061
  if (!app || !app->bus)
 
1062
  {
 
1063
    g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_APPLICATION_GONE,
 
1064
                          _("The application no longer exists"));
 
1065
    return FALSE;
 
1066
  }
 
1067
 
 
1068
  if (atspi_main_loop && connection_is_hung (app->bus_name))
 
1069
  {
 
1070
      g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC,
 
1071
                           "The process appears to be hung.");
 
1072
    return FALSE;
 
1073
  }
 
1074
 
 
1075
  return TRUE;
 
1076
}
 
1077
 
 
1078
static void
 
1079
set_timeout (AtspiApplication *app)
 
1080
{
 
1081
  struct timeval tv;
 
1082
  int diff;
 
1083
 
 
1084
  if (app && app_startup_time > 0)
 
1085
  {
 
1086
    gettimeofday (&tv, NULL);
 
1087
    diff = (tv.tv_sec - app->time_added.tv_sec) * 1000 + (tv.tv_usec - app->time_added.tv_usec) / 1000;
 
1088
    dbind_set_timeout (MAX(method_call_timeout, app_startup_time - diff));
 
1089
  }
 
1090
  else
 
1091
    dbind_set_timeout (method_call_timeout);
 
1092
}
 
1093
 
 
1094
dbus_bool_t
 
1095
_atspi_dbus_call (gpointer obj, const char *interface, const char *method, GError **error, const char *type, ...)
 
1096
{
 
1097
  va_list args;
 
1098
  dbus_bool_t retval;
 
1099
  DBusError err;
 
1100
  AtspiObject *aobj = ATSPI_OBJECT (obj);
 
1101
 
 
1102
  if (!check_app (aobj->app, error))
 
1103
    return FALSE;
 
1104
 
 
1105
  if (!allow_sync)
 
1106
  {
 
1107
    _atspi_set_error_no_sync (error);
 
1108
    return FALSE;
 
1109
  }
 
1110
 
 
1111
  va_start (args, type);
 
1112
  dbus_error_init (&err);
 
1113
  set_timeout (aobj->app);
 
1114
  retval = dbind_method_call_reentrant_va (aobj->app->bus, aobj->app->bus_name,
 
1115
                                           aobj->path, interface, method, &err,
 
1116
                                           type, args);
 
1117
  va_end (args);
 
1118
  check_for_hang (NULL, &err, aobj->app->bus, aobj->app->bus_name);
 
1119
  process_deferred_messages ();
 
1120
  if (dbus_error_is_set (&err))
 
1121
  {
 
1122
    g_set_error(error, ATSPI_ERROR, ATSPI_ERROR_IPC, "%s", err.message);
 
1123
    dbus_error_free (&err);
 
1124
  }
 
1125
  return retval;
 
1126
}
 
1127
 
 
1128
DBusMessage *
 
1129
_atspi_dbus_call_partial (gpointer obj,
 
1130
                          const char *interface,
 
1131
                          const char *method,
 
1132
                          GError **error,
 
1133
                          const char *type, ...)
 
1134
{
 
1135
  va_list args;
 
1136
 
 
1137
  va_start (args, type);
 
1138
  return _atspi_dbus_call_partial_va (obj, interface, method, error, type, args);
 
1139
}
 
1140
 
 
1141
 
 
1142
DBusMessage *
 
1143
_atspi_dbus_call_partial_va (gpointer obj,
 
1144
                          const char *interface,
 
1145
                          const char *method,
 
1146
                          GError **error,
 
1147
                          const char *type,
 
1148
                          va_list args)
 
1149
{
 
1150
  AtspiObject *aobj = ATSPI_OBJECT (obj);
 
1151
  DBusError err;
 
1152
    DBusMessage *msg = NULL, *reply = NULL;
 
1153
    DBusMessageIter iter;
 
1154
    const char *p;
 
1155
 
 
1156
  dbus_error_init (&err);
 
1157
 
 
1158
  if (!check_app (aobj->app, error))
 
1159
    goto out;
 
1160
 
 
1161
  msg = dbus_message_new_method_call (aobj->app->bus_name, aobj->path, interface, method);
 
1162
  if (!msg)
 
1163
    goto out;
 
1164
 
 
1165
  p = type;
 
1166
  dbus_message_iter_init_append (msg, &iter);
 
1167
  dbind_any_marshal_va (&iter, &p, args);
 
1168
 
 
1169
  set_timeout (aobj->app);
 
1170
  reply = dbind_send_and_allow_reentry (aobj->app->bus, msg, &err);
 
1171
  check_for_hang (reply, &err, aobj->app->bus, aobj->app->bus_name);
 
1172
out:
 
1173
  va_end (args);
 
1174
  if (msg)
 
1175
    dbus_message_unref (msg);
 
1176
  process_deferred_messages ();
 
1177
  if (dbus_error_is_set (&err))
 
1178
  {
 
1179
    /* TODO: Set gerror */
 
1180
    dbus_error_free (&err);
 
1181
  }
 
1182
 
 
1183
  if (reply && dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
 
1184
  {
 
1185
    const char *err_str = NULL;
 
1186
    dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &err_str, DBUS_TYPE_INVALID);
 
1187
    if (err_str)
 
1188
      g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err_str);
 
1189
    dbus_message_unref (reply);
 
1190
    return NULL;
 
1191
  }
 
1192
 
 
1193
  return reply;
 
1194
}
 
1195
 
 
1196
dbus_bool_t
 
1197
_atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, GError **error, const char *type, void *data)
 
1198
{
 
1199
  DBusMessage *message, *reply;
 
1200
  DBusMessageIter iter, iter_variant;
 
1201
  DBusError err;
 
1202
  dbus_bool_t retval = FALSE;
 
1203
  AtspiObject *aobj = ATSPI_OBJECT (obj);
 
1204
  char expected_type = (type [0] == '(' ? 'r' : type [0]);
 
1205
 
 
1206
  if (!aobj)
 
1207
    return FALSE;
 
1208
 
 
1209
  if (!check_app (aobj->app, error))
 
1210
    return FALSE;
 
1211
 
 
1212
  if (!allow_sync)
 
1213
  {
 
1214
    _atspi_set_error_no_sync (error);
 
1215
    return FALSE;
 
1216
  }
 
1217
 
 
1218
  message = dbus_message_new_method_call (aobj->app->bus_name,
 
1219
                                          aobj->path,
 
1220
                                          "org.freedesktop.DBus.Properties",
 
1221
                                          "Get");
 
1222
  if (!message)
 
1223
  {
 
1224
    // TODO: throw exception
 
1225
    return FALSE;
 
1226
  }
 
1227
  dbus_message_append_args (message, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
 
1228
  dbus_error_init (&err);
 
1229
  set_timeout (aobj->app);
 
1230
  reply = dbind_send_and_allow_reentry (aobj->app->bus, message, &err);
 
1231
  check_for_hang (reply, &err, aobj->app->bus, aobj->app->bus_name);
 
1232
  dbus_message_unref (message);
 
1233
  process_deferred_messages ();
 
1234
  if (!reply)
 
1235
  {
 
1236
    // TODO: throw exception
 
1237
    goto done;
 
1238
  }
 
1239
 
 
1240
  if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
 
1241
  {
 
1242
    const char *err_str = NULL;
 
1243
    dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &err_str, DBUS_TYPE_INVALID);
 
1244
    if (err_str)
 
1245
      g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err_str);
 
1246
    goto done;
 
1247
  }
 
1248
 
 
1249
  dbus_message_iter_init (reply, &iter);
 
1250
  if (dbus_message_iter_get_arg_type (&iter) != 'v')
 
1251
  {
 
1252
    g_warning ("AT-SPI: expected a variant when fetching %s from interface %s; got %s\n", name, interface, dbus_message_get_signature (reply));
 
1253
    goto done;
 
1254
  }
 
1255
  dbus_message_iter_recurse (&iter, &iter_variant);
 
1256
  if (dbus_message_iter_get_arg_type (&iter_variant) != expected_type)
 
1257
  {
 
1258
    g_warning ("atspi_dbus_get_property: Wrong type: expected %s, got %c\n", type, dbus_message_iter_get_arg_type (&iter_variant));
 
1259
    goto done;
 
1260
  }
 
1261
  if (!strcmp (type, "(so)"))
 
1262
  {
 
1263
    *((AtspiAccessible **)data) = _atspi_dbus_return_accessible_from_iter (&iter_variant);
 
1264
  }
 
1265
  else
 
1266
  {
 
1267
    dbus_message_iter_get_basic (&iter_variant, data);
 
1268
    if (type [0] == 's')
 
1269
      *(char **)data = g_strdup (*(char **)data);
 
1270
  }
 
1271
  retval = TRUE;
 
1272
done:
 
1273
  dbus_error_free (&err);
 
1274
  if (reply)
 
1275
    dbus_message_unref (reply);
 
1276
  return retval;
 
1277
}
 
1278
 
 
1279
DBusMessage *
 
1280
_atspi_dbus_send_with_reply_and_block (DBusMessage *message, GError **error)
 
1281
{
 
1282
  DBusMessage *reply;
 
1283
  DBusError err;
 
1284
  AtspiApplication *app;
 
1285
  DBusConnection *bus;
 
1286
 
 
1287
  app = get_application (dbus_message_get_destination (message));
 
1288
 
 
1289
  if (app && !app->bus)
 
1290
    return NULL;        /* will fail anyway; app has been disposed */
 
1291
 
 
1292
  bus = (app ? app->bus : _atspi_bus());
 
1293
  dbus_error_init (&err);
 
1294
  set_timeout (app);
 
1295
  reply = dbind_send_and_allow_reentry (bus, message, &err);
 
1296
  process_deferred_messages ();
 
1297
  dbus_message_unref (message);
 
1298
  if (dbus_error_is_set (&err))
 
1299
  {
 
1300
    if (error)
 
1301
      g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_IPC, err.message);
 
1302
    dbus_error_free (&err);
 
1303
  }
 
1304
  return reply;
 
1305
}
 
1306
 
 
1307
GHashTable *
 
1308
_atspi_dbus_return_hash_from_message (DBusMessage *message)
 
1309
{
 
1310
  DBusMessageIter iter;
 
1311
  GHashTable *ret;
 
1312
 
 
1313
  if (!message)
 
1314
    return NULL;
 
1315
 
 
1316
  _ATSPI_DBUS_CHECK_SIG (message, "a{ss}", NULL, NULL);
 
1317
 
 
1318
  dbus_message_iter_init (message, &iter);
 
1319
  ret = _atspi_dbus_hash_from_iter (&iter);
 
1320
  dbus_message_unref (message);
 
1321
  return ret;
 
1322
}
 
1323
 
 
1324
GHashTable *
 
1325
_atspi_dbus_hash_from_iter (DBusMessageIter *iter)
 
1326
{
 
1327
  GHashTable *hash = g_hash_table_new_full (g_str_hash, g_str_equal,
 
1328
                                            (GDestroyNotify) g_free,
 
1329
                                            (GDestroyNotify) g_free);
 
1330
  DBusMessageIter iter_array, iter_dict;
 
1331
 
 
1332
  dbus_message_iter_recurse (iter, &iter_array);
 
1333
  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
 
1334
  {
 
1335
    const char *name, *value;
 
1336
    dbus_message_iter_recurse (&iter_array, &iter_dict);
 
1337
    dbus_message_iter_get_basic (&iter_dict, &name);
 
1338
    dbus_message_iter_next (&iter_dict);
 
1339
    dbus_message_iter_get_basic (&iter_dict, &value);
 
1340
    g_hash_table_insert (hash, g_strdup (name), g_strdup (value));
 
1341
    dbus_message_iter_next (&iter_array);
 
1342
  }
 
1343
  return hash;
 
1344
}
 
1345
 
 
1346
GArray *
 
1347
_atspi_dbus_return_attribute_array_from_message (DBusMessage *message)
 
1348
{
 
1349
  DBusMessageIter iter;
 
1350
  GArray *ret;
 
1351
 
 
1352
  if (!message)
 
1353
    return NULL;
 
1354
 
 
1355
  _ATSPI_DBUS_CHECK_SIG (message, "a{ss}", NULL, NULL);
 
1356
 
 
1357
  dbus_message_iter_init (message, &iter);
 
1358
 
 
1359
  ret = _atspi_dbus_attribute_array_from_iter (&iter);
 
1360
  dbus_message_unref (message);
 
1361
  return ret;
 
1362
}
 
1363
 
 
1364
GArray *
 
1365
_atspi_dbus_attribute_array_from_iter (DBusMessageIter *iter)
 
1366
{
 
1367
  DBusMessageIter iter_array, iter_dict;
 
1368
  GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
 
1369
 
 
1370
  dbus_message_iter_recurse (iter, &iter_array);
 
1371
  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
 
1372
  {
 
1373
    const char *name, *value;
 
1374
    gchar *str;
 
1375
    dbus_message_iter_recurse (&iter_array, &iter_dict);
 
1376
    dbus_message_iter_get_basic (&iter_dict, &name);
 
1377
    dbus_message_iter_next (&iter_dict);
 
1378
    dbus_message_iter_get_basic (&iter_dict, &value);
 
1379
    str = g_strdup_printf ("%s:%s", name, value);
 
1380
    array = g_array_append_val (array, str);
 
1381
    dbus_message_iter_next (&iter_array);;
 
1382
  }
 
1383
  return array;
 
1384
}
 
1385
 
 
1386
void
 
1387
_atspi_dbus_set_interfaces (AtspiAccessible *accessible, DBusMessageIter *iter)
 
1388
{
 
1389
  DBusMessageIter iter_array;
 
1390
  char *iter_sig = dbus_message_iter_get_signature (iter);
 
1391
 
 
1392
  accessible->interfaces = 0;
 
1393
  if (strcmp (iter_sig, "as") != 0)
 
1394
  {
 
1395
    g_warning ("_atspi_dbus_set_interfaces: Passed iterator with invalid signature %s", dbus_message_iter_get_signature (iter));
 
1396
    dbus_free (iter_sig);
 
1397
    return;
 
1398
  }
 
1399
  dbus_free (iter_sig);
 
1400
  dbus_message_iter_recurse (iter, &iter_array);
 
1401
  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
 
1402
  {
 
1403
    const char *iface;
 
1404
    gint n;
 
1405
    dbus_message_iter_get_basic (&iter_array, &iface);
 
1406
    if (!strcmp (iface, "org.freedesktop.DBus.Introspectable")) continue;
 
1407
    n = _atspi_get_iface_num (iface);
 
1408
    if (n == -1)
 
1409
    {
 
1410
      g_warning ("AT-SPI: Unknown interface %s", iface);
 
1411
    }
 
1412
    else
 
1413
      accessible->interfaces |= (1 << n);
 
1414
    dbus_message_iter_next (&iter_array);
 
1415
  }
 
1416
  _atspi_accessible_add_cache (accessible, ATSPI_CACHE_INTERFACES);
 
1417
}
 
1418
 
 
1419
void
 
1420
_atspi_dbus_set_state (AtspiAccessible *accessible, DBusMessageIter *iter)
 
1421
{
 
1422
  DBusMessageIter iter_array;
 
1423
  gint count;
 
1424
  dbus_uint32_t *states;
 
1425
 
 
1426
  dbus_message_iter_recurse (iter, &iter_array);
 
1427
  dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
 
1428
  if (count != 2)
 
1429
  {
 
1430
    g_warning ("AT-SPI: expected 2 values in states array; got %d\n", count);
 
1431
    if (!accessible->states)
 
1432
      accessible->states = _atspi_state_set_new_internal (accessible, 0);
 
1433
  }
 
1434
  else
 
1435
  {
 
1436
    guint64 val = ((guint64)states [1]) << 32;
 
1437
    val += states [0];
 
1438
    if (!accessible->states)
 
1439
      accessible->states = _atspi_state_set_new_internal (accessible, val);
 
1440
    else
 
1441
      accessible->states->states = val;
 
1442
  }
 
1443
  _atspi_accessible_add_cache (accessible, ATSPI_CACHE_STATES);
 
1444
}
 
1445
 
 
1446
GQuark
 
1447
_atspi_error_quark (void)
 
1448
{
 
1449
  return g_quark_from_static_string ("atspi_error");
 
1450
}
 
1451
 
 
1452
/*
 
1453
 * Gets the IOR from the XDisplay.
 
1454
 */
 
1455
#ifdef HAVE_X11
 
1456
static char *
 
1457
get_accessibility_bus_address_x11 (void)
 
1458
{
 
1459
  Atom AT_SPI_BUS;
 
1460
  Atom actual_type;
 
1461
  Display *bridge_display = NULL;
 
1462
  int actual_format;
 
1463
  char *data;
 
1464
  unsigned char *data_x11 = NULL;
 
1465
  unsigned long nitems;
 
1466
  unsigned long leftover;
 
1467
  char *display_name;
 
1468
 
 
1469
  display_name = spi_display_name ();
 
1470
  if (!display_name)
 
1471
    return NULL;
 
1472
 
 
1473
  bridge_display = XOpenDisplay (display_name);
 
1474
  g_free (display_name);
 
1475
 
 
1476
  if (!bridge_display)
 
1477
    {
 
1478
      g_warning ("Could not open X display");
 
1479
      return NULL;
 
1480
    }
 
1481
      
 
1482
  AT_SPI_BUS = XInternAtom (bridge_display, "AT_SPI_BUS", False);
 
1483
  XGetWindowProperty (bridge_display,
 
1484
                      XDefaultRootWindow (bridge_display),
 
1485
                      AT_SPI_BUS, 0L,
 
1486
                      (long) BUFSIZ, False,
 
1487
                      (Atom) 31, &actual_type, &actual_format,
 
1488
                      &nitems, &leftover, &data_x11);
 
1489
  XCloseDisplay (bridge_display);
 
1490
 
 
1491
  data = g_strdup ((gchar *)data_x11);
 
1492
  XFree (data_x11);
 
1493
  return data;
 
1494
}
 
1495
#endif
 
1496
 
 
1497
static char *
 
1498
get_accessibility_bus_address_dbus (void)
 
1499
{
 
1500
  DBusConnection *session_bus = NULL;
 
1501
  DBusMessage *message;
 
1502
  DBusMessage *reply;
 
1503
  DBusError error;
 
1504
  char *address = NULL;
 
1505
 
 
1506
  session_bus = dbus_bus_get (DBUS_BUS_SESSION, NULL);
 
1507
  if (!session_bus)
 
1508
    return NULL;
 
1509
 
 
1510
  message = dbus_message_new_method_call ("org.a11y.Bus",
 
1511
                                          "/org/a11y/bus",
 
1512
                                          "org.a11y.Bus",
 
1513
                                          "GetAddress");
 
1514
 
 
1515
  dbus_error_init (&error);
 
1516
  reply = dbus_connection_send_with_reply_and_block (session_bus,
 
1517
                                                     message,
 
1518
                                                     -1,
 
1519
                                                     &error);
 
1520
  dbus_message_unref (message);
 
1521
 
 
1522
  if (!reply)
 
1523
  {
 
1524
    g_warning ("Error retrieving accessibility bus address: %s: %s",
 
1525
               error.name, error.message);
 
1526
    dbus_error_free (&error);
 
1527
    return NULL;
 
1528
  }
 
1529
  
 
1530
  {
 
1531
    const char *tmp_address;
 
1532
    if (!dbus_message_get_args (reply,
 
1533
                                NULL,
 
1534
                                DBUS_TYPE_STRING,
 
1535
                                &tmp_address,
 
1536
                                DBUS_TYPE_INVALID))
 
1537
      {
 
1538
        dbus_message_unref (reply);
 
1539
        return NULL;
 
1540
      }
 
1541
    address = g_strdup (tmp_address);
 
1542
    dbus_message_unref (reply);
 
1543
  }
 
1544
  
 
1545
  return address;
 
1546
}
 
1547
 
 
1548
static DBusConnection *a11y_bus;
 
1549
static dbus_int32_t a11y_dbus_slot = -1;
 
1550
 
 
1551
static void
 
1552
a11y_bus_free (void *data)
 
1553
{
 
1554
  if (data == a11y_bus)
 
1555
    {
 
1556
      a11y_bus = NULL;
 
1557
      dbus_connection_free_data_slot (&a11y_dbus_slot);
 
1558
    }
 
1559
}
 
1560
 
 
1561
/**
 
1562
 * atspi_get_a11y_bus: (skip)
 
1563
 */
 
1564
DBusConnection *
 
1565
atspi_get_a11y_bus (void)
 
1566
{
 
1567
  DBusError error;
 
1568
  char *address = NULL;
 
1569
 
 
1570
  if (a11y_bus && dbus_connection_get_is_connected (a11y_bus))
 
1571
    return a11y_bus;
 
1572
 
 
1573
  if (a11y_dbus_slot == -1)
 
1574
    if (!dbus_connection_allocate_data_slot (&a11y_dbus_slot))
 
1575
      g_warning ("at-spi: Unable to allocate D-Bus slot");
 
1576
 
 
1577
#ifdef HAVE_X11
 
1578
  address = get_accessibility_bus_address_x11 ();
 
1579
#endif
 
1580
  if (!address)
 
1581
    address = get_accessibility_bus_address_dbus ();
 
1582
  if (!address)
 
1583
    return NULL;
 
1584
 
 
1585
  dbus_error_init (&error);
 
1586
  a11y_bus = dbus_connection_open_private (address, &error);
 
1587
  g_free (address);
 
1588
 
 
1589
  if (!a11y_bus)
 
1590
    {
 
1591
      g_warning ("Couldn't connect to accessibility bus: %s", error.message);
 
1592
      dbus_error_free (&error);
 
1593
      return NULL;
 
1594
    }
 
1595
  else
 
1596
    {
 
1597
      if (!dbus_bus_register (a11y_bus, &error))
 
1598
        {
 
1599
          g_warning ("Couldn't register with accessibility bus: %s", error.message);
 
1600
          dbus_error_free (&error);
 
1601
          dbus_connection_close (a11y_bus);
 
1602
          dbus_connection_unref (a11y_bus);
 
1603
          a11y_bus = NULL;
 
1604
          return NULL;
 
1605
        }
 
1606
    }
 
1607
  
 
1608
  /* Simulate a weak ref on the bus */
 
1609
  dbus_connection_set_data (a11y_bus, a11y_dbus_slot, a11y_bus, a11y_bus_free);
 
1610
 
 
1611
  return a11y_bus;
 
1612
}
 
1613
 
 
1614
/**
 
1615
 * atspi_set_timeout:
 
1616
 * @val: The timeout value, in milliseconds, or -1 to disable the timeout.
 
1617
 * @startup_time: The amount of time, in milliseconds, to allow to pass
 
1618
 * before enforcing timeouts on an application. Can be used to prevent
 
1619
 * timeout exceptions if an application is likely to block for an extended
 
1620
 * period of time on initialization. -1 can be passed to disable this
 
1621
 * behavior.
 
1622
 *
 
1623
 * Set the timeout used for method calls. If this is not set explicitly,
 
1624
 * a default of 0.8 ms is used.
 
1625
 * Note that at-spi2-registryd currently uses a timeout of 3 seconds when
 
1626
 * sending a keyboard event notification. This means that, if an AT makes
 
1627
 * a call in response to the keyboard notification and the application
 
1628
 * being called does not respond before the timeout is reached,
 
1629
 * at-spi2-registryd will time out on the keyboard event notification and
 
1630
 * pass the key onto the application (ie, reply to indicate that the key
 
1631
 * was not consumed), so this may make it undesirable to set a timeout
 
1632
 * larger than 3 seconds.
 
1633
 *
 
1634
 * By default, the normal timeout is set to 800 ms, and the application startup
 
1635
 * timeout is set to 15 seconds.
 
1636
 */
 
1637
void
 
1638
atspi_set_timeout (gint val, gint startup_time)
 
1639
{
 
1640
  method_call_timeout = val;
 
1641
  app_startup_time = startup_time;
 
1642
}
 
1643
 
 
1644
/**
 
1645
 * atspi_set_main_context:
 
1646
 * @cnx: The #GMainContext to use.
 
1647
 *
 
1648
 * Sets the main loop context that AT-SPI should assume is in use when
 
1649
 * setting an idle callback.
 
1650
 * This function should be called by application-side implementors (ie,
 
1651
 * at-spi2-atk) when it is desirable to re-enter the main loop.
 
1652
 */
 
1653
void
 
1654
atspi_set_main_context (GMainContext *cnx)
 
1655
{
 
1656
  if (atspi_main_context == cnx)
 
1657
    return;
 
1658
  if (process_deferred_messages_source != NULL)
 
1659
  {
 
1660
    g_source_destroy (process_deferred_messages_source);
 
1661
    process_deferred_messages_source = g_idle_source_new ();
 
1662
    g_source_set_callback (process_deferred_messages_source,
 
1663
                           process_deferred_messages_callback, NULL, NULL);
 
1664
    g_source_attach (process_deferred_messages_source, cnx);
 
1665
    g_source_unref (process_deferred_messages_source);
 
1666
  }
 
1667
  atspi_main_context = cnx;
 
1668
  atspi_dbus_connection_setup_with_g_main (atspi_get_a11y_bus (), cnx);
 
1669
}
 
1670
 
 
1671
#ifdef DEBUG_REF_COUNTS
 
1672
static void
 
1673
print_disposed (gpointer key, gpointer value, gpointer data)
 
1674
{
 
1675
  AtspiAccessible *accessible = key;
 
1676
  if (accessible->parent.app)
 
1677
    return;
 
1678
  g_print ("disposed: %s %d\n", accessible->name, accessible->role);
 
1679
}
 
1680
 
 
1681
void
 
1682
debug_disposed ()
 
1683
{
 
1684
  g_hash_table_foreach (live_refs, print_disposed, NULL);
 
1685
}
 
1686
#endif
 
1687
 
 
1688
gchar *
 
1689
_atspi_name_compat (gchar *name)
 
1690
{
 
1691
  gchar *p = name;
 
1692
 
 
1693
  while (*p)
 
1694
  {
 
1695
    if (*p == '-')
 
1696
      *p = ' ';
 
1697
    p++;
 
1698
  }
 
1699
  return name;
 
1700
}
 
1701
 
 
1702
/**
 
1703
 * atspi_role_get_name:
 
1704
 * @role: an #AtspiRole object to query.
 
1705
 *
 
1706
 * Gets a localizable string that indicates the name of an #AtspiRole.
 
1707
 * <em>DEPRECATED.</em>
 
1708
 *
 
1709
 * Returns: a localizable string name for an #AtspiRole enumerated type.
 
1710
 **/
 
1711
gchar *
 
1712
atspi_role_get_name (AtspiRole role)
 
1713
{
 
1714
  gchar *retval = NULL;
 
1715
  GTypeClass *type_class;
 
1716
  GEnumValue *value;
 
1717
 
 
1718
  type_class = g_type_class_ref (ATSPI_TYPE_ROLE);
 
1719
  g_return_val_if_fail (G_IS_ENUM_CLASS (type_class), NULL);
 
1720
 
 
1721
  value = g_enum_get_value (G_ENUM_CLASS (type_class), role);
 
1722
 
 
1723
  if (value)
 
1724
    {
 
1725
      retval = g_strdup (value->value_nick);
 
1726
    }
 
1727
 
 
1728
  g_type_class_unref (type_class);
 
1729
 
 
1730
  if (retval)
 
1731
    return _atspi_name_compat (retval);
 
1732
 
 
1733
  return NULL;
 
1734
}
 
1735
 
 
1736
GHashTable *
 
1737
_atspi_dbus_update_cache_from_dict (AtspiAccessible *accessible, DBusMessageIter *iter)
 
1738
{
 
1739
  GHashTable *cache = _atspi_accessible_ref_cache (accessible);
 
1740
  DBusMessageIter iter_dict, iter_dict_entry, iter_struct, iter_variant;
 
1741
 
 
1742
  dbus_message_iter_recurse (iter, &iter_dict);
 
1743
  while (dbus_message_iter_get_arg_type (&iter_dict) != DBUS_TYPE_INVALID)
 
1744
  {
 
1745
    const char *key;
 
1746
    GValue *val = NULL;
 
1747
    dbus_message_iter_recurse (&iter_dict, &iter_dict_entry);
 
1748
    dbus_message_iter_get_basic (&iter_dict_entry, &key);
 
1749
    dbus_message_iter_next (&iter_dict_entry);
 
1750
    dbus_message_iter_recurse (&iter_dict_entry, &iter_variant);
 
1751
    if (!strcmp (key, "interfaces"))
 
1752
    {
 
1753
      _atspi_dbus_set_interfaces (accessible, &iter_variant);
 
1754
    }
 
1755
    else if (!strcmp (key, "Attributes"))
 
1756
    {
 
1757
      char *iter_sig = dbus_message_iter_get_signature (&iter_variant);
 
1758
      val = g_new0 (GValue, 1);;
 
1759
      g_value_init (val, G_TYPE_HASH_TABLE);
 
1760
      if (strcmp (iter_sig, "a{ss}") != 0)
 
1761
      {
 
1762
        dbus_free (iter_sig);
 
1763
        break;
 
1764
      }
 
1765
      dbus_free (iter_sig);
 
1766
      g_value_take_boxed (val, _atspi_dbus_hash_from_iter (&iter_variant));
 
1767
    }
 
1768
    else if (!strcmp (key, "Component.ScreenExtents"))
 
1769
    {
 
1770
      dbus_int32_t d_int;
 
1771
      AtspiRect extents;
 
1772
      char *iter_sig = dbus_message_iter_get_signature (&iter_variant);
 
1773
      val = g_new0 (GValue, 1);;
 
1774
      g_value_init (val, ATSPI_TYPE_RECT);
 
1775
      if (strcmp (iter_sig, "(iiii)") != 0)
 
1776
      {
 
1777
        dbus_free (iter_sig);
 
1778
        break;
 
1779
      }
 
1780
      dbus_free (iter_sig);
 
1781
      dbus_message_iter_recurse (&iter_variant, &iter_struct);
 
1782
      dbus_message_iter_get_basic (&iter_struct, &d_int);
 
1783
      extents.x = d_int;
 
1784
      dbus_message_iter_next (&iter_struct);
 
1785
      dbus_message_iter_get_basic (&iter_struct, &d_int);
 
1786
      extents.y = d_int;
 
1787
      dbus_message_iter_next (&iter_struct);
 
1788
      dbus_message_iter_get_basic (&iter_struct, &d_int);
 
1789
      extents.width = d_int;
 
1790
      dbus_message_iter_next (&iter_struct);
 
1791
      dbus_message_iter_get_basic (&iter_struct, &d_int);
 
1792
      extents.height = d_int;
 
1793
      g_value_set_boxed (val, &extents);
 
1794
    }
 
1795
    if (val)
 
1796
      g_hash_table_insert (cache, g_strdup (key), val); 
 
1797
    dbus_message_iter_next (&iter_dict);
 
1798
  }
 
1799
 
 
1800
  return cache;
 
1801
}
 
1802
 
 
1803
gboolean
 
1804
_atspi_get_allow_sync ()
 
1805
{
 
1806
  return allow_sync;
 
1807
}
 
1808
 
 
1809
gboolean
 
1810
_atspi_set_allow_sync (gboolean val)
 
1811
{
 
1812
  gboolean ret = allow_sync;
 
1813
 
 
1814
  allow_sync = val;
 
1815
  return ret;
 
1816
}
 
1817
 
 
1818
void
 
1819
_atspi_set_error_no_sync (GError **error)
 
1820
{
 
1821
  g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_SYNC_NOT_ALLOWED,
 
1822
                        _("Attempted synchronous call where prohibited"));
 
1823
}