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

« back to all changes in this revision

Viewing changes to atspi/atspi-event-listener.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 2002 Ximian Inc.
 
6
 * Copyright 2002 Sun Microsystems, 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
#include "atspi-private.h"
 
26
#include "atspi-accessible-private.h"
 
27
#include <string.h>
 
28
#include <ctype.h>
 
29
 
 
30
typedef struct
 
31
{
 
32
  AtspiEventListenerCB callback;
 
33
  void *user_data;
 
34
  GDestroyNotify callback_destroyed;
 
35
  char *event_type;
 
36
  char *category;
 
37
  char *name;
 
38
  char *detail;
 
39
  GArray *properties;
 
40
} EventListenerEntry;
 
41
 
 
42
G_DEFINE_TYPE (AtspiEventListener, atspi_event_listener, G_TYPE_OBJECT)
 
43
 
 
44
void
 
45
atspi_event_listener_init (AtspiEventListener *listener)
 
46
{
 
47
}
 
48
 
 
49
void
 
50
atspi_event_listener_class_init (AtspiEventListenerClass *klass)
 
51
{
 
52
}
 
53
 
 
54
static void
 
55
remove_datum (AtspiEvent *event, void *user_data)
 
56
{
 
57
  AtspiEventListenerSimpleCB cb = user_data;
 
58
  cb (event);
 
59
}
 
60
 
 
61
typedef struct
 
62
{
 
63
  gpointer callback;
 
64
  GDestroyNotify callback_destroyed;
 
65
  gint ref_count;
 
66
} CallbackInfo;
 
67
static GHashTable *callbacks;
 
68
 
 
69
void
 
70
callback_ref (void *callback, GDestroyNotify callback_destroyed)
 
71
{
 
72
  CallbackInfo *info;
 
73
 
 
74
  if (!callbacks)
 
75
  {
 
76
    callbacks = g_hash_table_new (g_direct_hash, g_direct_equal);
 
77
    if (!callbacks)
 
78
      return;
 
79
  }
 
80
 
 
81
  info = g_hash_table_lookup (callbacks, callback);
 
82
  if (!info)
 
83
  {
 
84
    info = g_new (CallbackInfo, 1);
 
85
    info->callback = callback;
 
86
    info->callback_destroyed = callback_destroyed;
 
87
    info->ref_count = 1;
 
88
    g_hash_table_insert (callbacks, callback, info);
 
89
  }
 
90
  else
 
91
    info->ref_count++;
 
92
}
 
93
 
 
94
void
 
95
callback_unref (gpointer callback)
 
96
{
 
97
  CallbackInfo *info;
 
98
 
 
99
  if (!callbacks)
 
100
    return;
 
101
  info = g_hash_table_lookup (callbacks, callback);
 
102
  if (!info)
 
103
  {
 
104
    g_warning ("Atspi: Dereferencing invalid callback %p\n", callback);
 
105
    return;
 
106
  }
 
107
  info->ref_count--;
 
108
  if (info->ref_count == 0)
 
109
  {
 
110
#if 0
 
111
    /* TODO: Figure out why this seg faults from Python */
 
112
    if (info->callback_destroyed)
 
113
      (*info->callback_destroyed) (info->callback);
 
114
#endif
 
115
    g_free (info);
 
116
    g_hash_table_remove (callbacks, callback);
 
117
  }
 
118
}
 
119
 
 
120
/**
 
121
 * atspi_event_listener_new:
 
122
 * @callback: (scope notified): An #AtspiEventListenerCB to be called
 
123
 * when an event is fired.
 
124
 * @user_data: (closure): data to pass to the callback.
 
125
 * @callback_destroyed: A #GDestroyNotify called when the listener is freed
 
126
 * and data associated with the callback should be freed.  Can be NULL.
 
127
 *
 
128
 * Creates a new #AtspiEventListener associated with a specified @callback.
 
129
 *
 
130
 * Returns: (transfer full): A new #AtspiEventListener.
 
131
 */
 
132
AtspiEventListener *
 
133
atspi_event_listener_new (AtspiEventListenerCB callback,
 
134
                                 gpointer user_data,
 
135
                                 GDestroyNotify callback_destroyed)
 
136
{
 
137
  AtspiEventListener *listener = g_object_new (ATSPI_TYPE_EVENT_LISTENER, NULL);
 
138
  listener->callback = callback;
 
139
  callback_ref (callback, callback_destroyed);
 
140
  listener->user_data = user_data;
 
141
  listener->cb_destroyed = callback_destroyed;
 
142
  return listener;
 
143
}
 
144
 
 
145
/**
 
146
 * atspi_event_listener_new_simple: (skip)
 
147
 * @callback: (scope notified): An #AtspiEventListenerSimpleCB to be called
 
148
 * when an event is fired.
 
149
 * @callback_destroyed: A #GDestroyNotify called when the listener is freed
 
150
 * and data associated with the callback should be freed.  Can be NULL.
 
151
 *
 
152
 * Creates a new #AtspiEventListener associated with a specified @callback.
 
153
 * Returns: (transfer full): A new #AtspiEventListener.
 
154
 **/
 
155
AtspiEventListener *
 
156
atspi_event_listener_new_simple (AtspiEventListenerSimpleCB callback,
 
157
                                 GDestroyNotify callback_destroyed)
 
158
{
 
159
  AtspiEventListener *listener = g_object_new (ATSPI_TYPE_EVENT_LISTENER, NULL);
 
160
  listener->callback = remove_datum;
 
161
  callback_ref (remove_datum, callback_destroyed);
 
162
  listener->user_data = callback;
 
163
  listener->cb_destroyed = callback_destroyed;
 
164
  return listener;
 
165
}
 
166
 
 
167
static GList *event_listeners = NULL;
 
168
 
 
169
static gchar *
 
170
convert_name_from_dbus (const char *name, gboolean path_hack)
 
171
{
 
172
  gchar *ret = g_malloc (g_utf8_strlen (name, -1) * 2 + 1);
 
173
  const char *p = name;
 
174
  gchar *q = ret;
 
175
 
 
176
  if (!ret)
 
177
    return NULL;
 
178
 
 
179
  while (*p)
 
180
  {
 
181
    if (isupper (*p))
 
182
    {
 
183
      if (q > ret)
 
184
        *q++ = '-';
 
185
      *q++ = tolower (*p++);
 
186
    }
 
187
    else if (path_hack && *p == '/')
 
188
    {
 
189
      *q++ = ':';
 
190
      p++;
 
191
    }
 
192
    else
 
193
      *q++ = *p++;
 
194
  }
 
195
  *q = '\0';
 
196
  return ret;
 
197
}
 
198
 
 
199
static void
 
200
cache_process_children_changed (AtspiEvent *event)
 
201
{
 
202
  AtspiAccessible *child;
 
203
 
 
204
  if (!G_VALUE_HOLDS (&event->any_data, ATSPI_TYPE_ACCESSIBLE) ||
 
205
      !(event->source->cached_properties & ATSPI_CACHE_CHILDREN) ||
 
206
      atspi_state_set_contains (event->source->states, ATSPI_STATE_MANAGES_DESCENDANTS))
 
207
    return;
 
208
 
 
209
  child = g_value_get_object (&event->any_data);
 
210
  if (child == NULL)
 
211
    return;
 
212
 
 
213
  if (!strncmp (event->type, "object:children-changed:add", 27))
 
214
  {
 
215
    g_ptr_array_remove (event->source->children, child); /* just to be safe */
 
216
    if (event->detail1 < 0 || event->detail1 > event->source->children->len)
 
217
    {
 
218
      event->source->cached_properties &= ~ATSPI_CACHE_CHILDREN;
 
219
      return;
 
220
    }
 
221
    /* Unfortunately, there's no g_ptr_array_insert or similar */
 
222
    g_ptr_array_add (event->source->children, NULL);
 
223
    memmove (event->source->children->pdata + event->detail1 + 1,
 
224
             event->source->children->pdata + event->detail1,
 
225
             (event->source->children->len - event->detail1 - 1) * sizeof (gpointer));
 
226
    g_ptr_array_index (event->source->children, event->detail1) = g_object_ref (child);
 
227
  }
 
228
  else
 
229
  {
 
230
    g_ptr_array_remove (event->source->children, child);
 
231
    if (child == child->parent.app->root)
 
232
      g_object_run_dispose (G_OBJECT (child->parent.app));
 
233
  }
 
234
}
 
235
 
 
236
static void
 
237
cache_process_property_change (AtspiEvent *event)
 
238
{
 
239
  if (!strcmp (event->type, "object:property-change:accessible-parent"))
 
240
  {
 
241
    if (event->source->accessible_parent)
 
242
      g_object_unref (event->source->accessible_parent);
 
243
    if (G_VALUE_HOLDS (&event->any_data, ATSPI_TYPE_ACCESSIBLE))
 
244
    {
 
245
      event->source->accessible_parent = g_value_dup_object (&event->any_data);
 
246
      _atspi_accessible_add_cache (event->source, ATSPI_CACHE_PARENT);
 
247
    }
 
248
    else
 
249
    {
 
250
      event->source->accessible_parent = NULL;
 
251
      event->source->cached_properties &= ~ATSPI_CACHE_PARENT;
 
252
    }
 
253
  }
 
254
  else if (!strcmp (event->type, "object:property-change:accessible-name"))
 
255
  {
 
256
    if (event->source->name)
 
257
      g_free (event->source->name);
 
258
    if (G_VALUE_HOLDS_STRING (&event->any_data))
 
259
    {
 
260
      event->source->name = g_value_dup_string (&event->any_data);
 
261
      _atspi_accessible_add_cache (event->source, ATSPI_CACHE_NAME);
 
262
    }
 
263
    else
 
264
    {
 
265
      event->source->name = NULL;
 
266
      event->source->cached_properties &= ~ATSPI_CACHE_NAME;
 
267
    }
 
268
  }
 
269
  else if (!strcmp (event->type, "object:property-change:accessible-description"))
 
270
  {
 
271
    if (event->source->description)
 
272
      g_free (event->source->description);
 
273
    if (G_VALUE_HOLDS_STRING (&event->any_data))
 
274
    {
 
275
      event->source->description = g_value_dup_string (&event->any_data);
 
276
      _atspi_accessible_add_cache (event->source, ATSPI_CACHE_DESCRIPTION);
 
277
    }
 
278
    else
 
279
    {
 
280
      event->source->description = NULL;
 
281
      event->source->cached_properties &= ~ATSPI_CACHE_DESCRIPTION;
 
282
    }
 
283
  }
 
284
  else if (!strcmp (event->type, "object:property-change:accessible-role"))
 
285
  {
 
286
    if (G_VALUE_HOLDS_INT (&event->any_data))
 
287
    {
 
288
      event->source->role = g_value_get_int (&event->any_data);
 
289
      _atspi_accessible_add_cache (event->source, ATSPI_CACHE_ROLE);
 
290
    }
 
291
    else
 
292
    {
 
293
      event->source->cached_properties &= ~ATSPI_CACHE_ROLE;
 
294
    }
 
295
  }
 
296
}
 
297
 
 
298
static void
 
299
cache_process_state_changed (AtspiEvent *event)
 
300
{
 
301
  if (event->source->states)
 
302
    atspi_state_set_set_by_name (event->source->states, event->type + 21,
 
303
                                 event->detail1);
 
304
}
 
305
 
 
306
static dbus_bool_t
 
307
demarshal_rect (DBusMessageIter *iter, AtspiRect *rect)
 
308
{
 
309
  dbus_int32_t x, y, width, height;
 
310
  DBusMessageIter iter_struct;
 
311
 
 
312
  dbus_message_iter_recurse (iter, &iter_struct);
 
313
  if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
 
314
  dbus_message_iter_get_basic (&iter_struct, &x);
 
315
  dbus_message_iter_next (&iter_struct);
 
316
  if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
 
317
  dbus_message_iter_get_basic (&iter_struct, &y);
 
318
  dbus_message_iter_next (&iter_struct);
 
319
  if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
 
320
  dbus_message_iter_get_basic (&iter_struct, &width);
 
321
  dbus_message_iter_next (&iter_struct);
 
322
  if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
 
323
  dbus_message_iter_get_basic (&iter_struct, &height);
 
324
  rect->x = x;
 
325
  rect->y = y;
 
326
  rect->width = width;
 
327
  rect->height = height;
 
328
  return TRUE;
 
329
}
 
330
 
 
331
static gchar *
 
332
strdup_and_adjust_for_dbus (const char *s)
 
333
{
 
334
  gchar *d = g_strdup (s);
 
335
  gchar *p;
 
336
  int parts = 0;
 
337
 
 
338
  if (!d)
 
339
    return NULL;
 
340
 
 
341
  for (p = d; *p; p++)
 
342
  {
 
343
    if (*p == '-')
 
344
    {
 
345
      memmove (p, p + 1, g_utf8_strlen (p, -1));
 
346
      *p = toupper (*p);
 
347
    }
 
348
    else if (*p == ':')
 
349
    {
 
350
      parts++;
 
351
      if (parts == 2)
 
352
        break;
 
353
      p [1] = toupper (p [1]);
 
354
    }
 
355
  }
 
356
 
 
357
  d [0] = toupper (d [0]);
 
358
  return d;
 
359
}
 
360
 
 
361
static gboolean
 
362
convert_event_type_to_dbus (const char *eventType, char **categoryp, char **namep, char **detailp, GPtrArray **matchrule_array)
 
363
{
 
364
  gchar *tmp = strdup_and_adjust_for_dbus (eventType);
 
365
  char *category = NULL, *name = NULL, *detail = NULL;
 
366
  char *saveptr = NULL;
 
367
 
 
368
  if (tmp == NULL) return FALSE;
 
369
  category = strtok_r (tmp, ":", &saveptr);
 
370
  if (category) category = g_strdup (category);
 
371
  name = strtok_r (NULL, ":", &saveptr);
 
372
  if (name)
 
373
  {
 
374
    name = g_strdup (name);
 
375
    detail = strtok_r (NULL, ":", &saveptr);
 
376
    if (detail) detail = g_strdup (detail);
 
377
  }
 
378
  if (matchrule_array)
 
379
  {
 
380
    gchar *matchrule;
 
381
    matchrule = g_strdup_printf ("type='signal',interface='org.a11y.atspi.Event.%s'", category);
 
382
    if (name && name [0])
 
383
    {
 
384
      gchar *new_str = g_strconcat (matchrule, ",member='", name, "'", NULL);
 
385
      g_free (matchrule);
 
386
      matchrule = new_str;
 
387
    }
 
388
    (*matchrule_array) = g_ptr_array_new ();
 
389
    if (detail && detail [0])
 
390
    {
 
391
      gchar *new_str = g_strconcat (matchrule, ",arg0='", detail, "'", NULL);
 
392
      g_ptr_array_add (*matchrule_array, new_str);
 
393
      new_str = g_strconcat (matchrule, ",arg0path='", detail, "/'", NULL);
 
394
      g_ptr_array_add (*matchrule_array, new_str);
 
395
      g_free (matchrule);
 
396
    }
 
397
    else
 
398
      g_ptr_array_add (*matchrule_array, matchrule);
 
399
  }
 
400
  if (categoryp) *categoryp = category;
 
401
  else g_free (category);
 
402
  if (namep) *namep = name;
 
403
  else if (name) g_free (name);
 
404
  if (detailp) *detailp = detail;
 
405
  else if (detail) g_free (detail);
 
406
  g_free (tmp);
 
407
  return TRUE;
 
408
}
 
409
 
 
410
static void
 
411
listener_entry_free (EventListenerEntry *e)
 
412
{
 
413
  gpointer callback = (e->callback == remove_datum ? (gpointer)e->user_data : (gpointer)e->callback);
 
414
  g_free (e->event_type);
 
415
  g_free (e->category);
 
416
  g_free (e->name);
 
417
  if (e->detail) g_free (e->detail);
 
418
  callback_unref (callback);
 
419
  g_free (e);
 
420
}
 
421
 
 
422
/**
 
423
 * atspi_event_listener_register:
 
424
 * @listener: The #AtspiEventListener to register against an event type.
 
425
 * @event_type: a character string indicating the type of events for which
 
426
 *            notification is requested.  Format is
 
427
 *            EventClass:major_type:minor_type:detail
 
428
 *            where all subfields other than EventClass are optional.
 
429
 *            EventClasses include "object", "window", "mouse",
 
430
 *            and toolkit events (e.g. "Gtk", "AWT").
 
431
 *            Examples: "focus:", "Gtk:GtkWidget:button_press_event".
 
432
 *
 
433
 * Adds an in-process callback function to an existing #AtspiEventListener.
 
434
 *
 
435
 * Legal object event types:
 
436
 *
 
437
 *    (property change events)
 
438
 *
 
439
 *            object:property-change
 
440
 *            object:property-change:accessible-name
 
441
 *            object:property-change:accessible-description
 
442
 *            object:property-change:accessible-parent
 
443
 *            object:property-change:accessible-value
 
444
 *            object:property-change:accessible-role
 
445
 *            object:property-change:accessible-table-caption
 
446
 *            object:property-change:accessible-table-column-description
 
447
 *            object:property-change:accessible-table-column-header
 
448
 *            object:property-change:accessible-table-row-description
 
449
 *            object:property-change:accessible-table-row-header
 
450
 *            object:property-change:accessible-table-summary
 
451
 *
 
452
 *    (other object events)
 
453
 *
 
454
 *            object:state-changed 
 
455
 *            object:children-changed
 
456
 *            object:visible-data-changed
 
457
 *            object:selection-changed
 
458
 *            object:text-selection-changed
 
459
 *            object:text-changed
 
460
 *            object:text-caret-moved
 
461
 *            object:row-inserted
 
462
 *            object:row-reordered
 
463
 *            object:row-deleted
 
464
 *            object:column-inserted
 
465
 *            object:column-reordered
 
466
 *            object:column-deleted
 
467
 *            object:model-changed
 
468
 *            object:active-descendant-changed
 
469
 *
 
470
 *  (window events)
 
471
 *
 
472
 *            window:minimize
 
473
 *            window:maximize
 
474
 *            window:restore
 
475
 *            window:close
 
476
 *            window:create
 
477
 *            window:reparent
 
478
 *            window:desktop-create
 
479
 *            window:desktop-destroy
 
480
 *            window:activate
 
481
 *            window:deactivate
 
482
 *            window:raise
 
483
 *            window:lower
 
484
 *            window:move
 
485
 *            window:resize
 
486
 *            window:shade
 
487
 *            window:unshade
 
488
 *            window:restyle
 
489
 *
 
490
 *  (other events)
 
491
 *
 
492
 *            focus:
 
493
 *            mouse:abs
 
494
 *            mouse:rel
 
495
 *            mouse:b1p
 
496
 *            mouse:b1r
 
497
 *            mouse:b2p
 
498
 *            mouse:b2r
 
499
 *            mouse:b3p
 
500
 *            mouse:b3r
 
501
 *
 
502
 * NOTE: this character string may be UTF-8, but should not contain byte 
 
503
 * value 56
 
504
 *            (ascii ':'), except as a delimiter, since non-UTF-8 string
 
505
 *            delimiting functions are used internally.
 
506
 *            In general, listening to
 
507
 *            toolkit-specific events is not recommended.
 
508
 *
 
509
 *
 
510
 * Returns: #TRUE if successful, otherwise #FALSE.
 
511
 **/
 
512
gboolean
 
513
atspi_event_listener_register (AtspiEventListener *listener,
 
514
                                             const gchar              *event_type,
 
515
                                             GError **error)
 
516
{
 
517
  /* TODO: Keep track of which events have been registered, so that we
 
518
 * deregister all of them when the event listener is destroyed */
 
519
 
 
520
  return atspi_event_listener_register_from_callback (listener->callback,
 
521
                                                      listener->user_data,
 
522
                                                      listener->cb_destroyed,
 
523
                                                      event_type, error);
 
524
}
 
525
 
 
526
/**
 
527
 * atspi_event_listener_register_full:
 
528
 * @listener: The #AtspiEventListener to register against an event type.
 
529
 * @event_type: a character string indicating the type of events for which
 
530
 *            notification is requested.  See #atspi_event_listener_register
 
531
 * for a description of the format and legal event types.
 
532
* @properties: (element-type gchar*) (transfer none) (allow-none): a list of
 
533
 *             properties that should be sent along with the event. The
 
534
 *             properties are valued for the duration of the event callback.k
 
535
 *             TODO: Document.
 
536
 *
 
537
 * Adds an in-process callback function to an existing #AtspiEventListener.
 
538
 *
 
539
 * Returns: #TRUE if successful, otherwise #FALSE.
 
540
 **/
 
541
gboolean
 
542
atspi_event_listener_register_full (AtspiEventListener *listener,
 
543
                                             const gchar              *event_type,
 
544
                                             GArray *properties,
 
545
                                             GError **error)
 
546
{
 
547
  /* TODO: Keep track of which events have been registered, so that we
 
548
 * deregister all of them when the event listener is destroyed */
 
549
 
 
550
  return atspi_event_listener_register_from_callback_full (listener->callback,
 
551
                                                           listener->user_data,
 
552
                                                           listener->cb_destroyed,
 
553
                                                           event_type,
 
554
                                                           properties,
 
555
                                                           error);
 
556
}
 
557
 
 
558
static gboolean
 
559
notify_event_registered (EventListenerEntry *e)
 
560
{
 
561
 
 
562
  if (e->properties)
 
563
    dbind_method_call_reentrant (_atspi_bus (), atspi_bus_registry,
 
564
                                 atspi_path_registry,
 
565
                                 atspi_interface_registry,
 
566
                                 "RegisterEvent",
 
567
                                 NULL, "sas", e->event_type,
 
568
                                 e->properties);
 
569
  else
 
570
    dbind_method_call_reentrant (_atspi_bus (), atspi_bus_registry,
 
571
                                 atspi_path_registry,
 
572
                                 atspi_interface_registry,
 
573
                                 "RegisterEvent",
 
574
                                 NULL, "s", e->event_type);
 
575
 
 
576
  return TRUE;
 
577
}
 
578
 
 
579
/**
 
580
 * atspi_event_listener_register_from_callback:
 
581
 * @callback: (scope notified): the #AtspiEventListenerCB to be registered 
 
582
 * against an event type.
 
583
 * @user_data: (closure): User data to be passed to the callback.
 
584
 * @callback_destroyed: A #GDestroyNotify called when the callback is destroyed.
 
585
 * @event_type: a character string indicating the type of events for which
 
586
 *            notification is requested.  See #atspi_event_listener_register
 
587
 * for a description of the format.
 
588
 * 
 
589
 * Registers an #AtspiEventListenerCB against an @event_type.
 
590
 *
 
591
 * Returns: #TRUE if successfull, otherwise #FALSE.
 
592
 *
 
593
 **/
 
594
gboolean
 
595
atspi_event_listener_register_from_callback (AtspiEventListenerCB callback,
 
596
                                             void *user_data,
 
597
                                             GDestroyNotify callback_destroyed,
 
598
                                             const gchar              *event_type,
 
599
                                             GError **error)
 
600
{
 
601
  return atspi_event_listener_register_from_callback_full (callback,
 
602
                                                           user_data,
 
603
                                                           callback_destroyed,
 
604
                                                           event_type, NULL,
 
605
                                                           error);
 
606
}
 
607
 
 
608
static GArray *
 
609
copy_event_properties (GArray *src)
 
610
{
 
611
  gint i;
 
612
 
 
613
  GArray *dst = g_array_new (FALSE, FALSE, sizeof (char *));
 
614
 
 
615
  if (!src)
 
616
    return dst;
 
617
  for (i = 0; i < src->len; i++)
 
618
    {
 
619
      gchar *dup = g_strdup (g_array_index (src, char *, i));
 
620
    g_array_append_val (dst, dup);
 
621
    }
 
622
  return dst;
 
623
}
 
624
 
 
625
gboolean
 
626
atspi_event_listener_register_from_callback_full (AtspiEventListenerCB callback,
 
627
                                                  void *user_data,
 
628
                                                  GDestroyNotify callback_destroyed,
 
629
                                                  const gchar              *event_type,
 
630
                                                  GArray *properties,
 
631
                                                  GError **error)
 
632
{
 
633
  EventListenerEntry *e;
 
634
  DBusError d_error;
 
635
  GPtrArray *matchrule_array;
 
636
  gint i;
 
637
 
 
638
  if (!callback)
 
639
    {
 
640
      return FALSE;
 
641
    }
 
642
 
 
643
  if (!event_type)
 
644
  {
 
645
    g_warning ("called atspi_event_listener_register_from_callback with a NULL event_type");
 
646
    return FALSE;
 
647
  }
 
648
 
 
649
  e = g_new (EventListenerEntry, 1);
 
650
  e->event_type = g_strdup (event_type);
 
651
  e->callback = callback;
 
652
  e->user_data = user_data;
 
653
  e->callback_destroyed = callback_destroyed;
 
654
  callback_ref (callback == remove_datum ? (gpointer)user_data : (gpointer)callback,
 
655
                callback_destroyed);
 
656
  if (!convert_event_type_to_dbus (event_type, &e->category, &e->name, &e->detail, &matchrule_array))
 
657
  {
 
658
    g_free (e);
 
659
    return FALSE;
 
660
  }
 
661
  e->properties = copy_event_properties (properties);
 
662
  event_listeners = g_list_prepend (event_listeners, e);
 
663
  for (i = 0; i < matchrule_array->len; i++)
 
664
  {
 
665
    char *matchrule = g_ptr_array_index (matchrule_array, i);
 
666
    dbus_error_init (&d_error);
 
667
    dbus_bus_add_match (_atspi_bus(), matchrule, &d_error);
 
668
    if (dbus_error_is_set (&d_error))
 
669
      {
 
670
        g_warning ("Atspi: Adding match: %s", d_error.message);
 
671
        dbus_error_free (&d_error);
 
672
        /* TODO: Set error */
 
673
      }
 
674
 
 
675
    g_free (matchrule);
 
676
  }
 
677
  g_ptr_array_free (matchrule_array, TRUE);
 
678
 
 
679
  notify_event_registered (e);
 
680
  return TRUE;
 
681
}
 
682
 
 
683
void
 
684
_atspi_reregister_event_listeners ()
 
685
{
 
686
  GList *l;
 
687
  EventListenerEntry *e;
 
688
 
 
689
  for (l = event_listeners; l; l = l->next)
 
690
    {
 
691
      e = l->data;
 
692
      notify_event_registered (e);
 
693
    }
 
694
}
 
695
 
 
696
/**
 
697
 * atspi_event_listener_register_no_data: (skip)
 
698
 * @callback: (scope notified): the #AtspiEventListenerSimpleCB to be
 
699
 *            registered against an event type.
 
700
 * @callback_destroyed: A #GDestroyNotify called when the callback is destroyed.
 
701
 * @event_type: a character string indicating the type of events for which
 
702
 *            notification is requested.  Format is
 
703
 *            EventClass:major_type:minor_type:detail
 
704
 *            where all subfields other than EventClass are optional.
 
705
 *            EventClasses include "object", "window", "mouse",
 
706
 *            and toolkit events (e.g. "Gtk", "AWT").
 
707
 *            Examples: "focus:", "Gtk:GtkWidget:button_press_event".
 
708
 *
 
709
 * Registers an #AtspiEventListenetSimpleCB. The method is similar to 
 
710
 * #atspi_event_listener_register, but @callback takes no user_data.
 
711
 *
 
712
 * Returns: #TRUE if successfull, otherwise #FALSE.
 
713
 **/
 
714
gboolean
 
715
atspi_event_listener_register_no_data (AtspiEventListenerSimpleCB callback,
 
716
                                 GDestroyNotify callback_destroyed,
 
717
                                 const gchar              *event_type,
 
718
                                 GError **error)
 
719
{
 
720
  return atspi_event_listener_register_from_callback (remove_datum, callback,
 
721
                                                      callback_destroyed,
 
722
                                                      event_type, error);
 
723
}
 
724
 
 
725
static gboolean
 
726
is_superset (const gchar *super, const gchar *sub)
 
727
{
 
728
  if (!super || !super [0])
 
729
    return TRUE;
 
730
  return (strcmp (super, sub) == 0);
 
731
}
 
732
 
 
733
/**
 
734
 * atspi_event_listener_deregister:
 
735
 * @listener: The #AtspiEventListener to deregister.
 
736
 * @event_type: a string specifying the event type for which this
 
737
 *             listener is to be deregistered.
 
738
 *
 
739
 * Deregisters an #AtspiEventListener from the registry, for a specific
 
740
 *             event type.
 
741
 *
 
742
 * Returns: #TRUE if successful, otherwise #FALSE.
 
743
 **/
 
744
gboolean
 
745
atspi_event_listener_deregister (AtspiEventListener *listener,
 
746
                                               const gchar              *event_type,
 
747
                                               GError **error)
 
748
{
 
749
  return atspi_event_listener_deregister_from_callback (listener->callback,
 
750
                                                        listener->user_data,
 
751
                                                        event_type, error);
 
752
}
 
753
 
 
754
/**
 
755
 * atspi_event_listener_deregister_from_callback:
 
756
 * @callback: (scope call): the #AtspiEventListenerCB registered against an
 
757
 *            event type.
 
758
 * @user_data: (closure): User data that was passed in for this callback.
 
759
 * @event_type: a string specifying the event type for which this
 
760
 *             listener is to be deregistered.
 
761
 *
 
762
 * Deregisters an #AtspiEventListenerCB from the registry, for a specific
 
763
 *             event type.
 
764
 *
 
765
 * Returns: #TRUE if successful, otherwise #FALSE.
 
766
 **/
 
767
gboolean
 
768
atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback,
 
769
                                               void *user_data,
 
770
                                               const gchar              *event_type,
 
771
                                               GError **error)
 
772
{
 
773
  char *category, *name, *detail;
 
774
  GPtrArray *matchrule_array;
 
775
  gint i;
 
776
  GList *l;
 
777
 
 
778
  if (!convert_event_type_to_dbus (event_type, &category, &name, &detail, &matchrule_array))
 
779
  {
 
780
    return FALSE;
 
781
  }
 
782
  if (!callback)
 
783
    {
 
784
      return FALSE;
 
785
    }
 
786
 
 
787
  for (l = event_listeners; l;)
 
788
  {
 
789
    EventListenerEntry *e = l->data;
 
790
    if (e->callback == callback &&
 
791
        e->user_data == user_data &&
 
792
        is_superset (category, e->category) &&
 
793
        is_superset (name, e->name) &&
 
794
        is_superset (detail, e->detail))
 
795
    {
 
796
      gboolean need_replace;
 
797
      DBusMessage *message, *reply;
 
798
      need_replace = (l == event_listeners);
 
799
      l = g_list_remove (l, e);
 
800
      if (need_replace)
 
801
        event_listeners = l;
 
802
      for (i = 0; i < matchrule_array->len; i++)
 
803
      {
 
804
        char *matchrule = g_ptr_array_index (matchrule_array, i);
 
805
        dbus_bus_remove_match (_atspi_bus(), matchrule, NULL);
 
806
      }
 
807
      message = dbus_message_new_method_call (atspi_bus_registry,
 
808
            atspi_path_registry,
 
809
            atspi_interface_registry,
 
810
            "DeregisterEvent");
 
811
      if (!message)
 
812
      return FALSE;
 
813
      dbus_message_append_args (message, DBUS_TYPE_STRING, &event_type, DBUS_TYPE_INVALID);
 
814
      reply = _atspi_dbus_send_with_reply_and_block (message, error);
 
815
      if (reply)
 
816
        dbus_message_unref (reply);
 
817
 
 
818
      listener_entry_free (e);
 
819
    }
 
820
    else l = g_list_next (l);
 
821
  }
 
822
  g_free (category);
 
823
  g_free (name);
 
824
  if (detail) g_free (detail);
 
825
  for (i = 0; i < matchrule_array->len; i++)
 
826
    g_free (g_ptr_array_index (matchrule_array, i));
 
827
  g_ptr_array_free (matchrule_array, TRUE);
 
828
  return TRUE;
 
829
}
 
830
 
 
831
/**
 
832
 * atspi_event_listener_deregister_no_data: (skip)
 
833
 * @callback: (scope call): the #AtspiEventListenerSimpleCB registered against
 
834
 *            an event type.
 
835
 * @event_type: a string specifying the event type for which this
 
836
 *             listener is to be deregistered.
 
837
 *
 
838
 * deregisters an #AtspiEventListenerSimpleCB from the registry, for a specific
 
839
 *             event type.
 
840
 *
 
841
 * Returns: #TRUE if successful, otherwise #FALSE.
 
842
 **/
 
843
gboolean
 
844
atspi_event_listener_deregister_no_data (AtspiEventListenerSimpleCB callback,
 
845
                                   const gchar              *event_type,
 
846
                                   GError **error)
 
847
{
 
848
  return atspi_event_listener_deregister_from_callback (remove_datum, callback,
 
849
                                                        event_type,
 
850
                                                        error);
 
851
}
 
852
 
 
853
static AtspiEvent *
 
854
atspi_event_copy (AtspiEvent *src)
 
855
{
 
856
  AtspiEvent *dst = g_new0 (AtspiEvent, 1);
 
857
  dst->type = g_strdup (src->type);
 
858
  dst->source = g_object_ref (src->source);
 
859
  dst->detail1 = src->detail1;
 
860
  dst->detail2 = src->detail2;
 
861
  g_value_init (&dst->any_data, G_VALUE_TYPE (&src->any_data));
 
862
  g_value_copy (&src->any_data, &dst->any_data);
 
863
  return dst;
 
864
}
 
865
 
 
866
static void
 
867
atspi_event_free (AtspiEvent *event)
 
868
{
 
869
  g_object_unref (event->source);
 
870
  g_free (event->type);
 
871
  g_value_unset (&event->any_data);
 
872
  g_free (event);
 
873
}
 
874
 
 
875
static gboolean
 
876
detail_matches_listener (const char *event_detail, const char *listener_detail)
 
877
{
 
878
  if (!listener_detail)
 
879
    return TRUE;
 
880
 
 
881
  if (!event_detail)
 
882
    return (listener_detail ? FALSE : TRUE);
 
883
 
 
884
  return !(listener_detail [strcspn (listener_detail, ":")] == '\0'
 
885
               ? strncmp (listener_detail, event_detail,
 
886
                          strcspn (event_detail, ":"))
 
887
               : strcmp (listener_detail, event_detail));
 
888
}
 
889
 
 
890
void
 
891
_atspi_send_event (AtspiEvent *e)
 
892
{
 
893
  char *category, *name, *detail;
 
894
  GList *l;
 
895
  GList *called_listeners = NULL;
 
896
 
 
897
  /* Ensure that the value is set to avoid a Python exception */
 
898
  /* TODO: Figure out how to do this without using a private field */
 
899
  if (e->any_data.g_type == 0)
 
900
  {
 
901
    g_value_init (&e->any_data, G_TYPE_INT);
 
902
    g_value_set_int (&e->any_data, 0);
 
903
  }
 
904
 
 
905
  if (!convert_event_type_to_dbus (e->type, &category, &name, &detail, NULL))
 
906
  {
 
907
    g_warning ("Atspi: Couldn't parse event: %s\n", e->type);
 
908
    return;
 
909
  }
 
910
  for (l = event_listeners; l; l = g_list_next (l))
 
911
  {
 
912
    EventListenerEntry *entry = l->data;
 
913
    if (!strcmp (category, entry->category) &&
 
914
        (entry->name == NULL || !strcmp (name, entry->name)) &&
 
915
        detail_matches_listener (detail, entry->detail))
 
916
    {
 
917
      GList *l2;
 
918
      for (l2 = called_listeners; l2; l2 = l2->next)
 
919
      {
 
920
        EventListenerEntry *e2 = l2->data;
 
921
        if (entry->callback == e2->callback && entry->user_data == e2->user_data)
 
922
          break;
 
923
      }
 
924
      if (!l2)
 
925
      {
 
926
        entry->callback (atspi_event_copy (e), entry->user_data);
 
927
        called_listeners = g_list_prepend (called_listeners, entry);
 
928
      }
 
929
    }
 
930
  }
 
931
  if (detail) g_free (detail);
 
932
  g_free (name);
 
933
  g_free (category);
 
934
  g_list_free (called_listeners);
 
935
}
 
936
 
 
937
DBusHandlerResult
 
938
_atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
 
939
{
 
940
  char *detail = NULL;
 
941
  const char *category = dbus_message_get_interface (message);
 
942
  const char *member = dbus_message_get_member (message);
 
943
  const char *signature = dbus_message_get_signature (message);
 
944
  gchar *name;
 
945
  gchar *converted_type;
 
946
  DBusMessageIter iter, iter_variant;
 
947
  dbus_message_iter_init (message, &iter);
 
948
  AtspiEvent e;
 
949
  dbus_int32_t detail1, detail2;
 
950
  char *p;
 
951
  GHashTable *cache = NULL;
 
952
 
 
953
  if (strcmp (signature, "siiv(so)") != 0 &&
 
954
      strcmp (signature, "siiva{sv}") != 0)
 
955
  {
 
956
    g_warning ("Got invalid signature %s for signal %s from interface %s\n", signature, member, category);
 
957
    return DBUS_HANDLER_RESULT_HANDLED;
 
958
  }
 
959
 
 
960
  memset (&e, 0, sizeof (e));
 
961
 
 
962
  if (category)
 
963
  {
 
964
    category = g_utf8_strrchr (category, -1, '.');
 
965
    if (category == NULL)
 
966
    {
 
967
      // TODO: Error
 
968
      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
969
    }
 
970
    category++;
 
971
  }
 
972
  dbus_message_iter_get_basic (&iter, &detail);
 
973
  dbus_message_iter_next (&iter);
 
974
  dbus_message_iter_get_basic (&iter, &detail1);
 
975
  e.detail1 = detail1;
 
976
  dbus_message_iter_next (&iter);
 
977
  dbus_message_iter_get_basic (&iter, &detail2);
 
978
  e.detail2 = detail2;
 
979
  dbus_message_iter_next (&iter);
 
980
 
 
981
  converted_type = convert_name_from_dbus (category, FALSE);
 
982
  name = convert_name_from_dbus (member, FALSE);
 
983
  detail = convert_name_from_dbus (detail, TRUE);
 
984
 
 
985
  if (strcasecmp  (category, name) != 0)
 
986
  {
 
987
    p = g_strconcat (converted_type, ":", name, NULL);
 
988
    g_free (converted_type);
 
989
    converted_type = p;
 
990
  }
 
991
  else if (detail [0] == '\0')
 
992
  {
 
993
    p = g_strconcat (converted_type, ":",  NULL);
 
994
    g_free (converted_type);
 
995
    converted_type = p;
 
996
  }
 
997
 
 
998
  if (detail[0] != '\0')
 
999
  {
 
1000
    p = g_strconcat (converted_type, ":", detail, NULL);
 
1001
    g_free (converted_type);
 
1002
    converted_type = p;
 
1003
  }
 
1004
  e.type = converted_type;
 
1005
  e.source = _atspi_ref_accessible (dbus_message_get_sender(message), dbus_message_get_path(message));
 
1006
  if (e.source == NULL)
 
1007
  {
 
1008
    g_warning ("Got no valid source accessible for signal for signal %s from interface %s\n", member, category);
 
1009
    return DBUS_HANDLER_RESULT_HANDLED;
 
1010
  }
 
1011
 
 
1012
  dbus_message_iter_recurse (&iter, &iter_variant);
 
1013
  switch (dbus_message_iter_get_arg_type (&iter_variant))
 
1014
  {
 
1015
    case DBUS_TYPE_STRUCT:
 
1016
    {
 
1017
      AtspiRect rect;
 
1018
      if (demarshal_rect (&iter_variant, &rect))
 
1019
      {
 
1020
        g_value_init (&e.any_data, ATSPI_TYPE_RECT);
 
1021
        g_value_set_boxed (&e.any_data, &rect);
 
1022
      }
 
1023
      else
 
1024
      {
 
1025
        AtspiAccessible *accessible;
 
1026
        accessible = _atspi_dbus_return_accessible_from_iter (&iter_variant);
 
1027
        g_value_init (&e.any_data, ATSPI_TYPE_ACCESSIBLE);
 
1028
        g_value_set_instance (&e.any_data, accessible);
 
1029
        if (accessible)
 
1030
          g_object_unref (accessible);  /* value now owns it */
 
1031
      }
 
1032
      break;
 
1033
    }
 
1034
    case DBUS_TYPE_STRING:
 
1035
    {
 
1036
      dbus_message_iter_get_basic (&iter_variant, &p);
 
1037
      g_value_init (&e.any_data, G_TYPE_STRING);
 
1038
      g_value_set_string (&e.any_data, p);
 
1039
      break;
 
1040
    }
 
1041
  default:
 
1042
    break;
 
1043
  }
 
1044
 
 
1045
  dbus_message_iter_next (&iter);
 
1046
  if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY)
 
1047
  {
 
1048
    /* new form -- parse properties sent with event */
 
1049
    cache = _atspi_dbus_update_cache_from_dict (e.source, &iter);
 
1050
  }
 
1051
 
 
1052
  if (!strncmp (e.type, "object:children-changed", 23))
 
1053
  {
 
1054
    cache_process_children_changed (&e);
 
1055
  }
 
1056
  else if (!strncmp (e.type, "object:property-change", 22))
 
1057
  {
 
1058
    cache_process_property_change (&e);
 
1059
  }
 
1060
  else if (!strncmp (e.type, "object:state-changed", 20))
 
1061
  {
 
1062
    cache_process_state_changed (&e);
 
1063
  }
 
1064
  else if (!strncmp (e.type, "focus", 5))
 
1065
  {
 
1066
    /* BGO#663992 - TODO: figure out the real problem */
 
1067
    e.source->cached_properties &= ~(ATSPI_CACHE_STATES);
 
1068
  }
 
1069
 
 
1070
  _atspi_send_event (&e);
 
1071
 
 
1072
  if (cache)
 
1073
    _atspi_accessible_unref_cache (e.source);
 
1074
 
 
1075
  g_free (converted_type);
 
1076
  g_free (name);
 
1077
  g_free (detail);
 
1078
  g_object_unref (e.source);
 
1079
  g_value_unset (&e.any_data);
 
1080
  return DBUS_HANDLER_RESULT_HANDLED;
 
1081
}
 
1082
 
 
1083
G_DEFINE_BOXED_TYPE (AtspiEvent, atspi_event, atspi_event_copy, atspi_event_free)