1

            
2
/*
3
 * AT-SPI - Assistive Technology Service Provider Interface
4
 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5
 *
6
 * Copyright 2001, 2002 Sun Microsystems Inc.,
7
 * Copyright 2001, 2002 Ximian, Inc.
8
 *
9
 * This library is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU Lesser General Public
11
 * License as published by the Free Software Foundation; either
12
 * version 2.1 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
 * Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public
20
 * License along with this library; if not, write to the
21
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22
 * Boston, MA 02110-1301, USA.
23
 */
24

            
25
/* atspi_registry.c: Global functions wrapping the registry */
26

            
27
#include "atspi-private.h"
28

            
29
/**
30
 * AtspiRegistry:
31
 *
32
 * A service through which applications providing accessibility services
33
 * can rendezvous with consumers of those services.
34
 *
35
 * A service through which applications providing accessibility services (servers)
36
 * can rendezvous with consumers of those services (Assistive Technologies). The
37
 * atspi-registry is the first "port of call" for accessible applications and for
38
 * assistive technologies wishing to query and interact with those applications.
39
 */
40

            
41
typedef struct
42
{
43
  AtspiDeviceListener *listener;
44
  GArray *key_set;
45
  AtspiKeyMaskType modmask;
46
  AtspiKeyEventMask event_types;
47
  gint sync_type;
48
} DeviceListenerEntry;
49

            
50
typedef struct
51
{
52
  AtspiGenerateMouseEventCB callback;
53
  void *callback_data;
54
} AtspiGenerateMouseEventClosure;
55

            
56
static GList *device_listeners;
57

            
58
/**
59
 * atspi_get_desktop_count:
60
 *
61
 * Gets the number of virtual desktops.
62
 * NOTE: multiple virtual desktops are not implemented yet; as a
63
 * consequence, this function always returns 1.
64
 *
65
 * Returns: a #gint indicating the number of active virtual desktops.
66
 **/
67
gint
68
atspi_get_desktop_count ()
69
{
70
  return 1;
71
}
72

            
73
/**
74
 * atspi_get_desktop:
75
 * @i: a #gint indicating which of the accessible desktops is to be returned.
76
 *
77
 * Gets the virtual desktop indicated by index @i.
78
 * NOTE: currently multiple virtual desktops are not implemented;
79
 * as a consequence, any @i value different from 0 will not return a
80
 * virtual desktop - instead it will return NULL.
81
 *
82
 * Returns: (transfer full): a pointer to the @i-th virtual desktop's
83
 * #AtspiAccessible representation.
84
 **/
85
AtspiAccessible *
86
3949
atspi_get_desktop (gint i)
87
{
88
3949
  if (i != 0)
89
    return NULL;
90
3949
  return _atspi_ref_accessible (atspi_bus_registry, atspi_path_root);
91
}
92

            
93
/**
94
 * atspi_get_desktop_list:
95
 *
96
 * Gets the list of virtual desktops.  On return, @list will point
97
 *     to a newly-created, NULL terminated array of virtual desktop
98
 *     pointers.
99
 *     It is the responsibility of the caller to free this array when
100
 *     it is no longer needed.
101
 * NOTE: currently multiple virtual desktops are not implemented;
102
 * this implementation always returns a #Garray with a single
103
 * #AtspiAccessible desktop.
104
 *
105
 * Returns: (element-type AtspiAccessible*) (transfer full): a #GArray of
106
 * desktops.
107
 **/
108
GArray *
109
atspi_get_desktop_list ()
110
{
111
  GArray *array = g_array_new (TRUE, TRUE, sizeof (AtspiAccessible *));
112
  AtspiAccessible *desktop;
113

            
114
  desktop = _atspi_ref_accessible (atspi_bus_registry, atspi_path_root);
115
  if (array)
116
    g_array_index (array, AtspiAccessible *, 0) = desktop;
117
  return array;
118
}
119

            
120
static gboolean
121
notify_keystroke_listener (DeviceListenerEntry *e)
122
{
123
  gchar *path = _atspi_device_listener_get_path (e->listener);
124
  dbus_uint32_t d_modmask = e->modmask;
125
  dbus_uint32_t d_event_types = e->event_types;
126
  AtspiEventListenerMode listener_mode;
127
  gboolean retval = FALSE;
128

            
129
  listener_mode.synchronous =
130
      (dbus_bool_t) ((e->sync_type & ATSPI_KEYLISTENER_SYNCHRONOUS) != 0);
131
  listener_mode.preemptive =
132
      (dbus_bool_t) ((e->sync_type & ATSPI_KEYLISTENER_CANCONSUME) != 0);
133
  listener_mode.global =
134
      (dbus_bool_t) ((e->sync_type & ATSPI_KEYLISTENER_ALL_WINDOWS) != 0);
135

            
136
  dbind_method_call (_atspi_bus (), atspi_bus_registry,
137
                     atspi_path_dec, atspi_interface_dec,
138
                     "RegisterKeystrokeListener",
139
                     "oa(iisi)uu(bbb)=>b", path, e->key_set,
140
                     d_modmask, d_event_types, &listener_mode,
141
                     &retval);
142

            
143
  g_free (path);
144

            
145
  return retval;
146
}
147

            
148
static void
149
device_listener_entry_free (DeviceListenerEntry *e)
150
{
151
  g_array_free (e->key_set, TRUE);
152
  g_free (e);
153
}
154

            
155
static void
156
unregister_listener (gpointer data, GObject *object)
157
{
158
  GList *l;
159
  AtspiDeviceListener *listener = ATSPI_DEVICE_LISTENER (object);
160

            
161
  for (l = device_listeners; l;)
162
    {
163
      DeviceListenerEntry *e = l->data;
164
      if (e->listener == listener)
165
        {
166
          GList *next = l->next;
167
          device_listener_entry_free (e);
168
          device_listeners = g_list_delete_link (device_listeners, l);
169
          l = next;
170
        }
171
      else
172
        l = l->next;
173
    }
174
}
175

            
176
/**
177
 * atspi_register_keystroke_listener:
178
 * @listener:  a pointer to the #AtspiDeviceListener for which
179
 *             keystroke events are requested.
180
 * @key_set: (element-type AtspiKeyDefinition) (allow-none): a pointer to the
181
 *        #AtspiKeyDefinition array indicating which keystroke events are
182
 *        requested, or NULL
183
 *        to indicate that all keycodes and keyvals for the specified
184
 *        modifier set are to be included.
185
 * @modmask:   an #AtspiKeyMaskType mask indicating which
186
 *             key event modifiers must be set in combination with @keys,
187
 *             events will only be reported for key events for which all
188
 *             modifiers in @modmask are set.  If you wish to listen for
189
 *             events with multiple modifier combinations, you must call
190
 *             #atspi_register_keystroke_listener once for each
191
 *             combination.
192
 * @event_types: an #AtspiKeyMaskType mask indicating which
193
 *             types of key events are requested (%ATSPI_KEY_PRESSED etc.).
194
 * @sync_type: an #AtspiKeyListenerSyncType parameter indicating
195
 *             the behavior of the notification/listener transaction.
196
 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
197
 *
198
 * Registers a listener for keystroke events, either pre-emptively for
199
 *             all windows (%ATSPI_KEYLISTENER_ALL_WINDOWS),
200
 *             non-preemptively (%ATSPI_KEYLISTENER_NOSYNC), or
201
 *             pre-emptively at the toolkit level (%ATSPI_KEYLISTENER_CANCONSUME).
202
 *             If ALL_WINDOWS or CANCONSUME are used, the event is consumed
203
 *             upon receipt if one of @listener's callbacks returns %TRUE
204
 *             (other sync_type values may be available in the future).
205
 *
206
 * Returns: %TRUE if successful, otherwise %FALSE.
207
 **/
208
gboolean
209
atspi_register_keystroke_listener (AtspiDeviceListener *listener,
210
                                   GArray *key_set,
211
                                   AtspiKeyMaskType modmask,
212
                                   AtspiKeyEventMask event_types,
213
                                   AtspiKeyListenerSyncType sync_type,
214
                                   GError **error)
215
{
216
  DeviceListenerEntry *e;
217

            
218
  g_return_val_if_fail (listener != NULL, FALSE);
219

            
220
  e = g_new0 (DeviceListenerEntry, 1);
221
  e->listener = listener;
222
  e->modmask = modmask;
223
  e->event_types = event_types;
224
  e->sync_type = sync_type;
225
  if (key_set)
226
    {
227
      gint i;
228
      e->key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition),
229
                                      key_set->len);
230
      e->key_set->len = key_set->len;
231
      for (i = 0; i < key_set->len; i++)
232
        {
233
          AtspiKeyDefinition *kd = ((AtspiKeyDefinition *) key_set->data) + i;
234
          AtspiKeyDefinition *d_kd = ((AtspiKeyDefinition *) e->key_set->data) + i;
235
          d_kd->keycode = kd->keycode;
236
          d_kd->keysym = kd->keysym;
237
          if (kd->keystring)
238
            {
239
              d_kd->keystring = kd->keystring;
240
            }
241
          else
242
            {
243
              d_kd->keystring = "";
244
            }
245
        }
246
    }
247
  else
248
    {
249
      e->key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition), 0);
250
    }
251

            
252
  g_object_weak_ref (G_OBJECT (listener), unregister_listener, NULL);
253
  device_listeners = g_list_prepend (device_listeners, e);
254
  return notify_keystroke_listener (e);
255
}
256

            
257
/**
258
 * atspi_deregister_keystroke_listener:
259
 * @listener: a pointer to the #AtspiDeviceListener for which
260
 *            keystroke events are requested.
261
 * @key_set: (element-type AtspiKeyDefinition) (allow-none): a pointer to the
262
 *        #AtspiKeyDefinition array indicating which keystroke events are
263
 *        requested, or %NULL
264
 *        to indicate that all keycodes and keyvals for the specified
265
 *        modifier set are to be included.
266
 * @modmask:  the key modifier mask for which this listener is to be
267
 *            'deregistered' (of type #AtspiKeyMaskType).
268
 * @event_types: an #AtspiKeyMaskType mask indicating which
269
 *             types of key events were requested (%ATSPI_KEY_PRESSED, etc.).
270
 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
271
 *
272
 * Removes a keystroke event listener from the registry's listener queue,
273
 *            ceasing notification of events with modifiers matching @modmask.
274
 *
275
 * Returns: %TRUE if successful, otherwise %FALSE.
276
 **/
277
gboolean
278
atspi_deregister_keystroke_listener (AtspiDeviceListener *listener,
279
                                     GArray *key_set,
280
                                     AtspiKeyMaskType modmask,
281
                                     AtspiKeyEventMask event_types,
282
                                     GError **error)
283
{
284
  GArray *d_key_set;
285
  gchar *path;
286
  gint i;
287
  dbus_uint32_t d_modmask = modmask;
288
  dbus_uint32_t d_event_types = event_types;
289
  GList *l;
290

            
291
  if (!listener)
292
    {
293
      return FALSE;
294
    }
295

            
296
  path = _atspi_device_listener_get_path (listener);
297

            
298
  /* copy the keyval filter values from the C api into the DBind KeySet */
299
  if (key_set)
300
    {
301
      d_key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition), key_set->len);
302
      d_key_set->len = key_set->len;
303
      for (i = 0; i < key_set->len; ++i)
304
        {
305
          AtspiKeyDefinition *kd = ((AtspiKeyDefinition *) key_set->data) + i;
306
          AtspiKeyDefinition *d_kd = ((AtspiKeyDefinition *) d_key_set->data) + i;
307
          d_kd->keycode = kd->keycode;
308
          d_kd->keysym = kd->keysym;
309
          if (kd->keystring)
310
            {
311
              d_kd->keystring = kd->keystring;
312
            }
313
          else
314
            {
315
              d_kd->keystring = "";
316
            }
317
        }
318
    }
319
  else
320
    {
321
      d_key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition), 0);
322
    }
323

            
324
  dbind_method_call (_atspi_bus (), atspi_bus_registry,
325
                     atspi_path_dec, atspi_interface_dec,
326
                     "DeregisterKeystrokeListener",
327
                     "oa(iisi)uu", path, d_key_set, d_modmask,
328
                     d_event_types);
329

            
330
  unregister_listener (listener, NULL);
331
  for (l = device_listeners; l;)
332
    {
333
      /* TODO: This code is all wrong / doesn't match what is in
334
       *       deviceeventcontroller.c. It would be nice to deprecate these methods
335
       *       in favor of methods that return an ID for the registration that can
336
       *       be passed to a deregister function, for instance. */
337
      DeviceListenerEntry *e = l->data;
338
      if (e->modmask == modmask && e->event_types == event_types)
339
        {
340
          GList *next = l->next;
341
          device_listener_entry_free (e);
342
          device_listeners = g_list_delete_link (device_listeners, l);
343
          l = next;
344
        }
345
      else
346
        l = l->next;
347
    }
348
  g_array_free (d_key_set, TRUE);
349
  g_free (path);
350
  return TRUE;
351
}
352

            
353
/**
354
 * atspi_register_device_event_listener:
355
 *
356
 * This function does nothing and should not be called.
357
 *
358
 * Returns: Always returns %FALSE.
359
 **/
360
gboolean
361
atspi_register_device_event_listener (AtspiDeviceListener *listener,
362
                                      AtspiDeviceEventMask event_types,
363
                                      void *filter,
364
                                      GError **error)
365
{
366
  /* See https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues/94 for why this code is removed */
367
  return FALSE;
368
}
369

            
370
/**
371
 * atspi_deregister_device_event_listener:
372
 * @listener: a pointer to the #AtspiDeviceListener for which
373
 *            device events are requested.
374
 * @filter: (allow-none): Unused parameter.
375
 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
376
 *
377
 * Removes a device event listener from the registry's listener queue,
378
 *            ceasing notification of events of the specified type.
379
 *
380
 * Returns: %TRUE if successful, otherwise %FALSE.
381
 **/
382
gboolean
383
atspi_deregister_device_event_listener (AtspiDeviceListener *listener,
384
                                        void *filter,
385
                                        GError **error)
386
{
387
  /* See https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues/94 for why this code is removed */
388
  return FALSE;
389
}
390

            
391
/**
392
 * atspi_generate_keyboard_event:
393
 * @keyval: a #gint indicating the keycode or keysym or modifier mask of the
394
 *           key event being synthesized.
395
 * @keystring: (allow-none): an (optional) UTF-8 string which, if
396
 *           @synth_type is %ATSPI_KEY_STRING, indicates a 'composed'
397
 *           keyboard input string being synthesized; this type of
398
 *           keyboard event synthesis does not emulate hardware
399
 *           keypresses but injects the string as though a composing
400
 *           input method (such as XIM) were used.
401
 * @synth_type: an #AtspiKeySynthType flag indicating whether @keyval
402
 *           is to be interpreted as a keysym rather than a keycode
403
 *           (%ATSPI_KEY_SYM) or a string (%ATSPI_KEY_STRING) or a modifier
404
 *           mask (%ATSPI_KEY_LOCKMODIFIERS and %ATSPI_KEY_UNLOCKMODIFIERS), or
405
 *           whether to synthesize %ATSPI_KEY_PRESS,
406
 *           %ATSPI_KEY_RELEASE, or both (%ATSPI_KEY_PRESSRELEASE).
407
 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
408
 *
409
 * Synthesizes a keyboard event (as if a hardware keyboard event occurred in the
410
 * current UI context).
411
 *
412
 * Returns: %TRUE if successful, otherwise %FALSE.
413
 **/
414
gboolean
415
atspi_generate_keyboard_event (glong keyval,
416
                               const gchar *keystring,
417
                               AtspiKeySynthType synth_type,
418
                               GError **error)
419
{
420
  dbus_uint32_t d_synth_type = synth_type;
421
  dbus_int32_t d_keyval = keyval;
422
  DBusError d_error;
423

            
424
  dbus_error_init (&d_error);
425
  if (!keystring)
426
    keystring = "";
427
  dbind_method_call_reentrant (_atspi_bus (), atspi_bus_registry, atspi_path_dec, atspi_interface_dec, "GenerateKeyboardEvent", &d_error, "isu", d_keyval, keystring, d_synth_type);
428
  if (dbus_error_is_set (&d_error))
429
    {
430
      g_warning ("GenerateKeyboardEvent failed: %s", d_error.message);
431
      dbus_error_free (&d_error);
432
    }
433

            
434
  return TRUE;
435
}
436

            
437
/**
438
 * atspi_generate_mouse_event:
439
 * @x: a #glong indicating the screen x coordinate of the mouse event.
440
 * @y: a #glong indicating the screen y coordinate of the mouse event.
441
 * @name: a string indicating which mouse event to be synthesized
442
 *        (e.g. "b1p", "b1c", "b2r", "rel", "abs").
443
 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
444
 *
445
 * Synthesizes a mouse event at a specific screen coordinate.
446
 * Most AT clients should use the #AccessibleAction interface when
447
 * tempted to generate mouse events, rather than this method.
448
 * Event names: b1p = button 1 press; b2r = button 2 release;
449
 *              b3c = button 3 click; b2d = button 2 double-click;
450
 *              abs = absolute motion; rel = relative motion.
451
 *
452
 * Returns: %TRUE if successful, otherwise %FALSE.
453
 **/
454
gboolean
455
atspi_generate_mouse_event (glong x, glong y, const gchar *name, GError **error)
456
{
457
  dbus_int32_t d_x = x, d_y = y;
458
  DBusError d_error;
459

            
460
  g_return_val_if_fail (name != NULL, FALSE);
461

            
462
  dbus_error_init (&d_error);
463
  dbind_method_call_reentrant (_atspi_bus (), atspi_bus_registry,
464
                               atspi_path_dec, atspi_interface_dec,
465
                               "GenerateMouseEvent", &d_error, "iis",
466
                               d_x, d_y, name);
467
  if (dbus_error_is_set (&d_error))
468
    {
469
      g_warning ("GenerateMouseEvent failed: %s", d_error.message);
470
      dbus_error_free (&d_error);
471
    }
472

            
473
  return TRUE;
474
}
475

            
476
static void
477
atspi_generate_mouse_event_cb (DBusPendingCall *pending, void *user_data)
478
{
479
  AtspiGenerateMouseEventClosure *closure = user_data;
480

            
481
  closure->callback (closure->callback_data);
482
  dbus_pending_call_unref (pending);
483
}
484

            
485
/**
486
 * atspi_generate_mouse_event_async:
487
 * @x: a #glong indicating the screen x coordinate of the mouse event.
488
 * @y: a #glong indicating the screen y coordinate of the mouse event.
489
 * @name: a string indicating which mouse event to be synthesized
490
 *        (e.g. "b1p", "b1c", "b2r", "rel", "abs").
491
 * @callback: (scope notified) (allow-none): a callback to be called when a
492
 * reply is received. May be NULL.
493
 * @callback_data: (closure) (allow-none): data to be passed to @callback.
494
 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
495
 *
496
 * Like atspi_generate_mouse_event, but asynchronous.
497
 **/
498
void
499
atspi_generate_mouse_event_async (glong x, glong y, const gchar *name, AtspiGenerateMouseEventCB callback, void *callback_data, GError **error)
500
{
501
  dbus_int32_t d_x = x, d_y = y;
502
  DBusMessage *message;
503
  DBusPendingCall *pending = NULL;
504
  AtspiGenerateMouseEventClosure *closure = NULL;
505

            
506
  g_return_if_fail (name != NULL);
507

            
508
  message = dbus_message_new_method_call (atspi_bus_registry,
509
                                          atspi_path_dec, atspi_interface_dec,
510
                                          "GenerateMouseEvent");
511
  dbus_message_append_args (message, DBUS_TYPE_INT32, &d_x, DBUS_TYPE_INT32, &d_y, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
512

            
513
  if (!callback)
514
    {
515
      dbus_connection_send (_atspi_bus (), message, NULL);
516
      dbus_message_unref (message);
517
      return;
518
    }
519

            
520
  dbus_connection_send_with_reply (_atspi_bus (), message, &pending, -1);
521
  dbus_message_unref (message);
522
  if (pending)
523
    {
524
      closure = g_new0 (AtspiGenerateMouseEventClosure, 1);
525
      closure->callback = callback;
526
      closure->callback_data = callback_data;
527
      dbus_pending_call_set_notify (pending, atspi_generate_mouse_event_cb, closure, g_free);
528
    }
529
}
530

            
531
/**
532
 * atspi_set_reference_window:
533
 * @accessible: the #AtspiAccessible corresponding to the window to select.
534
 *              should be a top-level window with a role of
535
 *              ATSPI_ROLE_APPLICATION.
536
 *
537
 * Deprecated. This function no longer does anything and should not be used.
538
 */
539
void
540
atspi_set_reference_window (AtspiAccessible *accessible)
541
{
542
}
543

            
544
AtspiKeyDefinition *
545
atspi_key_definition_copy (AtspiKeyDefinition *src)
546
{
547
  AtspiKeyDefinition *dst;
548

            
549
  dst = g_new0 (AtspiKeyDefinition, 1);
550
  dst->keycode = src->keycode;
551
  dst->keysym = src->keysym;
552
  if (src->keystring)
553
    dst->keystring = g_strdup (src->keystring);
554
  dst->modifiers = src->modifiers;
555
  return dst;
556
}
557

            
558
void
559
atspi_key_definition_free (AtspiKeyDefinition *kd)
560
{
561
  if (kd->keystring)
562
    g_free (kd->keystring);
563
  g_free (kd);
564
}
565

            
566
void
567
_atspi_reregister_device_listeners ()
568
{
569
  GList *l;
570
  DeviceListenerEntry *e;
571

            
572
  for (l = device_listeners; l; l = l->next)
573
    {
574
      e = l->data;
575
      notify_keystroke_listener (e);
576
    }
577
}
578
G_DEFINE_BOXED_TYPE (AtspiKeyDefinition, atspi_key_definition, atspi_key_definition_copy, atspi_key_definition_free)