1
/*
2
 * AT-SPI - Assistive Technology Service Provider Interface
3
 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4
 *
5
 * Copyright 2020 SUSE LLC.
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, write to the
19
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22

            
23
#include "atspi-device.h"
24
#include "atspi-device-a11y-manager.h"
25
#include "atspi-device-legacy.h"
26
#include "atspi-device-x11.h"
27
#include "atspi-private.h"
28

            
29
enum {
30
  PROP_0,
31
  PROP_APP_ID,
32
  NUM_PROPERTIES
33
};
34

            
35
static GParamSpec *obj_props[NUM_PROPERTIES];
36

            
37
typedef struct
38
{
39
  guint id;
40
  guint keycode;
41
  guint keysym;
42
  guint modifiers;
43
  AtspiKeyCallback callback;
44
  void *callback_data;
45
  GDestroyNotify callback_destroyed;
46
} AtspiKeyGrab;
47

            
48
typedef struct _AtspiDevicePrivate AtspiDevicePrivate;
49
struct _AtspiDevicePrivate
50
{
51
  GSList *key_watchers;
52
  GSList *keygrabs;
53
  guint last_grab_id;
54
  gchar *app_id;
55
};
56

            
57
GObjectClass *device_parent_class;
58

            
59
static void
60
atspi_device_init (AtspiDevice *device)
61
{
62
}
63

            
64
G_DEFINE_TYPE_WITH_CODE (AtspiDevice, atspi_device, G_TYPE_OBJECT, G_ADD_PRIVATE (AtspiDevice))
65

            
66
static void
67
atspi_device_finalize (GObject *object)
68
{
69
  AtspiDevice *device = (AtspiDevice *) object;
70
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
71

            
72
  g_slist_free_full (priv->keygrabs, g_free);
73
  priv->keygrabs = NULL;
74

            
75
  device_parent_class->finalize (object);
76
}
77

            
78
static gboolean
79
atspi_device_real_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd)
80
{
81
  return TRUE;
82
}
83

            
84
static void
85
atspi_device_real_remove_key_grab (AtspiDevice *device, guint id)
86
{
87
}
88

            
89
#if !GLIB_CHECK_VERSION(2, 76, 0)
90
static inline gboolean
91
g_set_str (char **str_pointer,
92
           const char *new_str)
93
{
94
  char *copy;
95

            
96
  if (*str_pointer == new_str ||
97
      (*str_pointer && new_str && strcmp (*str_pointer, new_str) == 0))
98
    return FALSE;
99

            
100
  copy = g_strdup (new_str);
101
  g_free (*str_pointer);
102
  *str_pointer = copy;
103

            
104
  return TRUE;
105
}
106
#endif
107

            
108
static void
109
atspi_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
110
{
111
  AtspiDevice *device = ATSPI_DEVICE (object);
112

            
113
  switch (prop_id)
114
    {
115
    case PROP_APP_ID:
116
      g_value_set_string (value, atspi_device_get_app_id (device));
117
      break;
118
    default:
119
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
120
      break;
121
    }
122
}
123

            
124
static void
125
atspi_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
126
{
127
  AtspiDevice *device = ATSPI_DEVICE (object);
128

            
129
  switch (prop_id)
130
    {
131
    case PROP_APP_ID:
132
      atspi_device_set_app_id (device, g_value_get_string (value));
133
      break;
134
    default:
135
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
136
      break;
137
    }
138
}
139

            
140
static void
141
atspi_device_class_init (AtspiDeviceClass *klass)
142
{
143
  GObjectClass *object_class = (GObjectClass *) klass;
144

            
145
  device_parent_class = g_type_class_peek_parent (klass);
146
  klass->add_key_grab = atspi_device_real_add_key_grab;
147
  klass->remove_key_grab = atspi_device_real_remove_key_grab;
148
  object_class->finalize = atspi_device_finalize;
149
  object_class->get_property = atspi_device_get_property;
150
  object_class->set_property = atspi_device_set_property;
151

            
152
  /**
153
   * AtspiDevice:app-id:
154
   *
155
   * The application ID of the application that created this device.
156
   * The ID might be used for access control purposes
157
   * by some device backends.
158
   */
159
  obj_props[PROP_APP_ID] =
160
    g_param_spec_string ("app-id",
161
                         "Application ID",
162
                         "The application ID of the application that created this device",
163
                         NULL,
164
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
165

            
166
  g_object_class_install_properties (object_class, NUM_PROPERTIES, obj_props);
167
                        }
168

            
169
/**
170
 * atspi_device_new_full:
171
 * @app_id: (nullable): The application ID of the application that created this device.
172
 *
173
 * Creates a new #AtspiDevice with a specified app id.
174
 *
175
 * Returns: (transfer full): a pointer to a newly-created #AtspiDevice.
176
 *
177
 * Since: 2.55
178
 */
179
AtspiDevice *
180
atspi_device_new_full (const gchar *app_id)
181
{
182
#ifdef HAVE_X11
183
  if (!g_getenv ("WAYLAND_DISPLAY") && !g_getenv ("ATSPI_USE_LEGACY_DEVICE") && !g_getenv ("ATSPI_USE_A11Y_MANAGER_DEVICE"))
184
    return ATSPI_DEVICE (atspi_device_x11_new_full (app_id));
185
#endif
186

            
187
  AtspiDeviceA11yManager *a11y_manager_device = NULL;
188
  if (!g_getenv ("ATSPI_USE_LEGACY_DEVICE"))
189
    a11y_manager_device = atspi_device_a11y_manager_try_new_full (app_id);
190

            
191
  if (!a11y_manager_device)
192
    {
193
      if (g_getenv ("ATSPI_USE_A11Y_MANAGER_DEVICE"))
194
        g_critical ("ATSPI_USE_A11Y_MANAGER_DEVICE is set, but no a11y manager device could be created. Falling back to legacy device.");
195
      return ATSPI_DEVICE (atspi_device_legacy_new_full (app_id));
196
    }
197
  else
198
    {
199
      return ATSPI_DEVICE (a11y_manager_device);
200
    }
201
}
202

            
203
/**
204
 * atspi_device_new:
205
 *
206
 * Creates a new #AtspiDevice.
207
 *
208
 * Returns: (transfer full): a pointer to a newly-created #AtspiDevice.
209
 */
210
AtspiDevice *
211
atspi_device_new ()
212
{
213
  return atspi_device_new_full (NULL);
214
}
215

            
216
static gboolean
217
key_matches_modifiers (gint keycode, guint key_mods, guint grab_mods)
218
{
219
  /* The presence or lack thereof of locking modifiers should make no
220
     difference when testing, but other modifiers should match. If the
221
     keypress has modifiers that (a) the grab does not check for and (b) are
222
     not lock modifiers, then we reject the match. Alt + left arrow should not
223
     match a grab on left arrow, for instance, but whether numlock is on or
224
     off would be irrelevant. */
225
  if (_atspi_key_is_on_keypad (keycode))
226
    key_mods &= ~((1 << ATSPI_MODIFIER_SHIFTLOCK));
227
  else
228
    key_mods &= ~((1 << ATSPI_MODIFIER_SHIFTLOCK) | (1 << ATSPI_MODIFIER_NUMLOCK));
229
  return (key_mods == grab_mods);
230
}
231

            
232
gboolean
233
atspi_device_notify_key (AtspiDevice *device, gboolean pressed, int keycode, int keysym, gint state, const gchar *text)
234
{
235
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
236
  GSList *l;
237
  gboolean ret = FALSE;
238

            
239
  for (l = priv->key_watchers; l; l = l->next)
240
    {
241
      AtspiKeyGrab *grab = l->data;
242
      grab->callback (device, pressed, keycode, keysym, state, text, grab->callback_data);
243
    }
244

            
245
  for (l = priv->keygrabs; l; l = l->next)
246
    {
247
      AtspiKeyGrab *grab = l->data;
248
      if (keycode == grab->keycode && key_matches_modifiers (keycode, state, grab->modifiers))
249
        {
250
          if (grab->callback)
251
            grab->callback (device, pressed, keycode, keysym, state, text, grab->callback_data);
252
          ret = TRUE;
253
        }
254
    }
255

            
256
  return ret;
257
}
258

            
259
static gboolean
260
is_id_used (AtspiDevice *device, guint id)
261
{
262
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
263
  GSList *l;
264

            
265
  for (l = priv->key_watchers; l; l = l->next)
266
    {
267
      AtspiKeyGrab *grab = l->data;
268
      if (grab->id == id)
269
        return TRUE;
270
    }
271

            
272
  for (l = priv->keygrabs; l; l = l->next)
273
    {
274
      AtspiKeyGrab *grab = l->data;
275
      if (grab->id == id)
276
        return TRUE;
277
    }
278

            
279
  return FALSE;
280
}
281

            
282
static guint
283
get_grab_id (AtspiDevice *device)
284
{
285
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
286

            
287
  while (is_id_used (device, priv->last_grab_id))
288
    priv->last_grab_id++;
289
  return priv->last_grab_id++;
290
}
291

            
292
/**
293
 *atspi_device_add_key_grab:
294
 * @device: the device.
295
 * @kd: a #AtspiKeyDefinition specifying the key code to grab.
296
 * @callback: (scope notified) (allow-none): the function to call when the
297
 *            given key is pressed.
298
 * @user_data: Data to be passed to @callback.
299
 * @callback_destroyed: callback function to be called when @callback is
300
 *                      destroyed.
301
 *
302
 * Returns: an identifier that can be later used to remove the grab, or 0
303
 * if the key/modifier combination could not be grabbed.
304
 * Add a key grab for the given key/modifier combination.
305
 */
306
guint
307
atspi_device_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed)
308
{
309
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
310
  AtspiKeyGrab *grab;
311

            
312
  if (!ATSPI_DEVICE_GET_CLASS (device)->add_key_grab (device, kd))
313
    return 0;
314

            
315
  grab = g_new (AtspiKeyGrab, 1);
316
  grab->keycode = kd->keycode;
317
  grab->keysym = kd->keysym;
318
  grab->modifiers = kd->modifiers;
319
  grab->callback = callback;
320
  grab->callback_data = user_data;
321
  grab->callback_destroyed = callback_destroyed;
322
  grab->id = get_grab_id (device);
323
  priv->keygrabs = g_slist_append (priv->keygrabs, grab);
324

            
325
  return grab->id;
326
}
327

            
328
/**
329
 * atspi_device_remove_key_grab:
330
 * @device: the device.
331
 * @id: the identifier of the grab to be removed.
332
 *
333
 * Removes the key grab specified by @id.
334
 */
335
void
336
atspi_device_remove_key_grab (AtspiDevice *device, guint id)
337
{
338
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
339
  GSList *l;
340

            
341
  for (l = priv->keygrabs; l; l = l->next)
342
    {
343
      AtspiKeyGrab *grab = l->data;
344
      if (grab->id == id)
345
        {
346
          ATSPI_DEVICE_GET_CLASS (device)->remove_key_grab (device, id);
347
          priv->keygrabs = g_slist_remove (priv->keygrabs, grab);
348
          if (grab->callback_destroyed)
349
            (*grab->callback_destroyed) (grab->callback);
350
          g_free (grab);
351
          return;
352
        }
353
    }
354
}
355

            
356
/**
357
 *atspi_device_add_key_watcher:
358
 * @device: the device.
359
 * @callback: (scope notified) (closure user_data) (destroy callback_destroyed): the
360
 *   function to call when the given key is pressed.
361
 * @user_data: Data to be passed to @callback.
362
 * @callback_destroyed: callback function to be called when @callback is destroyed.
363
 *
364
 * Add a callback that will receive a notification whenever a key is
365
 * pressed or released.
366
 */
367
void
368
atspi_device_add_key_watcher (AtspiDevice *device, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed)
369
{
370
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
371
  AtspiKeyGrab *grab = g_new0 (AtspiKeyGrab, 1);
372
  grab->id = get_grab_id (device);
373
  grab->callback = callback;
374
  grab->callback_data = user_data;
375
  grab->callback_destroyed = callback_destroyed;
376
  priv->key_watchers = g_slist_append (priv->key_watchers, grab);
377
}
378

            
379
AtspiKeyDefinition *
380
atspi_device_get_grab_by_id (AtspiDevice *device, guint id)
381
{
382
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
383
  GSList *l;
384

            
385
  for (l = priv->keygrabs; l; l = l->next)
386
    {
387
      AtspiKeyGrab *grab = l->data;
388
      if (grab->id == id)
389
        {
390
          AtspiKeyDefinition *kd = g_new0 (AtspiKeyDefinition, 1);
391
          kd->keycode = grab->keycode;
392
          kd->keysym = grab->keysym;
393
          kd->modifiers = grab->modifiers;
394
          return kd;
395
        }
396
    }
397
  return NULL;
398
}
399

            
400
/**
401
 * atspi_device_map_modifier:
402
 * @device: the device.
403
 * @keycode: the keycode to map.
404
 *
405
 * Maps the specified key code to a modifier so that it can be used in
406
 * conjunction with other keys to create a key grab. If the given keycode is
407
 * already mapped, then this function will return the modifier that is
408
 * currently mapped to the keycode, without doing anything else. Otherwise,
409
 * it will use the last modifier that AT-SPI used to map a key. If no keys
410
 * have yet been mapped using this device, then it will look for a modifier
411
 * that is not currently being used. If no unused modifier can be found,
412
 * then it will use the first modifier by default.
413
 *
414
 * Returns: the modifier that is now mapped to this keycode. This return
415
 * value can be passed to atspi_device_add_key_grab.
416
 */
417
guint
418
atspi_device_map_modifier (AtspiDevice *device, gint keycode)
419
{
420
  if (ATSPI_DEVICE_GET_CLASS (device)->map_modifier)
421
    return ATSPI_DEVICE_GET_CLASS (device)->map_modifier (device, keycode);
422

            
423
  return 0;
424
}
425

            
426
/**
427
 * atspi_device_unmap_modifier:
428
 * @device: the device.
429
 * @keycode: the keycode to unmap.
430
 *
431
 * Removes a mapped modifier from the given keycode.
432
 */
433
void
434
atspi_device_unmap_modifier (AtspiDevice *device, gint keycode)
435
{
436
  if (ATSPI_DEVICE_GET_CLASS (device)->unmap_modifier)
437
    ATSPI_DEVICE_GET_CLASS (device)->unmap_modifier (device, keycode);
438
}
439

            
440
/**
441
 * atspi_device_get_modifier:
442
 * @device: the device.
443
 * @keycode: the keycode to map.
444
 *
445
 * Gets the modifier for a given keycode, if one exists. Does not create a new
446
 * mapping. This function should be used when the intention is to query a
447
 * locking modifier such as num lock via atspi_device_get_locked_modifiers,
448
 * rather than to add key grabs.
449
 *
450
 * Returns: the modifier that is mapped to this keycode.
451
 */
452
guint
453
atspi_device_get_modifier (AtspiDevice *device, gint keycode)
454
{
455
  if (ATSPI_DEVICE_GET_CLASS (device)->get_modifier)
456
    return ATSPI_DEVICE_GET_CLASS (device)->get_modifier (device, keycode);
457

            
458
  return 0;
459
}
460

            
461
/**
462
 * atspi_device_get_locked_modifiers:
463
 * @device: the device.
464
 *
465
 * Returns the locked modifiers (ie, num lock, caps lock) associated with this
466
 * keyboard.
467
 *
468
 * Returns: a guint of modifier flags.
469
 */
470
guint
471
atspi_device_get_locked_modifiers (AtspiDevice *device)
472
{
473
  if (ATSPI_DEVICE_GET_CLASS (device)->get_locked_modifiers)
474
    return ATSPI_DEVICE_GET_CLASS (device)->get_locked_modifiers (device);
475

            
476
  return 0;
477
}
478

            
479
/**
480
 * atspi_device_grab_keyboard:
481
 *
482
 * Attempts to grab the entire keyboard. This should only be done
483
 * temporarily, as it may conflict with other applications that also want to
484
 * grab the keyboard.
485
 *
486
 * Returns: #TRUE if successful, #FALSE otherwise.
487
 */
488
gboolean
489
atspi_device_grab_keyboard (AtspiDevice *device)
490
{
491
  if (ATSPI_DEVICE_GET_CLASS (device)->grab_keyboard)
492
    return ATSPI_DEVICE_GET_CLASS (device)->grab_keyboard (device);
493

            
494
  return FALSE;
495
}
496

            
497
/**
498
 * atspi_device_ungrab_keyboard:
499
 *
500
 * Removes a keyboard grab added via a call to atspi_device_add_keyboard.
501
 */
502
void
503
atspi_device_ungrab_keyboard (AtspiDevice *device)
504
{
505
  if (ATSPI_DEVICE_GET_CLASS (device)->ungrab_keyboard)
506
    ATSPI_DEVICE_GET_CLASS (device)->ungrab_keyboard (device);
507
}
508

            
509
/**
510
 * atspi_device_generate_mouse_event:
511
 * @device: the device.
512
 * @obj: The #AtspiAccessible that should receive the click.
513
 * @x: a #gint indicating the x coordinate of the mouse event, relative to
514
 *     @obj..
515
 * @y: a #gint indicating the y coordinate of the mouse event, relative to
516
 *     @obj..
517
 * @name: a string indicating which mouse event to be synthesized
518
 *        (e.g. "b1p", "b1c", "b2r", "rel", "abs").
519
 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
520
 *
521
 * Synthesizes a mouse event at a specific screen coordinate.
522
 * Most AT clients should use the #AccessibleAction interface when
523
 * tempted to generate mouse events, rather than this method.
524
 * Event names: b1p = button 1 press; b2r = button 2 release;
525
 *              b3c = button 3 click; b2d = button 2 double-click;
526
 *              abs = absolute motion; rel = relative motion.
527
 *
528
 * Since: 2.52
529
 **/
530
void
531
atspi_device_generate_mouse_event (AtspiDevice *device, AtspiAccessible *obj, gint x, gint y, const gchar *name, GError **error)
532
{
533
  if (ATSPI_DEVICE_GET_CLASS (device)->generate_mouse_event)
534
    ATSPI_DEVICE_GET_CLASS (device)->generate_mouse_event (device, obj, x, y, name, error);
535
}
536

            
537
/**
538
 * atspi_device_map_keysym_modifier:
539
 * @device: the device.
540
 * @keysym: the XKB keysym to map.
541
 *
542
 * Maps the specified keysym to a modifier so that it can be used in
543
 * conjunction with other keys to create a key grab. If the given keysym is
544
 * already mapped, then this function will return the modifier that is
545
 * currently mapped to the keysym, without doing anything else. Otherwise,
546
 * it will use the last modifier that AT-SPI used to map a keysym. If no keys
547
 * have yet been mapped using this device, then it will look for a modifier
548
 * that is not currently being used. If no unused modifier can be found,
549
 * then it will use the first modifier by default.
550
 *
551
 * Returns: the modifier that is now mapped to this keysym. This return
552
 * value can be passed to atspi_device_add_key_grab.
553
 *
554
 * Since: 2.55
555
 */
556
guint
557
atspi_device_map_keysym_modifier (AtspiDevice *device, guint keysym)
558
{
559
  if (ATSPI_DEVICE_GET_CLASS (device)->map_keysym_modifier)
560
    return ATSPI_DEVICE_GET_CLASS (device)->map_keysym_modifier (device, keysym);
561

            
562
  return 0;
563
}
564

            
565
/**
566
 * atspi_device_unmap_keysym_modifier:
567
 * @device: the device.
568
 * @keysym: the XKB keysym to unmap.
569
 *
570
 * Removes a mapped modifier from the given keysym.
571
 *
572
 * Since: 2.55
573
 */
574
void
575
atspi_device_unmap_keysym_modifier (AtspiDevice *device, guint keysym)
576
{
577
  if (ATSPI_DEVICE_GET_CLASS (device)->unmap_keysym_modifier)
578
    ATSPI_DEVICE_GET_CLASS (device)->unmap_keysym_modifier (device, keysym);
579
}
580

            
581
/**
582
 * atspi_device_get_keysym_modifier:
583
 * @device: the device.
584
 * @keysym: the XKB keysym to map.
585
 *
586
 * Gets the modifier for a given keysym, if one exists. Does not create a new
587
 * mapping. This function should be used when the intention is to query a
588
 * locking modifier such as num lock via atspi_device_get_locked_modifiers,
589
 * rather than to add key grabs.
590
 *
591
 * Returns: the modifier that is mapped to this keysym.
592
 *
593
 * Since: 2.55
594
 */
595
guint
596
atspi_device_get_keysym_modifier (AtspiDevice *device, guint keysym)
597
{
598
  if (ATSPI_DEVICE_GET_CLASS (device)->get_keysym_modifier)
599
    return ATSPI_DEVICE_GET_CLASS (device)->get_keysym_modifier (device, keysym);
600

            
601
  return 0;
602
}
603

            
604
/**
605
 * atspi_device_get_app_id:
606
 * @device: the device.
607
 * 
608
 * Returns the application ID of the device.
609
 * 
610
 * Since: 2.55
611
 */
612
const gchar *
613
atspi_device_get_app_id (AtspiDevice *device)
614
{
615
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
616
  return priv->app_id;
617
}
618

            
619
/**
620
 * atspi_device_set_app_id:
621
 * @device: the device.
622
 * @app_id: the application ID.
623
 * 
624
 * Sets the application ID of the device.
625
 * 
626
 * Since: 2.55
627
 */
628
void
629
atspi_device_set_app_id (AtspiDevice *device, const gchar *app_id)
630
{
631
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
632
  if (g_set_str (&priv->app_id, app_id))
633
    g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_APP_ID]);
634
}