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-legacy.h"
25
#include "atspi-device-x11.h"
26
#include "atspi-private.h"
27

            
28
typedef struct
29
{
30
  guint id;
31
  guint keycode;
32
  guint keysym;
33
  guint modifiers;
34
  AtspiKeyCallback callback;
35
  void *callback_data;
36
  GDestroyNotify callback_destroyed;
37
} AtspiKeyGrab;
38

            
39
typedef struct _AtspiDevicePrivate AtspiDevicePrivate;
40
struct _AtspiDevicePrivate
41
{
42
  GSList *key_watchers;
43
  GSList *keygrabs;
44
  guint last_grab_id;
45
};
46

            
47
GObjectClass *device_parent_class;
48

            
49
static void
50
atspi_device_init (AtspiDevice *device)
51
{
52
}
53

            
54
G_DEFINE_TYPE_WITH_CODE (AtspiDevice, atspi_device, G_TYPE_OBJECT, G_ADD_PRIVATE (AtspiDevice))
55

            
56
static void
57
atspi_device_finalize (GObject *object)
58
{
59
  AtspiDevice *device = (AtspiDevice *) object;
60
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
61

            
62
  g_slist_free_full (priv->keygrabs, g_free);
63
  priv->keygrabs = NULL;
64

            
65
  device_parent_class->finalize (object);
66
}
67

            
68
static gboolean
69
atspi_device_real_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd)
70
{
71
  return TRUE;
72
}
73

            
74
static void
75
atspi_device_real_remove_key_grab (AtspiDevice *device, guint id)
76
{
77
}
78

            
79
static void
80
atspi_device_class_init (AtspiDeviceClass *klass)
81
{
82
  GObjectClass *object_class = (GObjectClass *) klass;
83

            
84
  device_parent_class = g_type_class_peek_parent (klass);
85
  klass->add_key_grab = atspi_device_real_add_key_grab;
86
  klass->remove_key_grab = atspi_device_real_remove_key_grab;
87
  object_class->finalize = atspi_device_finalize;
88
}
89

            
90
/**
91
 * atspi_device_new:
92
 *
93
 * Creates a new #AtspiDevice with a specified callback function.
94
 *
95
 * Returns: (transfer full): a pointer to a newly-created #AtspiDevice.
96
 *
97
 **/
98
AtspiDevice *
99
atspi_device_new ()
100
{
101
#ifdef HAVE_X11
102
  if (!g_getenv ("WAYLAND_DISPLAY") && !g_getenv ("ATSPI_USE_LEGACY_DEVICE"))
103
    return ATSPI_DEVICE (atspi_device_x11_new ());
104
#endif
105

            
106
  return ATSPI_DEVICE (atspi_device_legacy_new ());
107
}
108

            
109
static gboolean
110
key_matches_modifiers (gint keycode, guint key_mods, guint grab_mods)
111
{
112
  /* The presence or lack thereof of locking modifiers should make no
113
     difference when testing, but other modifiers should match. If the
114
     keypress has modifiers that (a) the grab does not check for and (b) are
115
     not lock modifiers, then we reject the match. Alt + left arrow should not
116
     match a grab on left arrow, for instance, but whether numlock is on or
117
     off would be irrelevant. */
118
  if (_atspi_key_is_on_keypad (keycode))
119
    key_mods &= ~((1 << ATSPI_MODIFIER_SHIFTLOCK));
120
  else
121
    key_mods &= ~((1 << ATSPI_MODIFIER_SHIFTLOCK) | (1 << ATSPI_MODIFIER_NUMLOCK));
122
  return (key_mods == grab_mods);
123
}
124

            
125
gboolean
126
atspi_device_notify_key (AtspiDevice *device, gboolean pressed, int keycode, int keysym, gint state, const gchar *text)
127
{
128
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
129
  GSList *l;
130
  gboolean ret = FALSE;
131

            
132
  for (l = priv->key_watchers; l; l = l->next)
133
    {
134
      AtspiKeyGrab *grab = l->data;
135
      grab->callback (device, pressed, keycode, keysym, state, text, grab->callback_data);
136
    }
137

            
138
  for (l = priv->keygrabs; l; l = l->next)
139
    {
140
      AtspiKeyGrab *grab = l->data;
141
      if (keycode == grab->keycode && key_matches_modifiers (keycode, state, grab->modifiers))
142
        {
143
          if (grab->callback)
144
            grab->callback (device, pressed, keycode, keysym, state, text, grab->callback_data);
145
          ret = TRUE;
146
        }
147
    }
148

            
149
  return ret;
150
}
151

            
152
static gboolean
153
is_id_used (AtspiDevice *device, guint id)
154
{
155
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
156
  GSList *l;
157

            
158
  for (l = priv->key_watchers; l; l = l->next)
159
    {
160
      AtspiKeyGrab *grab = l->data;
161
      if (grab->id == id)
162
        return TRUE;
163
    }
164

            
165
  for (l = priv->keygrabs; l; l = l->next)
166
    {
167
      AtspiKeyGrab *grab = l->data;
168
      if (grab->id == id)
169
        return TRUE;
170
    }
171

            
172
  return FALSE;
173
}
174

            
175
static guint
176
get_grab_id (AtspiDevice *device)
177
{
178
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
179

            
180
  while (is_id_used (device, priv->last_grab_id))
181
    priv->last_grab_id++;
182
  return priv->last_grab_id++;
183
}
184

            
185
/**
186
 *atspi_device_add_key_grab:
187
 * @device: the device.
188
 * @kd: a #AtspiKeyDefinition specifying the key code to grab.
189
 * @callback: (scope notified) (allow-none): the function to call when the
190
 *            given key is pressed.
191
 * @user_data: Data to be passed to @callback.
192
 * @callback_destroyed: callback function to be called when @callback is
193
 *                      destroyed.
194
 *
195
 * Returns: an identifier that can be later used to remove the grab, or 0
196
 * if the key/modifier combination could not be grabbed.
197
 * Add a key grab for the given key/modifier combination.
198
 */
199
guint
200
atspi_device_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed)
201
{
202
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
203
  AtspiKeyGrab *grab;
204

            
205
  if (!ATSPI_DEVICE_GET_CLASS (device)->add_key_grab (device, kd))
206
    return 0;
207

            
208
  grab = g_new (AtspiKeyGrab, 1);
209
  grab->keycode = kd->keycode;
210
  grab->keysym = kd->keysym;
211
  grab->modifiers = kd->modifiers;
212
  grab->callback = callback;
213
  grab->callback_data = user_data;
214
  grab->callback_destroyed = callback_destroyed;
215
  grab->id = get_grab_id (device);
216
  priv->keygrabs = g_slist_append (priv->keygrabs, grab);
217

            
218
  return grab->id;
219
}
220

            
221
/**
222
 * atspi_device_remove_key_grab:
223
 * @device: the device.
224
 * @id: the identifier of the grab to be removed.
225
 *
226
 * Removes the key grab specified by @id.
227
 */
228
void
229
atspi_device_remove_key_grab (AtspiDevice *device, guint id)
230
{
231
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
232
  GSList *l;
233

            
234
  for (l = priv->keygrabs; l; l = l->next)
235
    {
236
      AtspiKeyGrab *grab = l->data;
237
      if (grab->id == id)
238
        {
239
          ATSPI_DEVICE_GET_CLASS (device)->remove_key_grab (device, id);
240
          priv->keygrabs = g_slist_remove (priv->keygrabs, grab);
241
          if (grab->callback_destroyed)
242
            (*grab->callback_destroyed) (grab->callback);
243
          g_free (grab);
244
          return;
245
        }
246
    }
247
}
248

            
249
/**
250
 *atspi_device_add_key_watcher:
251
 * @device: the device.
252
 * @callback: (scope notified): the function to call when the given key is
253
 *            pressed.
254
 * @user_data: (closure callback): Data to be passed to @callback.
255
 * @callback_destroyed: (destroy callback): callback function to be called
256
 *                      when @callback is destroyed.
257
 *
258
 * Add a callback that will receive a notification whenever a key is
259
 * pressed or released.
260
 */
261
void
262
atspi_device_add_key_watcher (AtspiDevice *device, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed)
263
{
264
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
265
  AtspiKeyGrab *grab = g_new0 (AtspiKeyGrab, 1);
266
  grab->id = get_grab_id (device);
267
  grab->callback = callback;
268
  grab->callback_data = user_data;
269
  grab->callback_destroyed = callback_destroyed;
270
  priv->key_watchers = g_slist_append (priv->key_watchers, grab);
271
}
272

            
273
AtspiKeyDefinition *
274
atspi_device_get_grab_by_id (AtspiDevice *device, guint id)
275
{
276
  AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
277
  GSList *l;
278

            
279
  for (l = priv->keygrabs; l; l = l->next)
280
    {
281
      AtspiKeyGrab *grab = l->data;
282
      if (grab->id == id)
283
        {
284
          AtspiKeyDefinition *kd = g_new0 (AtspiKeyDefinition, 1);
285
          kd->keycode = grab->keycode;
286
          kd->modifiers = grab->modifiers;
287
          return kd;
288
        }
289
    }
290
  return NULL;
291
}
292

            
293
/**
294
 * atspi_device_map_modifier:
295
 * @device: the device.
296
 * @keycode: the keycode to map.
297
 *
298
 * Maps the specified key code to a modifier so that it can be used in
299
 * conjunction with other keys to create a key grab. If the given keycode is
300
 * already mapped, then this function will return the modifier that is
301
 * currently mapped to the keycode, without doing anything else. Otherwise,
302
 * it will use the last modifier that AT-SPI used to map a key. If no keys
303
 * have yet been mapped using this device, then it will look for a modifier
304
 * that is not currently being used. If no unused modifier can be found,
305
 * then it will use the first modifier by default.
306
 *
307
 * Returns: the modifier that is now mapped to this keycode. This return
308
 * value can be passed to atspi_device_add_key_grab.
309
 */
310
guint
311
atspi_device_map_modifier (AtspiDevice *device, gint keycode)
312
{
313
  if (ATSPI_DEVICE_GET_CLASS (device)->map_modifier)
314
    return ATSPI_DEVICE_GET_CLASS (device)->map_modifier (device, keycode);
315

            
316
  return 0;
317
}
318

            
319
/**
320
 * atspi_device_unmap_modifier:
321
 * @device: the device.
322
 * @keycode: the keycode to unmap.
323
 *
324
 * Removes a mapped modifier from the given keycode.
325
 */
326
void
327
atspi_device_unmap_modifier (AtspiDevice *device, gint keycode)
328
{
329
  if (ATSPI_DEVICE_GET_CLASS (device)->unmap_modifier)
330
    ATSPI_DEVICE_GET_CLASS (device)->unmap_modifier (device, keycode);
331
}
332

            
333
/**
334
 * atspi_device_get_modifier:
335
 * @device: the device.
336
 * @keycode: the keycode to map.
337
 *
338
 * Gets the modifier for a given keycode, if one exists. Does not create a new
339
 * mapping. This function should be used when the intention is to query a
340
 * locking modifier such as num lock via atspi_device_get_locked_modifiers,
341
 * rather than to add key grabs.
342
 *
343
 * Returns: the modifier that is mapped to this keycode.
344
 */
345
guint
346
atspi_device_get_modifier (AtspiDevice *device, gint keycode)
347
{
348
  if (ATSPI_DEVICE_GET_CLASS (device)->get_modifier)
349
    return ATSPI_DEVICE_GET_CLASS (device)->get_modifier (device, keycode);
350

            
351
  return 0;
352
}
353

            
354
/**
355
 * atspi_device_get_locked_modifiers:
356
 * @device: the device.
357
 *
358
 * Returns the locked modifiers (ie, num lock, caps lock) associated with this
359
 * keyboard.
360
 *
361
 * Returns: a guint of modifier flags.
362
 */
363
guint
364
atspi_device_get_locked_modifiers (AtspiDevice *device)
365
{
366
  if (ATSPI_DEVICE_GET_CLASS (device)->get_locked_modifiers)
367
    return ATSPI_DEVICE_GET_CLASS (device)->get_locked_modifiers (device);
368

            
369
  return 0;
370
}
371

            
372
/**
373
 * atspi_device_grab_keyboard:
374
 *
375
 * Attempts to grab the entire keyboard. This should only be done
376
 * temporarily, as it may conflict with other applications that also want to
377
 * grab the keyboard.
378
 *
379
 * Returns: #TRUE if successful, #FALSE otherwise.
380
 */
381
gboolean
382
atspi_device_grab_keyboard (AtspiDevice *device)
383
{
384
  if (ATSPI_DEVICE_GET_CLASS (device)->grab_keyboard)
385
    return ATSPI_DEVICE_GET_CLASS (device)->grab_keyboard (device);
386

            
387
  return FALSE;
388
}
389

            
390
/**
391
 * atspi_device_ungrab_keyboard:
392
 *
393
 * Removes a keyboard grab added via a call to atspi_device_add_keyboard.
394
 */
395
void
396
atspi_device_ungrab_keyboard (AtspiDevice *device)
397
{
398
  if (ATSPI_DEVICE_GET_CLASS (device)->ungrab_keyboard)
399
    ATSPI_DEVICE_GET_CLASS (device)->ungrab_keyboard (device);
400
}
401

            
402
/**
403
 * atspi_device_generate_mouse_event:
404
 * @device: the device.
405
 * @obj: The #AtspiAccessible that should receive the click.
406
 * @x: a #gint indicating the x coordinate of the mouse event, relative to
407
 *     @obj..
408
 * @y: a #gint indicating the y coordinate of the mouse event, relative to
409
 *     @obj..
410
 * @name: a string indicating which mouse event to be synthesized
411
 *        (e.g. "b1p", "b1c", "b2r", "rel", "abs").
412
 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
413
 *
414
 * Synthesizes a mouse event at a specific screen coordinate.
415
 * Most AT clients should use the #AccessibleAction interface when
416
 * tempted to generate mouse events, rather than this method.
417
 * Event names: b1p = button 1 press; b2r = button 2 release;
418
 *              b3c = button 3 click; b2d = button 2 double-click;
419
 *              abs = absolute motion; rel = relative motion.
420
 *
421
 * Since: 2.52
422
 **/
423
void
424
atspi_device_generate_mouse_event (AtspiDevice *device, AtspiAccessible *obj, gint x, gint y, const gchar *name, GError **error)
425
{
426
  if (ATSPI_DEVICE_GET_CLASS (device)->generate_mouse_event)
427
    ATSPI_DEVICE_GET_CLASS (device)->generate_mouse_event (device, obj, x, y, name, error);
428
}