Protecting against potential NULL pointer dereference
[platform/upstream/at-spi2-core.git] / atspi / atspi-device-legacy.c
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-private.h"
24 #include "atspi-device-legacy.h"
25
26 #ifdef HAVE_X11
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/extensions/XInput2.h>
30 #include <X11/XKBlib.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,
56                           ATSPI_TYPE_DEVICE,
57                           G_ADD_PRIVATE (AtspiDeviceLegacy))
58
59
60 static guint
61 find_virtual_mapping (AtspiDeviceLegacy *legacy_device, gint keycode)
62 {
63   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
64   GSList *l;
65
66   for (l = priv->modifiers; l; l = l->next)
67   {
68     AtspiLegacyKeyModifier *entry = l->data;
69     if (entry->keycode == keycode)
70       return entry->modifier;
71   }
72
73   return 0;
74 }
75
76 static void
77 set_virtual_modifier (AtspiDeviceLegacy *legacy_device, gint keycode, gboolean enabled)
78 {
79   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
80   guint modifier = find_virtual_mapping (legacy_device, keycode);
81
82   if (enabled)
83     priv->virtual_mods_enabled |= modifier;
84   else
85     priv->virtual_mods_enabled &= ~modifier;
86 }
87
88
89 gboolean
90 key_cb (AtspiDeviceEvent *event, void *user_data)
91 {
92   AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (user_data);
93   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
94   gboolean ret = priv->keyboard_grabbed;
95   guint modifiers;
96   
97   set_virtual_modifier (legacy_device, event->hw_code,
98                         event->type == (AtspiEventType)ATSPI_KEY_PRESS);
99
100   modifiers = event->modifiers | priv->virtual_mods_enabled;
101   if (modifiers & (1 << ATSPI_MODIFIER_NUMLOCK))
102     modifiers &= ~priv->numlock_physical_mask;
103
104   ret |= atspi_device_notify_key (ATSPI_DEVICE (legacy_device),
105                                   event->type == (AtspiEventType)ATSPI_KEY_PRESS,
106                                   event->hw_code, event->id,
107                                   modifiers,
108                                   event->event_string);
109
110   g_boxed_free (ATSPI_TYPE_DEVICE_EVENT, event);
111   return ret;
112 }
113
114 static guint
115 atspi_device_legacy_get_locked_modifiers (AtspiDevice *device)
116 {
117 #ifdef HAVE_X11
118   AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
119   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
120   XkbStateRec state_rec;
121
122   memset (&state_rec, 0, sizeof (state_rec));
123   XkbGetState (priv->display, XkbUseCoreKbd, &state_rec);
124   return state_rec.locked_mods;
125 #else
126   return 0;
127 #endif
128 }
129
130 static gboolean
131 check_virtual_modifier (AtspiDeviceLegacy *legacy_device, guint modifier)
132 {
133   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
134   GSList *l;
135
136   if (modifier == (1 << ATSPI_MODIFIER_NUMLOCK))
137     return TRUE;
138
139   for (l = priv->modifiers; l; l = l->next)
140   {
141     AtspiLegacyKeyModifier *entry = l->data;
142     if (entry->modifier == modifier)
143       return TRUE;
144   }
145
146   return FALSE;
147 }
148
149 static guint
150 get_unused_virtual_modifier (AtspiDeviceLegacy *legacy_device)
151 {
152   guint ret = 0x1000;
153
154   while (ret < 0x10000)
155   {
156     if (!check_virtual_modifier (legacy_device, ret))
157       return ret;
158     ret <<= 1;
159   }
160
161   return 0;
162 }
163
164 static guint
165 atspi_device_legacy_map_modifier (AtspiDevice *device, gint keycode)
166 {
167   AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
168   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
169   guint ret;
170   AtspiLegacyKeyModifier *entry;
171 #ifdef HAVE_X11
172   XkbDescPtr desc;
173
174   desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd);
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 #endif
188
189   ret = find_virtual_mapping (legacy_device, keycode);
190   if (ret)
191     return ret;
192
193   ret = get_unused_virtual_modifier (legacy_device);
194
195   entry = g_new (AtspiLegacyKeyModifier, 1);
196   entry->keycode = keycode;
197   entry->modifier = ret;
198   priv->modifiers = g_slist_append (priv->modifiers, entry);
199
200   return ret;
201 }
202
203 static void
204 atspi_device_legacy_unmap_modifier (AtspiDevice *device, gint keycode)
205 {
206   AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
207   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
208   GSList *l;
209
210   for (l = priv->modifiers; l; l = l->next)
211   {
212     AtspiLegacyKeyModifier *entry = l->data;
213     if (entry->keycode == keycode)
214     {
215       priv->modifiers = g_slist_remove (priv->modifiers, entry);
216       g_free (entry);
217       return;
218     }
219   }
220 }
221
222 static guint
223 atspi_device_legacy_get_modifier (AtspiDevice *device, gint keycode)
224 {
225   AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
226 #ifdef HAVE_X11
227   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
228   XkbDescPtr desc;
229   guint ret;
230
231   desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd);
232
233   if (keycode < desc->min_key_code || keycode >= desc->max_key_code)
234   {
235     XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
236     g_warning ("Passed invalid keycode %d", keycode);
237     return 0;
238   }
239
240   ret = desc->map->modmap[keycode];
241   XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
242   if (ret)
243     return ret;
244 #endif
245
246   return find_virtual_mapping (legacy_device, keycode);
247 }
248
249 static gboolean
250 atspi_device_legacy_grab_keyboard (AtspiDevice *device)
251 {
252   AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
253   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
254
255   priv->keyboard_grabbed = TRUE;
256   return TRUE;
257 }
258
259 static void
260 atspi_device_legacy_ungrab_keyboard (AtspiDevice *device)
261 {
262   AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
263   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
264
265   priv->keyboard_grabbed = FALSE;
266 }
267
268 static void
269 atspi_device_legacy_init (AtspiDeviceLegacy *device)
270 {
271   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device);
272   gint i;
273
274   priv->listener = atspi_device_listener_new (key_cb, device, NULL);
275   for (i = 0; i < 256; i++)
276     atspi_register_keystroke_listener (priv->listener, NULL, i, 3, ATSPI_KEYLISTENER_SYNCHRONOUS | ATSPI_KEYLISTENER_CANCONSUME, NULL);
277
278 #ifdef HAVE_X11
279   priv->display=XOpenDisplay("");
280   if (priv->display)
281     priv->window = DefaultRootWindow(priv->display);
282   priv->numlock_physical_mask = XkbKeysymToModifiers (priv->display,
283                                                       XK_Num_Lock);
284 #endif
285
286 }
287
288 static void
289 atspi_device_legacy_finalize (GObject *object)
290 {
291   AtspiDeviceLegacy *device = ATSPI_DEVICE_LEGACY (object);
292   AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device);
293
294   g_clear_object (&priv->listener);
295   device_legacy_parent_class->finalize (object);
296 }
297
298
299 static void
300 atspi_device_legacy_class_init (AtspiDeviceLegacyClass *klass)
301 {
302   GObjectClass *object_class = (GObjectClass *) klass;
303   AtspiDeviceClass *device_class = ATSPI_DEVICE_CLASS (klass);
304
305   device_legacy_parent_class = g_type_class_peek_parent (klass);
306   object_class->finalize = atspi_device_legacy_finalize;
307   device_class->map_modifier = atspi_device_legacy_map_modifier;
308   device_class->unmap_modifier = atspi_device_legacy_unmap_modifier;
309   device_class->get_modifier = atspi_device_legacy_get_modifier;
310   device_class->get_locked_modifiers = atspi_device_legacy_get_locked_modifiers;
311   device_class->grab_keyboard = atspi_device_legacy_grab_keyboard;
312   device_class->ungrab_keyboard = atspi_device_legacy_ungrab_keyboard;
313 }
314
315 /**
316  * atspi_device_legacy_new:
317  *
318  * Creates a new #AtspiDeviceLegacy.
319  *
320  * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceLegacy.
321  *
322  **/
323 AtspiDeviceLegacy *
324 atspi_device_legacy_new ()
325 {
326   AtspiDeviceLegacy *device = g_object_new (atspi_device_legacy_get_type (), NULL);
327
328   return device;
329 }