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-legacy.h"
24
#include "atspi-private.h"
25

            
26
#ifdef HAVE_X11
27
#include <X11/XKBlib.h>
28
#include <X11/Xlib.h>
29
#include <X11/Xutil.h>
30
#include <X11/extensions/XInput2.h>
31
#endif
32

            
33
typedef struct
34
{
35
  guint keycode;
36
  guint modifier;
37
} AtspiLegacyKeyModifier;
38

            
39
typedef struct _AtspiDeviceLegacyPrivate AtspiDeviceLegacyPrivate;
40
struct _AtspiDeviceLegacyPrivate
41
{
42
  AtspiDeviceListener *listener;
43
#ifdef HAVE_X11
44
  Display *display;
45
  Window window;
46
#endif
47
  GSList *modifiers;
48
  guint virtual_mods_enabled;
49
  gboolean keyboard_grabbed;
50
  unsigned int numlock_physical_mask;
51
};
52

            
53
GObjectClass *device_legacy_parent_class;
54

            
55
G_DEFINE_TYPE_WITH_CODE (AtspiDeviceLegacy, atspi_device_legacy, ATSPI_TYPE_DEVICE, G_ADD_PRIVATE (AtspiDeviceLegacy))
56

            
57
static guint
58
find_virtual_mapping (AtspiDeviceLegacy *legacy_device, gint keycode)
59
{
60
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
61
  GSList *l;
62

            
63
  for (l = priv->modifiers; l; l = l->next)
64
    {
65
      AtspiLegacyKeyModifier *entry = l->data;
66
      if (entry->keycode == keycode)
67
        return entry->modifier;
68
    }
69

            
70
  return 0;
71
}
72

            
73
static void
74
set_virtual_modifier (AtspiDeviceLegacy *legacy_device, gint keycode, gboolean enabled)
75
{
76
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
77
  guint modifier = find_virtual_mapping (legacy_device, keycode);
78

            
79
  if (enabled)
80
    priv->virtual_mods_enabled |= modifier;
81
  else
82
    priv->virtual_mods_enabled &= ~modifier;
83
}
84

            
85
static gboolean
86
key_cb (AtspiDeviceEvent *event, void *user_data)
87
{
88
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (user_data);
89
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
90
  gboolean ret = priv->keyboard_grabbed;
91
  guint modifiers;
92

            
93
  g_object_ref (legacy_device);
94
  set_virtual_modifier (legacy_device, event->hw_code,
95
                        event->type == (AtspiEventType) ATSPI_KEY_PRESS);
96

            
97
  modifiers = event->modifiers | priv->virtual_mods_enabled;
98
  if (modifiers & (1 << ATSPI_MODIFIER_NUMLOCK))
99
    modifiers &= ~priv->numlock_physical_mask;
100

            
101
  ret |= atspi_device_notify_key (ATSPI_DEVICE (legacy_device),
102
                                  event->type == (AtspiEventType) ATSPI_KEY_PRESS,
103
                                  event->hw_code, event->id,
104
                                  modifiers,
105
                                  event->event_string);
106

            
107
  g_boxed_free (ATSPI_TYPE_DEVICE_EVENT, event);
108
  g_object_unref (legacy_device);
109
  return ret;
110
}
111

            
112
static guint
113
atspi_device_legacy_get_locked_modifiers (AtspiDevice *device)
114
{
115
#ifdef HAVE_X11
116
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
117
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
118
  XkbStateRec state_rec;
119

            
120
  memset (&state_rec, 0, sizeof (state_rec));
121
  XkbGetState (priv->display, XkbUseCoreKbd, &state_rec);
122
  return state_rec.locked_mods;
123
#else
124
  return 0;
125
#endif
126
}
127

            
128
static gboolean
129
check_virtual_modifier (AtspiDeviceLegacy *legacy_device, guint modifier)
130
{
131
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
132
  GSList *l;
133

            
134
  if (modifier == (1 << ATSPI_MODIFIER_NUMLOCK))
135
    return TRUE;
136

            
137
  for (l = priv->modifiers; l; l = l->next)
138
    {
139
      AtspiLegacyKeyModifier *entry = l->data;
140
      if (entry->modifier == modifier)
141
        return TRUE;
142
    }
143

            
144
  return FALSE;
145
}
146

            
147
static guint
148
get_unused_virtual_modifier (AtspiDeviceLegacy *legacy_device)
149
{
150
  guint ret = 0x1000;
151

            
152
  while (ret < 0x10000)
153
    {
154
      if (!check_virtual_modifier (legacy_device, ret))
155
        return ret;
156
      ret <<= 1;
157
    }
158

            
159
  return 0;
160
}
161

            
162
static guint
163
atspi_device_legacy_map_modifier (AtspiDevice *device, gint keycode)
164
{
165
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
166
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
167
  guint ret;
168
  AtspiLegacyKeyModifier *entry;
169
#ifdef HAVE_X11
170
  XkbDescPtr desc;
171

            
172
  desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd);
173

            
174
  if (desc)
175
    {
176
      if (keycode < desc->min_key_code || keycode >= desc->max_key_code)
177
        {
178
          XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
179
          g_warning ("Passed invalid keycode %d", keycode);
180
          return 0;
181
        }
182

            
183
      ret = desc->map->modmap[keycode];
184
      XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
185
      if (ret & (ShiftMask | ControlMask))
186
        return ret;
187
    }
188
#endif
189

            
190
  ret = find_virtual_mapping (legacy_device, keycode);
191
  if (ret)
192
    return ret;
193

            
194
  ret = get_unused_virtual_modifier (legacy_device);
195

            
196
  entry = g_new (AtspiLegacyKeyModifier, 1);
197
  entry->keycode = keycode;
198
  entry->modifier = ret;
199
  priv->modifiers = g_slist_append (priv->modifiers, entry);
200

            
201
  return ret;
202
}
203

            
204
static void
205
atspi_device_legacy_unmap_modifier (AtspiDevice *device, gint keycode)
206
{
207
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
208
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
209
  GSList *l;
210

            
211
  for (l = priv->modifiers; l; l = l->next)
212
    {
213
      AtspiLegacyKeyModifier *entry = l->data;
214
      if (entry->keycode == keycode)
215
        {
216
          priv->modifiers = g_slist_remove (priv->modifiers, entry);
217
          g_free (entry);
218
          return;
219
        }
220
    }
221
}
222

            
223
static guint
224
atspi_device_legacy_get_modifier (AtspiDevice *device, gint keycode)
225
{
226
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
227
#ifdef HAVE_X11
228
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
229
  XkbDescPtr desc;
230
  guint ret;
231

            
232
  desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd);
233

            
234
  if (desc)
235
    {
236
      if (keycode < desc->min_key_code || keycode >= desc->max_key_code)
237
        {
238
          XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
239
          g_warning ("Passed invalid keycode %d", keycode);
240
          return 0;
241
        }
242

            
243
      ret = desc->map->modmap[keycode];
244
      XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
245
      if (ret)
246
        return ret;
247
    }
248
#endif
249

            
250
  return find_virtual_mapping (legacy_device, keycode);
251
}
252

            
253
static gboolean
254
atspi_device_legacy_grab_keyboard (AtspiDevice *device)
255
{
256
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
257
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
258

            
259
  priv->keyboard_grabbed = TRUE;
260
  return TRUE;
261
}
262

            
263
static void
264
atspi_device_legacy_ungrab_keyboard (AtspiDevice *device)
265
{
266
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
267
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
268

            
269
  priv->keyboard_grabbed = FALSE;
270
}
271

            
272
static void
273
atspi_device_legacy_generate_mouse_event (AtspiDevice *device, AtspiAccessible *obj, gint x, gint y, const gchar *name, GError **error)
274
{
275
  AtspiPoint *p;
276

            
277
  p = atspi_component_get_position (ATSPI_COMPONENT (obj), ATSPI_COORD_TYPE_SCREEN, error);
278
  if (p->y == -1 && atspi_accessible_get_role (obj, NULL) == ATSPI_ROLE_APPLICATION)
279
    {
280
      g_clear_error (error);
281
      AtspiAccessible *child = atspi_accessible_get_child_at_index (obj, 0, NULL);
282
      if (child)
283
        {
284
          g_free (p);
285
          p = atspi_component_get_position (ATSPI_COMPONENT (child), ATSPI_COORD_TYPE_SCREEN, error);
286
          g_object_unref (child);
287
        }
288
    }
289

            
290
  if (p->y == -1 || p->x == -1)
291
    {
292
      g_free(p);
293
      return;
294
    }
295

            
296
  x += p->x;
297
  y += p->y;
298
  g_free (p);
299

            
300
  atspi_generate_mouse_event (x, y, name, error);
301
}
302

            
303
static guint
304
atspi_device_legacy_map_keysym_modifier (AtspiDevice *device, guint keysym)
305
{
306
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
307
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
308

            
309
  guint resolved_keysym = keysym;
310
#ifdef HAVE_X11
311
  resolved_keysym = XKeysymToKeycode (priv->display, keysym);
312
#endif
313

            
314
  return atspi_device_legacy_map_modifier (device, resolved_keysym);
315
}
316

            
317
static void
318
atspi_device_legacy_unmap_keysym_modifier (AtspiDevice *device, guint keysym)
319
{
320
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
321
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
322

            
323
  guint resolved_keysym = keysym;
324
#ifdef HAVE_X11
325
  resolved_keysym = XKeysymToKeycode (priv->display, keysym);
326
#endif
327

            
328
  atspi_device_legacy_unmap_modifier (device, resolved_keysym);
329
}
330

            
331
static guint
332
atspi_device_legacy_get_keysym_modifier (AtspiDevice *device, guint keysym)
333
{
334
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
335
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
336

            
337
  guint resolved_keysym = keysym;
338
#ifdef HAVE_X11
339
  resolved_keysym = XKeysymToKeycode (priv->display, keysym);
340
#endif
341

            
342
  return atspi_device_legacy_get_modifier (device, resolved_keysym);
343
}
344

            
345
static void
346
atspi_device_legacy_init (AtspiDeviceLegacy *device)
347
{
348
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device);
349
  gint i;
350

            
351
  priv->listener = atspi_device_listener_new (key_cb, device, NULL);
352
  for (i = 0; i < 256; i++)
353
    atspi_register_keystroke_listener (priv->listener, NULL, i, 3, ATSPI_KEYLISTENER_SYNCHRONOUS | ATSPI_KEYLISTENER_CANCONSUME, NULL);
354

            
355
#ifdef HAVE_X11
356
  priv->display = XOpenDisplay ("");
357
  if (priv->display)
358
    priv->window = DefaultRootWindow (priv->display);
359
  priv->numlock_physical_mask = XkbKeysymToModifiers (priv->display,
360
                                                      XK_Num_Lock);
361
#endif
362
}
363

            
364
static void
365
atspi_device_legacy_finalize (GObject *object)
366
{
367
  AtspiDeviceLegacy *device = ATSPI_DEVICE_LEGACY (object);
368
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device);
369

            
370
  g_clear_object (&priv->listener);
371
  device_legacy_parent_class->finalize (object);
372
}
373

            
374
static void
375
atspi_device_legacy_class_init (AtspiDeviceLegacyClass *klass)
376
{
377
  GObjectClass *object_class = (GObjectClass *) klass;
378
  AtspiDeviceClass *device_class = ATSPI_DEVICE_CLASS (klass);
379

            
380
  device_legacy_parent_class = g_type_class_peek_parent (klass);
381
  object_class->finalize = atspi_device_legacy_finalize;
382
  device_class->map_modifier = atspi_device_legacy_map_modifier;
383
  device_class->unmap_modifier = atspi_device_legacy_unmap_modifier;
384
  device_class->get_modifier = atspi_device_legacy_get_modifier;
385
  device_class->get_locked_modifiers = atspi_device_legacy_get_locked_modifiers;
386
  device_class->grab_keyboard = atspi_device_legacy_grab_keyboard;
387
  device_class->ungrab_keyboard = atspi_device_legacy_ungrab_keyboard;
388
  device_class->generate_mouse_event = atspi_device_legacy_generate_mouse_event;
389
  device_class->map_keysym_modifier = atspi_device_legacy_map_keysym_modifier;
390
  device_class->unmap_keysym_modifier = atspi_device_legacy_unmap_keysym_modifier;
391
  device_class->get_keysym_modifier = atspi_device_legacy_get_keysym_modifier;
392
}
393

            
394
/**
395
 * atspi_device_legacy_new_full:
396
 * @app_id: (nullable): The application id.
397
 *
398
 * Creates a new #AtspiDeviceLegacy with the given app id.
399
 *
400
 * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceLegacy.
401
 *
402
 * Since: 2.55
403
 */
404
AtspiDeviceLegacy *
405
atspi_device_legacy_new_full (const gchar *app_id)
406
{
407
  AtspiDeviceLegacy *device = g_object_new (atspi_device_legacy_get_type (), "app-id", app_id, NULL);
408

            
409
  return device;
410
}
411

            
412
/**
413
 * atspi_device_legacy_new:
414
 *
415
 * Creates a new #AtspiDeviceLegacy.
416
 *
417
 * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceLegacy.
418
 *
419
 */
420
AtspiDeviceLegacy *
421
atspi_device_legacy_new (void)
422
{
423
  return atspi_device_legacy_new_full (NULL);
424
}