update to 1.10.4
[profile/ivi/clutter.git] / clutter / x11 / clutter-keymap-x11.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2010  Intel Corp.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Author: Emmanuele Bassi <ebassi@linux.intel.com>
22  */
23
24 #include "config.h"
25
26 #include "clutter-keymap-x11.h"
27 #include "clutter-backend-x11.h"
28
29 #include "clutter-debug.h"
30 #include "clutter-event-translator.h"
31 #include "clutter-private.h"
32
33 #include <X11/Xatom.h>
34
35 #ifdef HAVE_XKB
36 #include <X11/XKBlib.h>
37 #endif
38
39 typedef struct _ClutterKeymapX11Class   ClutterKeymapX11Class;
40
41 struct _ClutterKeymapX11
42 {
43   GObject parent_instance;
44
45   ClutterBackend *backend;
46
47   int min_keycode;
48   int max_keycode;
49
50   ClutterModifierType modmap[8];
51
52   ClutterModifierType num_lock_mask;
53
54 #ifdef HAVE_XKB
55   XkbDescPtr xkb_desc;
56   int xkb_event_base;
57   guint xkb_map_serial;
58 #endif
59
60   guint caps_lock_state : 1;
61   guint num_lock_state  : 1;
62 };
63
64 struct _ClutterKeymapX11Class
65 {
66   GObjectClass parent_class;
67 };
68
69 enum
70 {
71   PROP_0,
72
73   PROP_BACKEND,
74
75   PROP_LAST
76 };
77
78 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
79
80 static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
81
82 #define clutter_keymap_x11_get_type     _clutter_keymap_x11_get_type
83
84 G_DEFINE_TYPE_WITH_CODE (ClutterKeymapX11, clutter_keymap_x11, G_TYPE_OBJECT,
85                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR,
86                                                 clutter_event_translator_iface_init));
87
88 #ifdef HAVE_XKB
89
90 /* code adapted from gdk/x11/gdkkeys-x11.c - update_modmap */
91 static void
92 update_modmap (Display          *display,
93                ClutterKeymapX11 *keymap_x11)
94 {
95   static struct {
96     const gchar *name;
97     Atom atom;
98     ClutterModifierType mask;
99   } vmods[] = {
100     { "Meta",  0, CLUTTER_META_MASK  },
101     { "Super", 0, CLUTTER_SUPER_MASK },
102     { "Hyper", 0, CLUTTER_HYPER_MASK },
103     { NULL, 0, 0 }
104   };
105
106   int i, j, k;
107
108   if (vmods[0].atom == 0)
109     for (i = 0; vmods[i].name; i++)
110       vmods[i].atom = XInternAtom (display, vmods[i].name, FALSE);
111
112   for (i = 0; i < 8; i++)
113     keymap_x11->modmap[i] = 1 << i;
114
115   for (i = 0; i < XkbNumVirtualMods; i++)
116     {
117       for (j = 0; vmods[j].atom; j++)
118         {
119           if (keymap_x11->xkb_desc->names->vmods[i] == vmods[j].atom)
120             {
121               for (k = 0; k < 8; k++)
122                 {
123                   if (keymap_x11->xkb_desc->server->vmods[i] & (1 << k))
124                     keymap_x11->modmap[k] |= vmods[j].mask;
125                 }
126             }
127         }
128     }
129 }
130
131 static XkbDescPtr
132 get_xkb (ClutterKeymapX11 *keymap_x11)
133 {
134   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend);
135
136   if (keymap_x11->max_keycode == 0)
137     XDisplayKeycodes (backend_x11->xdpy,
138                       &keymap_x11->min_keycode,
139                       &keymap_x11->max_keycode);
140
141   if (keymap_x11->xkb_desc == NULL)
142     {
143       int flags = XkbKeySymsMask
144                 | XkbKeyTypesMask
145                 | XkbModifierMapMask
146                 | XkbVirtualModsMask;
147
148       keymap_x11->xkb_desc = XkbGetMap (backend_x11->xdpy, flags, XkbUseCoreKbd);
149       if (G_UNLIKELY (keymap_x11->xkb_desc == NULL))
150         {
151           g_error ("Failed to get the keymap from XKB");
152           return NULL;
153         }
154
155       flags = XkbGroupNamesMask | XkbVirtualModNamesMask;
156       XkbGetNames (backend_x11->xdpy, flags, keymap_x11->xkb_desc);
157
158       update_modmap (backend_x11->xdpy, keymap_x11);
159     }
160   else if (keymap_x11->xkb_map_serial != backend_x11->keymap_serial)
161     {
162       int flags = XkbKeySymsMask
163                 | XkbKeyTypesMask
164                 | XkbModifierMapMask
165                 | XkbVirtualModsMask;
166
167       CLUTTER_NOTE (BACKEND, "Updating XKB keymap");
168
169       XkbGetUpdatedMap (backend_x11->xdpy, flags, keymap_x11->xkb_desc);
170
171       flags = XkbGroupNamesMask | XkbVirtualModNamesMask;
172       XkbGetNames (backend_x11->xdpy, flags, keymap_x11->xkb_desc);
173
174       update_modmap (backend_x11->xdpy, keymap_x11);
175
176       keymap_x11->xkb_map_serial = backend_x11->keymap_serial;
177     }
178
179   if (keymap_x11->num_lock_mask == 0)
180     keymap_x11->num_lock_mask = XkbKeysymToModifiers (backend_x11->xdpy,
181                                                       XK_Num_Lock);
182
183   return keymap_x11->xkb_desc;
184 }
185 #endif /* HAVE_XKB */
186
187 #ifdef HAVE_XKB
188 static void
189 update_locked_mods (ClutterKeymapX11 *keymap_x11,
190                     gint              locked_mods)
191 {
192 #if 0
193   gboolean old_caps_lock_state, old_num_lock_state;
194
195   old_caps_lock_state = keymap_x11->caps_lock_state;
196   old_num_lock_state  = keymap_x11->num_lock_state;
197 #endif
198
199   keymap_x11->caps_lock_state = (locked_mods & CLUTTER_LOCK_MASK) != 0;
200   keymap_x11->num_lock_state  = (locked_mods & keymap_x11->num_lock_mask) != 0;
201
202   CLUTTER_NOTE (BACKEND, "Locks state changed - Num: %s, Caps: %s",
203                 keymap_x11->num_lock_state ? "set" : "unset",
204                 keymap_x11->caps_lock_state ? "set" : "unset");
205
206 #if 0
207   /* Add signal to ClutterBackend? */
208   if ((keymap_x11->caps_lock_state != old_caps_lock_state) ||
209       (keymap_x11->num_lock_state != old_num_lock_state))
210     g_signal_emit_by_name (keymap_x11->backend, "key-lock-changed");
211 #endif
212 }
213 #endif /* HAVE_XKB */
214
215 static void
216 clutter_keymap_x11_constructed (GObject *gobject)
217 {
218   ClutterKeymapX11 *keymap_x11 = CLUTTER_KEYMAP_X11 (gobject);
219   ClutterBackendX11 *backend_x11;
220
221   g_assert (keymap_x11->backend != NULL);
222   backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend);
223
224 #ifdef HAVE_XKB
225   {
226     gint xkb_major = XkbMajorVersion;
227     gint xkb_minor = XkbMinorVersion;
228
229     if (XkbLibraryVersion (&xkb_major, &xkb_minor))
230       {
231         xkb_major = XkbMajorVersion;
232         xkb_minor = XkbMinorVersion;
233
234         if (XkbQueryExtension (backend_x11->xdpy,
235                                NULL,
236                                &keymap_x11->xkb_event_base,
237                                NULL,
238                                &xkb_major, &xkb_minor))
239           {
240             Bool detectable_autorepeat_supported;
241
242             backend_x11->use_xkb = TRUE;
243
244             XkbSelectEvents (backend_x11->xdpy,
245                              XkbUseCoreKbd,
246                              XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask,
247                              XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask);
248
249             XkbSelectEventDetails (backend_x11->xdpy,
250                                    XkbUseCoreKbd, XkbStateNotify,
251                                    XkbAllStateComponentsMask,
252                                    XkbGroupLockMask | XkbModifierLockMask);
253
254             /* enable XKB autorepeat */
255             XkbSetDetectableAutoRepeat (backend_x11->xdpy,
256                                         True,
257                                         &detectable_autorepeat_supported);
258
259             backend_x11->have_xkb_autorepeat = detectable_autorepeat_supported;
260
261             CLUTTER_NOTE (BACKEND, "Detectable autorepeat: %s",
262                           backend_x11->have_xkb_autorepeat ? "supported"
263                                                            : "not supported");
264           }
265       }
266   }
267 #endif /* HAVE_XKB */
268 }
269
270 static void
271 clutter_keymap_x11_set_property (GObject      *gobject,
272                                  guint         prop_id,
273                                  const GValue *value,
274                                  GParamSpec   *pspec)
275 {
276   ClutterKeymapX11 *keymap = CLUTTER_KEYMAP_X11 (gobject);
277
278   switch (prop_id)
279     {
280     case PROP_BACKEND:
281       keymap->backend = g_value_get_object (value);
282       break;
283
284     default:
285       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
286       break;
287     }
288 }
289
290 static void
291 clutter_keymap_x11_finalize (GObject *gobject)
292 {
293   ClutterKeymapX11 *keymap;
294   ClutterEventTranslator *translator;
295
296   keymap = CLUTTER_KEYMAP_X11 (gobject);
297   translator = CLUTTER_EVENT_TRANSLATOR (keymap);
298
299 #ifdef HAVE_XKB
300   _clutter_backend_remove_event_translator (keymap->backend, translator);
301
302   if (keymap->xkb_desc != NULL)
303     XkbFreeKeyboard (keymap->xkb_desc, XkbAllComponentsMask, True);
304 #endif
305
306   G_OBJECT_CLASS (clutter_keymap_x11_parent_class)->finalize (gobject);
307 }
308
309 static void
310 clutter_keymap_x11_class_init (ClutterKeymapX11Class *klass)
311 {
312   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
313
314   obj_props[PROP_BACKEND] =
315     g_param_spec_object ("backend",
316                          P_("Backend"),
317                          P_("The Clutter backend"),
318                          CLUTTER_TYPE_BACKEND,
319                          CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
320
321   gobject_class->constructed = clutter_keymap_x11_constructed;
322   gobject_class->set_property = clutter_keymap_x11_set_property;
323   gobject_class->finalize = clutter_keymap_x11_finalize;
324   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
325 }
326
327 static void
328 clutter_keymap_x11_init (ClutterKeymapX11 *keymap)
329 {
330 }
331
332 static ClutterTranslateReturn
333 clutter_keymap_x11_translate_event (ClutterEventTranslator *translator,
334                                     gpointer                native,
335                                     ClutterEvent           *event)
336 {
337   ClutterKeymapX11 *keymap_x11 = CLUTTER_KEYMAP_X11 (translator);
338   ClutterBackendX11 *backend_x11;
339   ClutterTranslateReturn retval;
340   XEvent *xevent;
341
342   backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend);
343   if (!backend_x11->use_xkb)
344     return CLUTTER_TRANSLATE_CONTINUE;
345
346   xevent = native;
347
348   retval = CLUTTER_TRANSLATE_CONTINUE;
349
350 #ifdef HAVE_XKB
351   if (xevent->type == keymap_x11->xkb_event_base)
352     {
353       XkbEvent *xkb_event = (XkbEvent *) xevent;
354
355       switch (xkb_event->any.xkb_type)
356         {
357         case XkbStateNotify:
358           CLUTTER_NOTE (EVENT, "Updating locked modifiers");
359           update_locked_mods (keymap_x11, xkb_event->state.locked_mods);
360           retval = CLUTTER_TRANSLATE_REMOVE;
361           break;
362
363         case XkbMapNotify:
364           CLUTTER_NOTE (EVENT, "Updating keyboard mapping");
365           XkbRefreshKeyboardMapping (&xkb_event->map);
366           backend_x11->keymap_serial += 1;
367           retval = CLUTTER_TRANSLATE_REMOVE;
368           break;
369
370         default:
371           break;
372         }
373     }
374 #endif /* HAVE_XKB */
375
376   return retval;
377 }
378
379 static void
380 clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
381 {
382   iface->translate_event = clutter_keymap_x11_translate_event;
383 }
384
385 gint
386 _clutter_keymap_x11_get_key_group (ClutterKeymapX11    *keymap,
387                                    ClutterModifierType  state)
388 {
389 #ifdef HAVE_XKB
390   return XkbGroupForCoreState (state);
391 #else
392   return 0;
393 #endif /* HAVE_XKB */
394 }
395
396 gboolean
397 _clutter_keymap_x11_get_num_lock_state (ClutterKeymapX11 *keymap)
398 {
399   g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), FALSE);
400
401   return keymap->num_lock_state;
402 }
403
404 gboolean
405 _clutter_keymap_x11_get_caps_lock_state (ClutterKeymapX11 *keymap)
406 {
407   g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), FALSE);
408
409   return keymap->caps_lock_state;
410 }
411
412 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
413
414 /* XXX - yes, I know that XKeycodeToKeysym() has been deprecated; hopefully,
415  * this code will never get run on any decent system that is also able to
416  * run Clutter. I just don't want to copy the implementation inside GDK for
417  * a fallback path.
418  */
419 static int
420 translate_keysym (ClutterKeymapX11 *keymap,
421                   guint             hardware_keycode)
422 {
423   ClutterBackendX11 *backend_x11;
424   gint retval;
425
426   backend_x11 = CLUTTER_BACKEND_X11 (keymap->backend);
427
428   retval = XKeycodeToKeysym (backend_x11->xdpy, hardware_keycode, 0);
429
430   return retval;
431 }
432
433 G_GNUC_END_IGNORE_DEPRECATIONS
434
435 gint
436 _clutter_keymap_x11_translate_key_state (ClutterKeymapX11    *keymap,
437                                          guint                hardware_keycode,
438                                          ClutterModifierType  modifier_state,
439                                          ClutterModifierType *mods_p)
440 {
441   ClutterBackendX11 *backend_x11;
442   ClutterModifierType unconsumed_modifiers = 0;
443   gint retval;
444
445   g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), 0);
446
447   backend_x11 = CLUTTER_BACKEND_X11 (keymap->backend);
448
449 #ifdef HAVE_XKB
450   if (backend_x11->use_xkb)
451     {
452       XkbDescRec *xkb = get_xkb (keymap);
453       KeySym tmp_keysym;
454
455       if (XkbTranslateKeyCode (xkb, hardware_keycode, modifier_state,
456                                &unconsumed_modifiers,
457                                &tmp_keysym))
458         {
459           retval = tmp_keysym;
460         }
461       else
462         retval = 0;
463     }
464   else
465 #endif /* HAVE_XKB */
466     retval = translate_keysym (keymap, hardware_keycode);
467
468   if (mods_p)
469     *mods_p = unconsumed_modifiers;
470
471   return retval;
472 }
473
474 gboolean
475 _clutter_keymap_x11_get_is_modifier (ClutterKeymapX11 *keymap,
476                                      gint              keycode)
477 {
478   g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), FALSE);
479
480   if (keycode < keymap->min_keycode || keycode > keymap->max_keycode)
481     return FALSE;
482
483 #ifdef HAVE_XKB
484   if (CLUTTER_BACKEND_X11 (keymap->backend)->use_xkb)
485     {
486       XkbDescRec *xkb = get_xkb (keymap);
487
488       if (xkb->map->modmap && xkb->map->modmap[keycode] != 0)
489         return TRUE;
490     }
491 #endif /* HAVE_XKB */
492
493   return FALSE;
494 }