event.c 43.6 KB
Newer Older
1
2
3
4
/*
 * AT-SPI - Assistive Technology Service Provider Interface
 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
 *
5
 * Copyright 2011, F123 Consulting & Mais Diferenças
6
 * Copyright 2008, 2009, Codethink Ltd.
7
8
9
10
 * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
 * Copyright 2001, 2002, 2003 Ximian, Inc.
 *
 * This library is free software; you can redistribute it and/or
Mike Gorse's avatar
Mike Gorse committed
11
 * modify it under the terms of the GNU Lesser General Public
12
 * License as published by the Free Software Foundation; either
Mike Gorse's avatar
Mike Gorse committed
13
 * version 2.1 of the License, or (at your option) any later version.
14
15
16
17
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Mike Gorse's avatar
Mike Gorse committed
18
 * Lesser General Public License for more details.
19
 *
Mike Gorse's avatar
Mike Gorse committed
20
 * You should have received a copy of the GNU Lesser General Public
21
 * License along with this library; if not, write to the
Mike Gorse's avatar
Mike Gorse committed
22
23
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
24
25
26
 */

#include <string.h>
27
#include <ctype.h>
28
29
30

#include <atk/atk.h>
#include <droute/droute.h>
31
#include <atspi/atspi.h>
32

33
#include "bridge.h"
34
#include "accessible-register.h"
35

36
#include "spi-dbus.h"
Mike Gorse's avatar
Mike Gorse committed
37
38
#include "event.h"
#include "object.h"
39
40
41
42
43
44

static GArray *listener_ids = NULL;

static gint atk_bridge_key_event_listener_id;
static gint atk_bridge_focus_tracker_id;

45
46
GMainContext *spi_context = NULL;

47
48
/*---------------------------------------------------------------------------*/

49
50
51
52
#define ITF_EVENT_OBJECT   "org.a11y.atspi.Event.Object"
#define ITF_EVENT_WINDOW   "org.a11y.atspi.Event.Window"
#define ITF_EVENT_DOCUMENT "org.a11y.atspi.Event.Document"
#define ITF_EVENT_FOCUS    "org.a11y.atspi.Event.Focus"
53
54
55

/*---------------------------------------------------------------------------*/

56
57
typedef struct _SpiReentrantCallClosure 
{
58
  DBusConnection *bus;
59
60
  GMainLoop   *loop;
  DBusMessage *reply;
61
  guint timeout;
62
63
} SpiReentrantCallClosure;

64
65
66
67
68
static void
switch_main_context (GMainContext *cnx)
{
  GList *list;

69
70
  if (spi_global_app_data->server)
    atspi_dbus_server_setup_with_g_main (spi_global_app_data->server, cnx);
71
  atspi_dbus_connection_setup_with_g_main (spi_global_app_data->bus, cnx);
72
  atspi_set_main_context (cnx);
73
  for (list = spi_global_app_data->direct_connections; list; list = list->next)
74
    atspi_dbus_connection_setup_with_g_main (list->data, cnx);
75

76
77
  if (_atk_bridge_remove_pending_application_registration (spi_global_app_data))
    _atk_bridge_schedule_application_registration (spi_global_app_data);
78
79
}

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
guint
spi_idle_add(GSourceFunc    function, gpointer       data)
{
  GSource *source;
  guint id;

  source = g_idle_source_new ();
  g_source_set_callback (source, function, data, NULL);
  id = g_source_attach (source, spi_context);
  g_source_unref (source);

  return id;
}

guint
spi_timeout_add_seconds (gint interval, GSourceFunc function, gpointer    data)
{
  GSource *source;
  guint id;

  source = g_timeout_source_new_seconds (interval);
  g_source_set_callback (source, function, data, NULL);
  id = g_source_attach (source, spi_context);
  g_source_unref (source);

  return id;
}

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
guint
spi_timeout_add_full (gint priority, guint interval, GSourceFunc function,
                      gpointer data, GDestroyNotify notify)
{
  GSource *source;
  guint id;

  source = g_timeout_source_new (interval);
  g_source_set_priority (source, priority);
  g_source_set_callback (source, function, data, notify);
  id = g_source_attach (source, spi_context);
  g_source_unref (source);

  return id;
}

124
static void
Mark Doffman's avatar
Mark Doffman committed
125
set_reply (DBusPendingCall * pending, void *user_data)
126
{
127
  SpiReentrantCallClosure* closure = (SpiReentrantCallClosure *) user_data; 
128

129
  closure->reply = dbus_pending_call_steal_reply (pending);
Mike Gorse's avatar
Mike Gorse committed
130
  dbus_pending_call_unref (pending);
131
  switch_main_context (spi_context);
132
  g_main_loop_quit (closure->loop);
133
134
}

135
136
137
138
139
static gboolean
timeout_reply (void *data)
{
  SpiReentrantCallClosure *closure = data;

140
  switch_main_context (spi_context);
141
  g_main_loop_quit (closure->loop);
142
143
144
145
  closure->timeout = -1;
  return FALSE;
}

146
static DBusMessage *
Mark Doffman's avatar
Mark Doffman committed
147
send_and_allow_reentry (DBusConnection * bus, DBusMessage * message)
148
{
Mark Doffman's avatar
Mark Doffman committed
149
  DBusPendingCall *pending;
150
  SpiReentrantCallClosure closure;
151
  GSource *source;
152

153
  closure.bus = bus;
154
  closure.loop = g_main_loop_new (spi_global_app_data->main_context, FALSE);
155
  closure.reply = NULL;
156
  switch_main_context (spi_global_app_data->main_context);
157

158
  if (!dbus_connection_send_with_reply (bus, message, &pending, 9000) || !pending)
159
    {
160
      switch_main_context (spi_context);
161
      return NULL;
162
    }
163
  dbus_pending_call_set_notify (pending, set_reply, (void *) &closure, NULL);
164
165
  source = g_timeout_source_new (500);
  g_source_set_callback (source, timeout_reply, &closure, NULL);
166
  closure.timeout = g_source_attach (source, spi_global_app_data->main_context);
167
  g_source_unref (source);
168
  g_main_loop_run  (closure.loop);
169
  if (closure.timeout != -1)
170
    g_source_destroy (source);
171
172
  
  g_main_loop_unref (closure.loop);
173
174
  if (!closure.reply)
    dbus_pending_call_cancel (pending);
175
  return closure.reply;
176
177
}

178
179
180
181
182
183
184
void
atk_bridge_set_event_context(GMainContext *cnx)
{
  spi_context = cnx;
  switch_main_context(spi_context);
}

185
186
187
188
189
190
191
192
/*---------------------------------------------------------------------------*/

/*
 * Functionality related to sending device events from the application.
 *
 * This is used for forwarding key events on to the registry daemon.
 */

193
static gboolean
Mark Doffman's avatar
Mark Doffman committed
194
Accessibility_DeviceEventController_NotifyListenersSync (const
195
                                                         AtspiDeviceEvent
Mark Doffman's avatar
Mark Doffman committed
196
                                                         * key_event)
197
198
199
200
{
  DBusMessage *message;
  dbus_bool_t consumed = FALSE;

201
  message =
Mark Doffman's avatar
Mark Doffman committed
202
    dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY,
203
204
                                  ATSPI_DBUS_PATH_DEC,
                                  ATSPI_DBUS_INTERFACE_DEC,
Mark Doffman's avatar
Mark Doffman committed
205
206
207
                                  "NotifyListenersSync");

  if (spi_dbus_marshal_deviceEvent (message, key_event))
208
    {
Mark Doffman's avatar
Mark Doffman committed
209
      DBusMessage *reply =
210
        send_and_allow_reentry (spi_global_app_data->bus, message);
Mark Doffman's avatar
Mark Doffman committed
211
212
213
214
      if (reply)
        {
          DBusError error;
          dbus_error_init (&error);
Mike Gorse's avatar
Mike Gorse committed
215
216
217
218
219
220
          if (!dbus_message_get_args (reply, &error, DBUS_TYPE_BOOLEAN,
              &consumed, DBUS_TYPE_INVALID))
            {
              /* TODO: print a warning */
              dbus_error_free (&error);
            }
Mark Doffman's avatar
Mark Doffman committed
221
222
          dbus_message_unref (reply);
        }
223
    }
Mark Doffman's avatar
Mark Doffman committed
224
  dbus_message_unref (message);
225
226
227
228
  return consumed;
}

static void
229
spi_init_keystroke_from_atk_key_event (AtspiDeviceEvent * keystroke,
Mark Doffman's avatar
Mark Doffman committed
230
                                       AtkKeyEventStruct * event)
231
{
Mark Doffman's avatar
Mark Doffman committed
232
233
  keystroke->id = (dbus_int32_t) event->keyval;
  keystroke->hw_code = (dbus_int16_t) event->keycode;
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  keystroke->timestamp = (dbus_uint32_t) event->timestamp;
  keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF);
  if (event->string)
    {
      gunichar c;

      keystroke->event_string = g_strdup (event->string);
      c = g_utf8_get_char_validated (event->string, -1);
      if (c > 0 && g_unichar_isprint (c))
        keystroke->is_text = TRUE;
      else
        keystroke->is_text = FALSE;
    }
  else
    {
      keystroke->event_string = g_strdup ("");
      keystroke->is_text = FALSE;
    }
  switch (event->type)
    {
    case (ATK_KEY_EVENT_PRESS):
255
      keystroke->type = ATSPI_KEY_PRESSED;
256
257
      break;
    case (ATK_KEY_EVENT_RELEASE):
258
      keystroke->type = ATSPI_KEY_RELEASED;
259
260
261
262
263
      break;
    default:
      keystroke->type = 0;
      break;
    }
Mark Doffman's avatar
Mark Doffman committed
264
265
266
267
268
269
#if 0
  g_print
    ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
     (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
     (int) keystroke->modifiers, keystroke->event_string,
     (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
270
271
272
273
274
#endif
}


static gint
Mark Doffman's avatar
Mark Doffman committed
275
spi_atk_bridge_key_listener (AtkKeyEventStruct * event, gpointer data)
276
{
Mark Doffman's avatar
Mark Doffman committed
277
  gboolean result;
278
  AtspiDeviceEvent key_event;
279
280
281

  spi_init_keystroke_from_atk_key_event (&key_event, event);

Mark Doffman's avatar
Mark Doffman committed
282
283
  result =
    Accessibility_DeviceEventController_NotifyListenersSync (&key_event);
284

Mark Doffman's avatar
Mark Doffman committed
285
286
  if (key_event.event_string)
    g_free (key_event.event_string);
287
288
289
290
291
292

  return result;
}

/*---------------------------------------------------------------------------*/

293
static const void *
294
validate_for_dbus (const gint type,
295
296
297
298
299
300
301
302
              const void *val)
{
  switch (type)
    {
      case DBUS_TYPE_STRING:
      case DBUS_TYPE_OBJECT_PATH:
	   if (!val)
	      return "";
303
304
305
306
307
	   else if (!g_utf8_validate (val, -1, NULL))
             {
	       g_warning ("atk-bridge: Received bad UTF-8 string when emitting event");
	       return "";
               }
308
309
310
311
312
313
314
	   else
	      return val;
      default:
	   return val;
    }
}

Mark Doffman's avatar
Mark Doffman committed
315
static void
316
317
318
append_basic (DBusMessageIter *iter,
              const char *type,
              const void *val)
319
{
320
  DBusMessageIter sub;
321

322
  dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &sub);
323

324
    val = validate_for_dbus ((int) *type, val);
325
    dbus_message_iter_append_basic(&sub, (int) *type, &val);
326

327
  dbus_message_iter_close_container(iter, &sub);
328
329
330
}

static void
331
332
333
append_rect (DBusMessageIter *iter,
             const char *type,
             const void *val)
334
{
335
336
  DBusMessageIter variant, sub;
  const AtkRectangle *rect = (const AtkRectangle *) val;
337

338
  dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &variant);
339

340
341
342
343
344
345
    dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub);

      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x));
      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y));
      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width));
      dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height));
346

347
348
349
350
351
352
353
354
355
356
357
358
359
    dbus_message_iter_close_container (&variant, &sub);

  dbus_message_iter_close_container(iter, &variant);
}

static void
append_object (DBusMessageIter *iter,
               const char *type,
               const void *val)
{
  spi_object_append_v_reference (iter, ATK_OBJECT (val));
}

Mike Gorse's avatar
Mike Gorse committed
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
static gchar *
signal_name_to_dbus (const gchar *s)
{
  gchar *ret = g_strdup (s);
  gchar *t;

  if (!ret)
    return NULL;
  ret [0] = toupper (ret [0]);
  while ((t = strchr (ret, '-')) != NULL)
  {
    memmove (t, t + 1, strlen (t));
    *t = toupper (*t);
  }
  return ret;
}

377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/*
 * Converts names of the form "active-descendant-changed" to
 * "ActiveDescendantChanged"
 */
static gchar *
ensure_proper_format (const char *name)
{
  gchar *ret = (gchar *) g_malloc (strlen (name) * 2 + 2);
  gchar *p = ret;
  gboolean need_upper = TRUE;

  if (!ret)
    return NULL;
  while (*name)
    {
      if (need_upper)
        {
          *p++ = toupper (*name);
          need_upper = FALSE;
        }
      else if (*name == '-')
        need_upper = TRUE;
      else if (*name == ':')
        {
          need_upper = TRUE;
          *p++ = *name;
        }
      else
        *p++ = *name;
      name++;
    }
  *p = '\0';
  return ret;
}

Mike Gorse's avatar
Mike Gorse committed
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
void
append_properties (GArray *properties, event_data *evdata)
{
  GSList *ls;
  gint i;

  for (ls = evdata->properties; ls; ls = ls->next)
  {
    gboolean dup = FALSE;
    for (i = 0; i < properties->len; i++)
    {
      if (ls->data == g_array_index (properties, AtspiPropertyDefinition *, i))
      {
        dup = TRUE;
        break;
      }
    }
    if (!dup)
      g_array_append_val (properties, ls->data);
  }
}

434
static gboolean
435
436
signal_is_needed (AtkObject *obj, const gchar *klass, const gchar *major,
                  const gchar *minor, GArray **properties)
437
438
439
440
441
{
  gchar *data [4];
  event_data *evdata;
  gboolean ret = FALSE;
  GList *list;
Mike Gorse's avatar
Mike Gorse committed
442
  GArray *props = NULL;
443

444
445
446
  if (!spi_global_app_data->events_initialized)
    return TRUE;

447
  data [0] = ensure_proper_format (klass[0] ? klass + 21 : klass);
448
449
450
451
  data [1] = ensure_proper_format (major);
  data [2] = ensure_proper_format (minor);
  data [3] = NULL;

452
453
454
455
  /* Hack: Always pass events that update the cache.
   * TODO: FOr 2.2, have at-spi2-core define a special "cache listener" for
   * this instead, so that we don't send these if no one is listening */
  if (!g_strcmp0 (data [1], "ChildrenChanged") ||
456
      ((!g_strcmp0 (data [1], "PropertyChange")) &&
457
458
459
460
       (!g_strcmp0 (data [2], "AccessibleName") ||
        !g_strcmp0 (data [2], "AccessibleDescription") ||
        !g_strcmp0 (data [2], "AccessibleParent") ||
        !g_strcmp0 (data [2], "AccessibleRole"))) ||
461
      !g_strcmp0 (data [1], "StateChanged"))
462
463
464
465
466
467
468
469
470
471
472
473
  {
    if (minor && !g_strcmp0 (minor, "defunct"))
      ret = TRUE;
    else
    {
      AtkStateSet *set = atk_object_ref_state_set (obj);
      AtkState state = ((!g_strcmp0 (data[1], "ChildrenChanged")) ?
                        ATK_STATE_MANAGES_DESCENDANTS : ATK_STATE_TRANSIENT);
      ret = !atk_state_set_contains_state (set, state);
      g_object_unref (set);
    }
  }
474

475
476
477
  /* Hack: events such as "object::text-changed::insert:system" as
     generated by Gecko */
  data [2][strcspn (data [2], ":")] = '\0';
Mike Gorse's avatar
Mike Gorse committed
478

479
480
481
482
483
484
  for (list = spi_global_app_data->events; list; list = list->next)
    {
      evdata = list->data;
      if (spi_event_is_subtype (data, evdata->data))
        {
          ret = TRUE;
Mike Gorse's avatar
Mike Gorse committed
485
486
487
          if (!props)
          props = g_array_new (TRUE, TRUE, sizeof (AtspiPropertyDefinition *));
          append_properties (props, evdata);
488
489
490
491
492
493
        }
    }

  g_free (data [2]);
  g_free (data [1]);
  g_free (data [0]);
Mike Gorse's avatar
Mike Gorse committed
494
  *properties = props;
495
496
497
  return ret;
}

498
499
500
/* Convert a : to a / so that listeners can use arg0path to match only
 *  * the prefix */
static char *
501
adapt_minor_for_dbus (const char *source)
502
503
504
505
506
507
508
509
{
  gchar *ret = g_strdup (source);
  int i = strcspn (ret, ":");
  if (ret[i] == ':')
    ret[i] = '/';
  return ret;
}

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
/*
 * Emits an AT-SPI event.
 * AT-SPI events names are split into three parts:
 * class:major:minor
 * This is mapped onto D-Bus events as:
 * D-Bus Interface:Signal Name:Detail argument
 *
 * Marshals a basic type into the 'any_data' attribute of
 * the AT-SPI event.
 */
static void 
emit_event (AtkObject  *obj,
            const char *klass,
            const char *major,
            const char *minor,
            dbus_int32_t detail1,
            dbus_int32_t detail2,
            const char *type,
            const void *val,
            void (*append_variant) (DBusMessageIter *, const char *, const void *))
{
  DBusConnection *bus = spi_global_app_data->bus;
Mike Gorse's avatar
Mike Gorse committed
532
  char *path;
533
  char *minor_dbus;
534

Bastien Nocera's avatar
Bastien Nocera committed
535
  gchar *cname;
536
  DBusMessage *sig;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
537
  DBusMessageIter iter, iter_dict, iter_dict_entry;
Mike Gorse's avatar
Mike Gorse committed
538
  GArray *properties = NULL;
539
540
541
542
543
  
  if (!klass) klass = "";
  if (!major) major = "";
  if (!minor) minor = "";
  if (!type) type = "u";
544

545
  if (!signal_is_needed (obj, klass, major, minor, &properties))
546
547
    return;

548
  path =  spi_register_object_to_path (spi_global_register, G_OBJECT (obj));
549
  g_return_if_fail (path != NULL);
550

551
552
553
554
555
  /*
   * This is very annoying, but as '-' isn't a legal signal
   * name in D-Bus (Why not??!?) The names need converting
   * on this side, and again on the client side.
   */
Mike Gorse's avatar
Mike Gorse committed
556
  cname = signal_name_to_dbus (major);
557
558
559
560
  sig = dbus_message_new_signal(path, klass, cname);

  dbus_message_iter_init_append(sig, &iter);

561
562
563
  minor_dbus = adapt_minor_for_dbus (minor);
  dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor_dbus);
  g_free (minor_dbus);
564
565
566
  dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1);
  dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2);
  append_variant (&iter, type, val);
Mike Gorse's avatar
Mike Gorse committed
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588

  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_dict);
  /* Add requested properties, unless the object is being marked defunct, in
     which case it's safest not to touch it */
  if (minor == NULL || strcmp (minor, "defunct") != 0 || detail1 == 0)
  {
    if (properties)
    {
      gint i;
      for (i = 0; i < properties->len; i++)
      {
        AtspiPropertyDefinition *prop = g_array_index (properties, AtspiPropertyDefinition *, i);
        dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY, NULL,
                                          &iter_dict_entry);
        dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop->name);
        prop->func (&iter_dict_entry, obj);
        dbus_message_iter_close_container (&iter_dict, &iter_dict_entry);
      }
      g_array_free (properties, TRUE);
    }
  }
    dbus_message_iter_close_container (&iter, &iter_dict);
589
590
591

  dbus_connection_send(bus, sig, NULL);
  dbus_message_unref(sig);
592
593
594

  if (g_strcmp0 (cname, "ChildrenChanged") != 0)
    spi_object_lease_if_needed (G_OBJECT (obj));
595
596

  g_free(cname);
597
  g_free (path);
598
599
600
601
602
603
604
605
606
}

/*---------------------------------------------------------------------------*/

/*
 * The focus listener handles the ATK 'focus' signal and forwards it
 * as the AT-SPI event, 'focus:'
 */
static void
Mark Doffman's avatar
Mark Doffman committed
607
focus_tracker (AtkObject * accessible)
608
{
609
610
  emit_event (accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0,
              DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
611
612
613
614
}

/*---------------------------------------------------------------------------*/

Mike Gorse's avatar
Mike Gorse committed
615
#define PCHANGE "PropertyChange"
616
617
618
619
620
621
622
623
624
625

/* 
 * This handler handles the following ATK signals and
 * converts them to AT-SPI events:
 *  
 * Gtk:AtkObject:property-change -> object:property-change:(property-name)
 *
 * The property-name is part of the ATK property-change signal.
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
626
627
628
property_event_listener (GSignalInvocationHint * signal_hint,
                         guint n_param_values,
                         const GValue * param_values, gpointer data)
629
630
631
632
633
634
635
{
  AtkObject *accessible;
  AtkPropertyValues *values;

  const gchar *pname = NULL;

  AtkObject *otemp;
Bastien Nocera's avatar
Bastien Nocera committed
636
  const gchar *s1;
637
  gint i;
638

639
  accessible = g_value_get_object (&param_values[0]);
Mark Doffman's avatar
Mark Doffman committed
640
  values = (AtkPropertyValues *) g_value_get_pointer (&param_values[1]);
641
642
643
644
645
646

  pname = values[0].property_name;

  /* TODO Could improve this control statement by matching
   * on only the end of the signal names,
   */
647
648
649
650
651
652
653
  if (strcmp (pname, "accessible-name") == 0)
    {
      s1 = atk_object_get_name (accessible);
      if (s1 != NULL)
        emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
                    DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
    }
Mike Gorse's avatar
Mike Gorse committed
654
  else if (strcmp (pname, "accessible-description") == 0)
655
656
657
658
659
660
    {
      s1 = atk_object_get_description (accessible);
      if (s1 != NULL)
        emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
                    DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
    }
Mike Gorse's avatar
Mike Gorse committed
661
  else if (strcmp (pname, "accessible-parent") == 0)
662
663
664
665
666
667
    {
      otemp = atk_object_get_parent (accessible);
      if (otemp != NULL)
        emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
                    "(so)", otemp, append_object);
    }
Mike Gorse's avatar
Mike Gorse committed
668
669
  else if (strcmp (pname, "accessible-role") == 0)
    {
670
      i = atk_object_get_role (accessible);
Mike Gorse's avatar
Mike Gorse committed
671
      emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
672
                    DBUS_TYPE_UINT32_AS_STRING, GINT_TO_POINTER(i), append_basic);
Mike Gorse's avatar
Mike Gorse committed
673
674
    }
  else if (strcmp (pname, "accessible-table-summary") == 0)
675
    {
Mark Doffman's avatar
Mark Doffman committed
676
      otemp = atk_table_get_summary (ATK_TABLE (accessible));
677
678
679
      if (otemp != NULL)
        emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
                    "(so)", otemp, append_object);
680
681
682
683
    }
  else if (strcmp (pname, "accessible-table-column-header") == 0)
    {
      i = g_value_get_int (&(values->new_value));
Mark Doffman's avatar
Mark Doffman committed
684
      otemp = atk_table_get_column_header (ATK_TABLE (accessible), i);
685
686
687
      if (otemp != NULL)
        emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
                    "(so)", otemp, append_object);
688
689
690
691
    }
  else if (strcmp (pname, "accessible-table-row-header") == 0)
    {
      i = g_value_get_int (&(values->new_value));
Mark Doffman's avatar
Mark Doffman committed
692
      otemp = atk_table_get_row_header (ATK_TABLE (accessible), i);
693
694
695
      if (otemp != NULL)
        emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
                    "(so)", otemp, append_object);
696
697
698
699
    }
  else if (strcmp (pname, "accessible-table-row-description") == 0)
    {
      i = g_value_get_int (&(values->new_value));
700
701
702
      s1 = atk_table_get_row_description (ATK_TABLE (accessible), i);
      emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
                  DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
703
704
705
706
    }
  else if (strcmp (pname, "accessible-table-column-description") == 0)
    {
      i = g_value_get_int (&(values->new_value));
707
708
709
      s1 = atk_table_get_column_description (ATK_TABLE (accessible), i);
      emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
                  DBUS_TYPE_STRING_AS_STRING, s1, append_basic);
710
711
712
    }
  else if (strcmp (pname, "accessible-table-caption-object") == 0)
    {
Mark Doffman's avatar
Mark Doffman committed
713
      otemp = atk_table_get_caption (ATK_TABLE (accessible));
714
715
      emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
                  "(so)", otemp, append_object);
716
717
718
    }
  else
    {
719
720
      emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0,
            DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
721
722
723
724
725
726
727
728
729
730
731
732
733
734
    }
  return TRUE;
}

/*---------------------------------------------------------------------------*/

#define STATE_CHANGED "state-changed"

/*
 * The state event listener handles 'Gtk:AtkObject:state-change' ATK signals
 * and forwards them as object:state-changed:(param-name) AT-SPI events. Where
 * the param-name is part of the ATK state-change signal.
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
735
736
737
state_event_listener (GSignalInvocationHint * signal_hint,
                      guint n_param_values,
                      const GValue * param_values, gpointer data)
738
739
{
  AtkObject *accessible;
Mike Gorse's avatar
Mike Gorse committed
740
  const gchar *pname;
741
742
  guint detail1;

Mark Doffman's avatar
Mark Doffman committed
743
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
744
  pname = g_value_get_string (&param_values[1]);
745
746

  detail1 = (g_value_get_boolean (&param_values[2])) ? 1 : 0;
747
748
  emit_event (accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0,
              DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
749

750
  if (!g_strcmp0 (pname, "defunct") && detail1)
751
752
    spi_register_deregister_object (spi_global_register, G_OBJECT (accessible),
                                    TRUE);
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
  return TRUE;
}

/*---------------------------------------------------------------------------*/

/*
 * The window event listener handles the following ATK signals and forwards
 * them as AT-SPI events:
 *
 * window:create     -> window:create
 * window:destroy    -> window:destroy
 * window:minimize   -> window:minimize
 * window:maximize   -> window:maximize
 * window:activate   -> window:activate
 * window:deactivate -> window:deactivate
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
770
771
772
window_event_listener (GSignalInvocationHint * signal_hint,
                       guint n_param_values,
                       const GValue * param_values, gpointer data)
773
774
775
776
{
  AtkObject *accessible;
  GSignalQuery signal_query;
  const gchar *name, *s;
Mark Doffman's avatar
Mark Doffman committed
777

778
779
780
  g_signal_query (signal_hint->signal_id, &signal_query);
  name = signal_query.signal_name;

Mark Doffman's avatar
Mark Doffman committed
781
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
782
  s = atk_object_get_name (accessible);
783
784
  emit_event (accessible, ITF_EVENT_WINDOW, name, "", 0, 0,
              DBUS_TYPE_STRING_AS_STRING, s, append_basic);
785
786
787
788
789
790
791
792
793
794
795
796
797

  return TRUE;
}

/*---------------------------------------------------------------------------*/

/* 
 * The document event listener handles the following ATK signals
 * and converts them to AT-SPI events:
 *
 * Gtk:AtkDocument:load-complete ->  document:load-complete
 * Gtk:AtkDocument:load-stopped  ->  document:load-stopped
 * Gtk:AtkDocument:reload        ->  document:reload
798
 * Gtk:AtkDocument:page-changed  ->  document:page-changed
799
800
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
801
document_event_listener (GSignalInvocationHint * signal_hint,
802
                         guint n_param_values,
Mark Doffman's avatar
Mark Doffman committed
803
                         const GValue * param_values, gpointer data)
804
805
806
807
{
  AtkObject *accessible;
  GSignalQuery signal_query;
  const gchar *name, *s;
808
  gint detail1 = 0;
809
810
811
812

  g_signal_query (signal_hint->signal_id, &signal_query);
  name = signal_query.signal_name;

813
814
815
816
  if (n_param_values > 0) // on the case of page-changed
    if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
      detail1 = g_value_get_int (&param_values[1]);

Mark Doffman's avatar
Mark Doffman committed
817
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
818
  s = atk_object_get_name (accessible);
819
  emit_event (accessible, ITF_EVENT_DOCUMENT, name, "", detail1, 0,
820
              DBUS_TYPE_STRING_AS_STRING, s, append_basic);
821
822
823
824
825
826
827
828
829
830
831

  return TRUE;
}

/*---------------------------------------------------------------------------*/

/*
 * Signal handler for  "Gtk:AtkComponent:bounds-changed". Converts
 * this to an AT-SPI event - "object:bounds-changed".
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
832
bounds_event_listener (GSignalInvocationHint * signal_hint,
833
                       guint n_param_values,
Mark Doffman's avatar
Mark Doffman committed
834
                       const GValue * param_values, gpointer data)
835
836
837
838
{
  AtkObject *accessible;
  AtkRectangle *atk_rect;
  GSignalQuery signal_query;
Bastien Nocera's avatar
Bastien Nocera committed
839
  const gchar *name;
840
841
842
843

  g_signal_query (signal_hint->signal_id, &signal_query);
  name = signal_query.signal_name;

Mark Doffman's avatar
Mark Doffman committed
844
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
845
846

  if (G_VALUE_HOLDS_BOXED (param_values + 1))
847
  {
848
849
    atk_rect = g_value_get_boxed (param_values + 1);

850
851
852
    emit_event (accessible, ITF_EVENT_OBJECT, name, "", 0, 0,
                "(iiii)", atk_rect, append_rect);
  }
853
854
855
856
857
858
859
860
861
862
863
  return TRUE;
}

/*---------------------------------------------------------------------------*/

/* 
 * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and 
 * converts it to the AT-SPI signal - 'object:active-descendant-changed'.
 *
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
864
865
866
active_descendant_event_listener (GSignalInvocationHint * signal_hint,
                                  guint n_param_values,
                                  const GValue * param_values, gpointer data)
867
868
869
870
{
  AtkObject *accessible;
  AtkObject *child;
  GSignalQuery signal_query;
Bastien Nocera's avatar
Bastien Nocera committed
871
  const gchar *name;
872
873
874
875
876
  gint detail1;

  g_signal_query (signal_hint->signal_id, &signal_query);
  name = signal_query.signal_name;

Mark Doffman's avatar
Mark Doffman committed
877
878
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
  child = ATK_OBJECT (g_value_get_pointer (&param_values[1]));
879
880
881
882
  g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);

  detail1 = atk_object_get_index_in_parent (child);

883
884
  emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, 0,
              "(so)", child, append_object);
885
886
887
888
889
890
891
892
893
894
895
  return TRUE;
}

/*---------------------------------------------------------------------------*/

/* 
 * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and
 * converts it to the AT-SPI signal - 'object:link-selected'
 *
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
896
897
898
link_selected_event_listener (GSignalInvocationHint * signal_hint,
                              guint n_param_values,
                              const GValue * param_values, gpointer data)
899
900
901
902
{
  AtkObject *accessible;
  GSignalQuery signal_query;
  const gchar *name, *minor;
903
  gint detail1 = 0;
904
905
906
907

  g_signal_query (signal_hint->signal_id, &signal_query);
  name = signal_query.signal_name;

Mark Doffman's avatar
Mark Doffman committed
908
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
909
910
911
  minor = g_quark_to_string (signal_hint->detail);

  if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
Mark Doffman's avatar
Mark Doffman committed
912
    detail1 = g_value_get_int (&param_values[1]);
913

914
915
  emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0,
              DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
916
917
918
919
920
921
922
923
  return TRUE;
}

/*---------------------------------------------------------------------------*/

/* 
 * Handles the ATK signal 'Gtk:AtkText:text-changed' and
 * converts it to the AT-SPI signal - 'object:text-changed'
924
925
 * This signal is deprecated by Gtk:AtkText:text-insert
 * and Gtk:AtkText:text-remove
926
927
928
 *
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
929
930
931
text_changed_event_listener (GSignalInvocationHint * signal_hint,
                             guint n_param_values,
                             const GValue * param_values, gpointer data)
932
933
934
935
936
{
  AtkObject *accessible;
  GSignalQuery signal_query;
  const gchar *name, *minor;
  gchar *selected;
937
  gint detail1 = 0, detail2 = 0;
938
939
940
941

  g_signal_query (signal_hint->signal_id, &signal_query);
  name = signal_query.signal_name;

Mark Doffman's avatar
Mark Doffman committed
942
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
943
944
945
  minor = g_quark_to_string (signal_hint->detail);

  if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
Mark Doffman's avatar
Mark Doffman committed
946
    detail1 = g_value_get_int (&param_values[1]);
947
948

  if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
Mark Doffman's avatar
Mark Doffman committed
949
    detail2 = g_value_get_int (&param_values[2]);
950

Mark Doffman's avatar
Mark Doffman committed
951
952
  selected =
    atk_text_get_text (ATK_TEXT (accessible), detail1, detail1 + detail2);
953

954
955
  emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
              DBUS_TYPE_STRING_AS_STRING, selected, append_basic);
Michael Meeks's avatar
Michael Meeks committed
956
957
  g_free (selected);

958
959
960
  return TRUE;
}

961
962
963
964
965
966
967
968
969
970
971
972
973
974
/* 
 * Handles the ATK signal 'Gtk:AtkText:text-insert' and
 * converts it to the AT-SPI signal - 'object:text-changed'
 *
 */
static gboolean
text_insert_event_listener (GSignalInvocationHint * signal_hint,
                            guint n_param_values,
                            const GValue * param_values, gpointer data)
{
  AtkObject *accessible;
  guint text_changed_signal_id;
  GSignalQuery signal_query;
  const gchar *name;
Mike Gorse's avatar
Mike Gorse committed
975
976
  const gchar *minor_raw, *text;
  gchar *minor;
977
  gint detail1 = 0, detail2 = 0;
978
979
980
981
982
983
984
985
986
987
988

  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
  /* Get signal name for 'Gtk:AtkText:text-changed' so
   * we convert it to the AT-SPI signal - 'object:text-changed'
   */
  text_changed_signal_id = g_signal_lookup ("text-changed", G_OBJECT_TYPE (accessible));
  g_signal_query (text_changed_signal_id, &signal_query);
  name = signal_query.signal_name;


  /* Add the insert and keep any detail coming from atk */
Mike Gorse's avatar
Mike Gorse committed
989
990
991
  minor_raw = g_quark_to_string (signal_hint->detail);
  if (minor_raw)
    minor = g_strconcat ("insert:", minor_raw, NULL);
992
993
994
995
996
997
998
999
1000
1001
1002
  else
    minor = g_strdup ("insert");

  if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
    detail1 = g_value_get_int (&param_values[1]);

  if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
    detail2 = g_value_get_int (&param_values[2]);

  if (G_VALUE_TYPE (&param_values[3]) == G_TYPE_STRING)
    text = g_value_get_string (&param_values[3]);
1003
1004
  else
    text = "";
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025

  emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
              DBUS_TYPE_STRING_AS_STRING, text, append_basic);
  g_free (minor);
  return TRUE;
}

/* 
 * Handles the ATK signal 'Gtk:AtkText:text-remove' and
 * converts it to the AT-SPI signal - 'object:text-changed'
 *
 */
static gboolean
text_remove_event_listener (GSignalInvocationHint * signal_hint,
                            guint n_param_values,
                            const GValue * param_values, gpointer data)
{
  AtkObject *accessible;
  guint text_changed_signal_id;
  GSignalQuery signal_query;
  const gchar *name;
Mike Gorse's avatar
Mike Gorse committed
1026
1027
  const gchar *minor_raw, *text;
  gchar *minor;
1028
  gint detail1 = 0, detail2 = 0;
1029
1030
1031
1032
1033
1034
1035
1036
1037

  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
  /* Get signal name for 'Gtk:AtkText:text-changed' so
   * we convert it to the AT-SPI signal - 'object:text-changed'
   */
  text_changed_signal_id = g_signal_lookup ("text-changed", G_OBJECT_TYPE (accessible));
  g_signal_query (text_changed_signal_id, &signal_query);
  name = signal_query.signal_name;

Mike Gorse's avatar
Mike Gorse committed
1038
  minor_raw = g_quark_to_string (signal_hint->detail);
1039
1040

  /* Add the delete and keep any detail coming from atk */
Mike Gorse's avatar
Mike Gorse committed
1041
1042
  if (minor_raw)
    minor = g_strconcat ("delete:", minor_raw, NULL);
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
  else
    minor = g_strdup ("delete");

  if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
    detail1 = g_value_get_int (&param_values[1]);

  if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
    detail2 = g_value_get_int (&param_values[2]);

  if (G_VALUE_TYPE (&param_values[3]) == G_TYPE_STRING)
    text = g_value_get_string (&param_values[3]);
1054
1055
  else
    text = "";
1056
1057
1058
1059
1060
1061
1062
1063

  emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
              DBUS_TYPE_STRING_AS_STRING, text, append_basic);
  g_free (minor);
  return TRUE;
}


1064
1065
1066
1067
1068
1069
1070
1071
/*---------------------------------------------------------------------------*/

/* 
 * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and
 * converts it to the AT-SPI signal - 'object:text-selection-changed'
 *
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
1072
1073
1074
1075
text_selection_changed_event_listener (GSignalInvocationHint * signal_hint,
                                       guint n_param_values,
                                       const GValue * param_values,
                                       gpointer data)
1076
1077
1078
1079
1080
1081
1082
1083
{
  AtkObject *accessible;
  GSignalQuery signal_query;
  const gchar *name, *minor;

  g_signal_query (signal_hint->signal_id, &signal_query);
  name = signal_query.signal_name;

Mark Doffman's avatar
Mark Doffman committed
1084
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
1085
1086
  minor = g_quark_to_string (signal_hint->detail);

1087
  emit_event (accessible, ITF_EVENT_OBJECT, name, minor, 0, 0,
1088
              DBUS_TYPE_STRING_AS_STRING, "", append_basic);
1089
1090
1091
1092
1093
  return TRUE;
}

/*---------------------------------------------------------------------------*/

1094
1095
1096
/*
 * Children changed signal converter and forwarder.
 *
1097
 * Klass (Interface) org.a11y.atspi.Event.Object
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
 * Major is the signal name.
 * Minor is 'add' or 'remove'
 * detail1 is the index.
 * detail2 is 0.
 * any_data is the child reference.
 */
static gboolean
children_changed_event_listener (GSignalInvocationHint * signal_hint,
                                 guint n_param_values,
                                 const GValue * param_values, gpointer data)
{
  GSignalQuery signal_query;
  const gchar *name, *minor;
1111
  gint detail1 = 0, detail2 = 0;
1112

1113
  AtkObject *accessible, *ao=NULL;
1114
  gpointer child;
1115
1116
  AtkStateSet *set;
  gboolean ret;
1117
1118
1119
1120

  g_signal_query (signal_hint->signal_id, &signal_query);
  name = signal_query.signal_name;

1121
1122
  /* If the accessible is on STATE_MANAGES_DESCENDANTS state,
     children-changed signal are not forwarded. */
1123
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
1124
1125
1126
1127
1128
1129
1130
  set = atk_object_ref_state_set (accessible);
  ret = atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS);
  g_object_unref (set);

  if (ret)
    return TRUE;

1131
1132
  minor = g_quark_to_string (signal_hint->detail);

1133
1134
  detail1 = g_value_get_uint (param_values + 1);
  child = g_value_get_pointer (param_values + 2);
1135

1136
  if (ATK_IS_OBJECT (child))
1137
    {
1138
      ao = ATK_OBJECT (child);
1139
      emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
1140
1141
1142
1143
1144
1145
1146
1147
                  "(so)", ao, append_object);
    }
  else if ((minor != NULL) && (strcmp (minor, "add") == 0))
    {
      ao = atk_object_ref_accessible_child (accessible, 
                                            detail1);
      emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
                  "(so)", ao, append_object);
1148
      g_object_unref (ao);
1149
1150
1151
1152
    }
  else
    {
      emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
1153
                  "(so)", ao, append_object);
1154
    }
1155
 
1156
1157
1158
1159
1160
  return TRUE;
}

/*---------------------------------------------------------------------------*/

1161
1162
1163
/*
 * Generic signal converter and forwarder.
 *
1164
 * Klass (Interface) org.a11y.atspi.Event.Object
1165
1166
1167
1168
1169
1170
1171
 * Major is the signal name.
 * Minor is NULL.
 * detail1 is 0.
 * detail2 is 0.
 * any_data is NULL.
 */
static gboolean
Mark Doffman's avatar
Mark Doffman committed
1172
1173
1174
generic_event_listener (GSignalInvocationHint * signal_hint,
                        guint n_param_values,
                        const GValue * param_values, gpointer data)
1175
1176
1177
1178
{
  AtkObject *accessible;
  GSignalQuery signal_query;
  const gchar *name;
Mike Gorse's avatar
Mike Gorse committed
1179
  int detail1 = 0, detail2 = 0;
1180
1181
1182
1183

  g_signal_query (signal_hint->signal_id, &signal_query);
  name = signal_query.signal_name;

Mark Doffman's avatar
Mark Doffman committed
1184
  accessible = ATK_OBJECT (g_value_get_object (&param_values[0]));
Mike Gorse's avatar
Mike Gorse committed
1185
1186

  if (n_param_values > 1 && G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
Mark Doffman's avatar
Mark Doffman committed
1187
    detail1 = g_value_get_int (&param_values[1]);
Mike Gorse's avatar
Mike Gorse committed
1188
1189

  if (n_param_values > 2 && G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
Mark Doffman's avatar
Mark Doffman committed
1190
    detail2 = g_value_get_int (&param_values[2]);
Mike Gorse's avatar
Mike Gorse committed
1191

1192
1193
  emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, detail2,
              DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
  return TRUE;
}

/*---------------------------------------------------------------------------*/

/*
 * Registers the provided function as a handler for the given signal name
 * and stores the signal id returned so that the function may be
 * de-registered later.
 */
1204
static guint
1205
1206
1207
1208
1209
add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
{
  guint id;

  id = atk_add_global_event_listener (listener, signal_name);
1210
1211
1212
1213
1214

  if (id > 0) /* id == 0 is a failure */
    g_array_append_val (listener_ids, id);

  return id;
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
}

/*
 * Initialization for the signal handlers.
 *
 * Registers all required signal handlers.
 */
void
spi_atk_register_event_listeners (void)
{
  /*
   * Kludge to make sure the Atk interface types are registered, otherwise
   * the AtkText signal handlers below won't get registered
   */