4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2010 Intel Corp.
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.
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.
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/>.
21 * Author: Emmanuele Bassi <ebassi@linux.intel.com>
26 #include "clutter-keymap-x11.h"
27 #include "clutter-backend-x11.h"
29 #include "clutter-debug.h"
30 #include "clutter-event-translator.h"
31 #include "clutter-private.h"
33 #include <X11/Xatom.h>
36 #include <X11/XKBlib.h>
39 typedef struct _ClutterKeymapX11Class ClutterKeymapX11Class;
41 struct _ClutterKeymapX11
43 GObject parent_instance;
45 ClutterBackend *backend;
50 ClutterModifierType modmap[8];
52 ClutterModifierType num_lock_mask;
60 guint caps_lock_state : 1;
61 guint num_lock_state : 1;
64 struct _ClutterKeymapX11Class
66 GObjectClass parent_class;
78 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
80 static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
82 #define clutter_keymap_x11_get_type _clutter_keymap_x11_get_type
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));
90 /* code adapted from gdk/x11/gdkkeys-x11.c - update_modmap */
92 update_modmap (Display *display,
93 ClutterKeymapX11 *keymap_x11)
98 ClutterModifierType mask;
100 { "Meta", 0, CLUTTER_META_MASK },
101 { "Super", 0, CLUTTER_SUPER_MASK },
102 { "Hyper", 0, CLUTTER_HYPER_MASK },
108 if (vmods[0].atom == 0)
109 for (i = 0; vmods[i].name; i++)
110 vmods[i].atom = XInternAtom (display, vmods[i].name, FALSE);
112 for (i = 0; i < 8; i++)
113 keymap_x11->modmap[i] = 1 << i;
115 for (i = 0; i < XkbNumVirtualMods; i++)
117 for (j = 0; vmods[j].atom; j++)
119 if (keymap_x11->xkb_desc->names->vmods[i] == vmods[j].atom)
121 for (k = 0; k < 8; k++)
123 if (keymap_x11->xkb_desc->server->vmods[i] & (1 << k))
124 keymap_x11->modmap[k] |= vmods[j].mask;
132 get_xkb (ClutterKeymapX11 *keymap_x11)
134 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend);
136 if (keymap_x11->max_keycode == 0)
137 XDisplayKeycodes (backend_x11->xdpy,
138 &keymap_x11->min_keycode,
139 &keymap_x11->max_keycode);
141 if (keymap_x11->xkb_desc == NULL)
143 int flags = XkbKeySymsMask
146 | XkbVirtualModsMask;
148 keymap_x11->xkb_desc = XkbGetMap (backend_x11->xdpy, flags, XkbUseCoreKbd);
149 if (G_UNLIKELY (keymap_x11->xkb_desc == NULL))
151 g_error ("Failed to get the keymap from XKB");
155 flags = XkbGroupNamesMask | XkbVirtualModNamesMask;
156 XkbGetNames (backend_x11->xdpy, flags, keymap_x11->xkb_desc);
158 update_modmap (backend_x11->xdpy, keymap_x11);
160 else if (keymap_x11->xkb_map_serial != backend_x11->keymap_serial)
162 int flags = XkbKeySymsMask
165 | XkbVirtualModsMask;
167 CLUTTER_NOTE (BACKEND, "Updating XKB keymap");
169 XkbGetUpdatedMap (backend_x11->xdpy, flags, keymap_x11->xkb_desc);
171 flags = XkbGroupNamesMask | XkbVirtualModNamesMask;
172 XkbGetNames (backend_x11->xdpy, flags, keymap_x11->xkb_desc);
174 update_modmap (backend_x11->xdpy, keymap_x11);
176 keymap_x11->xkb_map_serial = backend_x11->keymap_serial;
179 if (keymap_x11->num_lock_mask == 0)
180 keymap_x11->num_lock_mask = XkbKeysymToModifiers (backend_x11->xdpy,
183 return keymap_x11->xkb_desc;
185 #endif /* HAVE_XKB */
189 update_locked_mods (ClutterKeymapX11 *keymap_x11,
193 gboolean old_caps_lock_state, old_num_lock_state;
195 old_caps_lock_state = keymap_x11->caps_lock_state;
196 old_num_lock_state = keymap_x11->num_lock_state;
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;
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");
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");
213 #endif /* HAVE_XKB */
216 clutter_keymap_x11_constructed (GObject *gobject)
218 ClutterKeymapX11 *keymap_x11 = CLUTTER_KEYMAP_X11 (gobject);
219 ClutterBackendX11 *backend_x11;
221 g_assert (keymap_x11->backend != NULL);
222 backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend);
226 gint xkb_major = XkbMajorVersion;
227 gint xkb_minor = XkbMinorVersion;
229 if (XkbLibraryVersion (&xkb_major, &xkb_minor))
231 xkb_major = XkbMajorVersion;
232 xkb_minor = XkbMinorVersion;
234 if (XkbQueryExtension (backend_x11->xdpy,
236 &keymap_x11->xkb_event_base,
238 &xkb_major, &xkb_minor))
240 Bool detectable_autorepeat_supported;
242 backend_x11->use_xkb = TRUE;
244 XkbSelectEvents (backend_x11->xdpy,
246 XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask,
247 XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask);
249 XkbSelectEventDetails (backend_x11->xdpy,
250 XkbUseCoreKbd, XkbStateNotify,
251 XkbAllStateComponentsMask,
252 XkbGroupLockMask | XkbModifierLockMask);
254 /* enable XKB autorepeat */
255 XkbSetDetectableAutoRepeat (backend_x11->xdpy,
257 &detectable_autorepeat_supported);
259 backend_x11->have_xkb_autorepeat = detectable_autorepeat_supported;
261 CLUTTER_NOTE (BACKEND, "Detectable autorepeat: %s",
262 backend_x11->have_xkb_autorepeat ? "supported"
267 #endif /* HAVE_XKB */
271 clutter_keymap_x11_set_property (GObject *gobject,
276 ClutterKeymapX11 *keymap = CLUTTER_KEYMAP_X11 (gobject);
281 keymap->backend = g_value_get_object (value);
285 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
291 clutter_keymap_x11_finalize (GObject *gobject)
293 ClutterKeymapX11 *keymap;
294 ClutterEventTranslator *translator;
296 keymap = CLUTTER_KEYMAP_X11 (gobject);
297 translator = CLUTTER_EVENT_TRANSLATOR (keymap);
300 _clutter_backend_remove_event_translator (keymap->backend, translator);
302 if (keymap->xkb_desc != NULL)
303 XkbFreeKeyboard (keymap->xkb_desc, XkbAllComponentsMask, True);
306 G_OBJECT_CLASS (clutter_keymap_x11_parent_class)->finalize (gobject);
310 clutter_keymap_x11_class_init (ClutterKeymapX11Class *klass)
312 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
314 obj_props[PROP_BACKEND] =
315 g_param_spec_object ("backend",
317 P_("The Clutter backend"),
318 CLUTTER_TYPE_BACKEND,
319 CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
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);
328 clutter_keymap_x11_init (ClutterKeymapX11 *keymap)
332 static ClutterTranslateReturn
333 clutter_keymap_x11_translate_event (ClutterEventTranslator *translator,
337 ClutterKeymapX11 *keymap_x11 = CLUTTER_KEYMAP_X11 (translator);
338 ClutterBackendX11 *backend_x11;
339 ClutterTranslateReturn retval;
342 backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend);
343 if (!backend_x11->use_xkb)
344 return CLUTTER_TRANSLATE_CONTINUE;
348 retval = CLUTTER_TRANSLATE_CONTINUE;
351 if (xevent->type == keymap_x11->xkb_event_base)
353 XkbEvent *xkb_event = (XkbEvent *) xevent;
355 switch (xkb_event->any.xkb_type)
358 CLUTTER_NOTE (EVENT, "Updating locked modifiers");
359 update_locked_mods (keymap_x11, xkb_event->state.locked_mods);
360 retval = CLUTTER_TRANSLATE_REMOVE;
364 CLUTTER_NOTE (EVENT, "Updating keyboard mapping");
365 XkbRefreshKeyboardMapping (&xkb_event->map);
366 backend_x11->keymap_serial += 1;
367 retval = CLUTTER_TRANSLATE_REMOVE;
374 #endif /* HAVE_XKB */
380 clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
382 iface->translate_event = clutter_keymap_x11_translate_event;
386 _clutter_keymap_x11_get_key_group (ClutterKeymapX11 *keymap,
387 ClutterModifierType state)
390 return XkbGroupForCoreState (state);
393 #endif /* HAVE_XKB */
397 _clutter_keymap_x11_get_num_lock_state (ClutterKeymapX11 *keymap)
399 g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), FALSE);
401 return keymap->num_lock_state;
405 _clutter_keymap_x11_get_caps_lock_state (ClutterKeymapX11 *keymap)
407 g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), FALSE);
409 return keymap->caps_lock_state;
412 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
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
420 translate_keysym (ClutterKeymapX11 *keymap,
421 guint hardware_keycode)
423 ClutterBackendX11 *backend_x11;
426 backend_x11 = CLUTTER_BACKEND_X11 (keymap->backend);
428 retval = XKeycodeToKeysym (backend_x11->xdpy, hardware_keycode, 0);
433 G_GNUC_END_IGNORE_DEPRECATIONS
436 _clutter_keymap_x11_translate_key_state (ClutterKeymapX11 *keymap,
437 guint hardware_keycode,
438 ClutterModifierType modifier_state,
439 ClutterModifierType *mods_p)
441 ClutterBackendX11 *backend_x11;
442 ClutterModifierType unconsumed_modifiers = 0;
445 g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), 0);
447 backend_x11 = CLUTTER_BACKEND_X11 (keymap->backend);
450 if (backend_x11->use_xkb)
452 XkbDescRec *xkb = get_xkb (keymap);
455 if (XkbTranslateKeyCode (xkb, hardware_keycode, modifier_state,
456 &unconsumed_modifiers,
465 #endif /* HAVE_XKB */
466 retval = translate_keysym (keymap, hardware_keycode);
469 *mods_p = unconsumed_modifiers;
475 _clutter_keymap_x11_get_is_modifier (ClutterKeymapX11 *keymap,
478 g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), FALSE);
480 if (keycode < keymap->min_keycode || keycode > keymap->max_keycode)
484 if (CLUTTER_BACKEND_X11 (keymap->backend)->use_xkb)
486 XkbDescRec *xkb = get_xkb (keymap);
488 if (xkb->map->modmap && xkb->map->modmap[keycode] != 0)
491 #endif /* HAVE_XKB */