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

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

            
187
  ret = find_virtual_mapping (legacy_device, keycode);
188
  if (ret)
189
    return ret;
190

            
191
  ret = get_unused_virtual_modifier (legacy_device);
192

            
193
  entry = g_new (AtspiLegacyKeyModifier, 1);
194
  entry->keycode = keycode;
195
  entry->modifier = ret;
196
  priv->modifiers = g_slist_append (priv->modifiers, entry);
197

            
198
  return ret;
199
}
200

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

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

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

            
229
  desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd);
230

            
231
  if (keycode < desc->min_key_code || keycode >= desc->max_key_code)
232
    {
233
      XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
234
      g_warning ("Passed invalid keycode %d", keycode);
235
      return 0;
236
    }
237

            
238
  ret = desc->map->modmap[keycode];
239
  XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
240
  if (ret)
241
    return ret;
242
#endif
243

            
244
  return find_virtual_mapping (legacy_device, keycode);
245
}
246

            
247
static gboolean
248
atspi_device_legacy_grab_keyboard (AtspiDevice *device)
249
{
250
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
251
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
252

            
253
  priv->keyboard_grabbed = TRUE;
254
  return TRUE;
255
}
256

            
257
static void
258
atspi_device_legacy_ungrab_keyboard (AtspiDevice *device)
259
{
260
  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
261
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
262

            
263
  priv->keyboard_grabbed = FALSE;
264
}
265

            
266
static void
267
atspi_device_legacy_generate_mouse_event (AtspiDevice *device, AtspiAccessible *obj, gint x, gint y, const gchar *name, GError **error)
268
{
269
  AtspiPoint *p;
270

            
271
  p = atspi_component_get_position (ATSPI_COMPONENT (obj), ATSPI_COORD_TYPE_SCREEN, error);
272
  if (p->y == -1 && atspi_accessible_get_role (obj, NULL) == ATSPI_ROLE_APPLICATION)
273
    {
274
      g_clear_error (error);
275
      g_free (p);
276
      AtspiAccessible *child = atspi_accessible_get_child_at_index (obj, 0, NULL);
277
      if (child)
278
        {
279
          p = atspi_component_get_position (ATSPI_COMPONENT (child), ATSPI_COORD_TYPE_SCREEN, error);
280
          g_object_unref (child);
281
        }
282
    }
283

            
284
  if (p->y == -1 || p->x == -1)
285
    return;
286

            
287
  x += p->x;
288
  y += p->y;
289
  g_free (p);
290

            
291
  atspi_generate_mouse_event (x, y, name, error);
292
}
293

            
294
static void
295
atspi_device_legacy_init (AtspiDeviceLegacy *device)
296
{
297
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device);
298
  gint i;
299

            
300
  priv->listener = atspi_device_listener_new (key_cb, device, NULL);
301
  for (i = 0; i < 256; i++)
302
    atspi_register_keystroke_listener (priv->listener, NULL, i, 3, ATSPI_KEYLISTENER_SYNCHRONOUS | ATSPI_KEYLISTENER_CANCONSUME, NULL);
303

            
304
#ifdef HAVE_X11
305
  priv->display = XOpenDisplay ("");
306
  if (priv->display)
307
    priv->window = DefaultRootWindow (priv->display);
308
  priv->numlock_physical_mask = XkbKeysymToModifiers (priv->display,
309
                                                      XK_Num_Lock);
310
#endif
311
}
312

            
313
static void
314
atspi_device_legacy_finalize (GObject *object)
315
{
316
  AtspiDeviceLegacy *device = ATSPI_DEVICE_LEGACY (object);
317
  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device);
318

            
319
  g_clear_object (&priv->listener);
320
  device_legacy_parent_class->finalize (object);
321
}
322

            
323
static void
324
atspi_device_legacy_class_init (AtspiDeviceLegacyClass *klass)
325
{
326
  GObjectClass *object_class = (GObjectClass *) klass;
327
  AtspiDeviceClass *device_class = ATSPI_DEVICE_CLASS (klass);
328

            
329
  device_legacy_parent_class = g_type_class_peek_parent (klass);
330
  object_class->finalize = atspi_device_legacy_finalize;
331
  device_class->map_modifier = atspi_device_legacy_map_modifier;
332
  device_class->unmap_modifier = atspi_device_legacy_unmap_modifier;
333
  device_class->get_modifier = atspi_device_legacy_get_modifier;
334
  device_class->get_locked_modifiers = atspi_device_legacy_get_locked_modifiers;
335
  device_class->grab_keyboard = atspi_device_legacy_grab_keyboard;
336
  device_class->ungrab_keyboard = atspi_device_legacy_ungrab_keyboard;
337
  device_class->generate_mouse_event = atspi_device_legacy_generate_mouse_event;
338
}
339

            
340
/**
341
 * atspi_device_legacy_new:
342
 *
343
 * Creates a new #AtspiDeviceLegacy.
344
 *
345
 * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceLegacy.
346
 *
347
 **/
348
AtspiDeviceLegacy *
349
atspi_device_legacy_new ()
350
{
351
  AtspiDeviceLegacy *device = g_object_new (atspi_device_legacy_get_type (), NULL);
352

            
353
  return device;
354
}