diff --git a/clutter/clutter/x11/clutter-keymap-x11.c b/clutter/clutter/x11/clutter-keymap-x11.c index 914e31434..c34e676a4 100644 --- a/clutter/clutter/x11/clutter-keymap-x11.c +++ b/clutter/clutter/x11/clutter-keymap-x11.c @@ -38,6 +38,14 @@ typedef struct _ClutterKeymapX11Class ClutterKeymapX11Class; typedef struct _DirectionCacheEntry DirectionCacheEntry; +typedef struct _ClutterKeymapKey ClutterKeymapKey; + +struct _ClutterKeymapKey +{ + guint keycode; + guint group; + guint level; +}; struct _DirectionCacheEntry { @@ -59,6 +67,7 @@ struct _ClutterKeymapX11 ClutterModifierType num_lock_mask; ClutterModifierType scroll_lock_mask; + ClutterModifierType level3_shift_mask; PangoDirection current_direction; @@ -69,6 +78,7 @@ struct _ClutterKeymapX11 Atom current_group_atom; guint current_cache_serial; DirectionCacheEntry group_direction_cache[4]; + int current_group; #endif guint caps_lock_state : 1; @@ -198,6 +208,9 @@ get_xkb (ClutterKeymapX11 *keymap_x11) if (keymap_x11->scroll_lock_mask == 0) keymap_x11->scroll_lock_mask = XkbKeysymToModifiers (backend_x11->xdpy, XK_Scroll_Lock); + if (keymap_x11->level3_shift_mask == 0) + keymap_x11->level3_shift_mask = XkbKeysymToModifiers (backend_x11->xdpy, + XK_ISO_Level3_Shift); return keymap_x11->xkb_desc; } @@ -469,6 +482,7 @@ static void clutter_keymap_x11_init (ClutterKeymapX11 *keymap) { keymap->current_direction = PANGO_DIRECTION_NEUTRAL; + keymap->current_group = -1; } static ClutterTranslateReturn @@ -498,7 +512,8 @@ clutter_keymap_x11_translate_event (ClutterEventTranslator *translator, { case XkbStateNotify: CLUTTER_NOTE (EVENT, "Updating keyboard state"); - update_direction (keymap_x11, XkbStateGroup (&xkb_event->state)); + keymap_x11->current_group = XkbStateGroup (&xkb_event->state); + update_direction (keymap_x11, keymap_x11->current_group); update_locked_mods (keymap_x11, xkb_event->state.locked_mods); retval = CLUTTER_TRANSLATE_REMOVE; break; @@ -665,3 +680,164 @@ _clutter_keymap_x11_get_direction (ClutterKeymapX11 *keymap) #endif return PANGO_DIRECTION_NEUTRAL; } + +static gboolean +clutter_keymap_x11_get_entries_for_keyval (ClutterKeymapX11 *keymap_x11, + guint keyval, + ClutterKeymapKey **keys, + gint *n_keys) +{ +#ifdef HAVE_XKB + if (CLUTTER_BACKEND_X11 (keymap_x11->backend)->use_xkb) + { + XkbDescRec *xkb = get_xkb (keymap_x11); + GArray *retval; + gint keycode; + + keycode = keymap_x11->min_keycode; + retval = g_array_new (FALSE, FALSE, sizeof (ClutterKeymapKey)); + + while (keycode <= keymap_x11->max_keycode) + { + gint max_shift_levels = XkbKeyGroupsWidth (xkb, keycode); + gint group = 0; + gint level = 0; + gint total_syms = XkbKeyNumSyms (xkb, keycode); + gint i = 0; + KeySym *entry; + + /* entry is an array with all syms for group 0, all + * syms for group 1, etc. and for each group the + * shift level syms are in order + */ + entry = XkbKeySymsPtr (xkb, keycode); + + while (i < total_syms) + { + g_assert (i == (group * max_shift_levels + level)); + + if (entry[i] == keyval) + { + ClutterKeymapKey key; + + key.keycode = keycode; + key.group = group; + key.level = level; + + g_array_append_val (retval, key); + + g_assert (XkbKeySymEntry (xkb, keycode, level, group) == + keyval); + } + + ++level; + + if (level == max_shift_levels) + { + level = 0; + ++group; + } + + ++i; + } + + ++keycode; + } + + if (retval->len > 0) + { + *keys = (ClutterKeymapKey*) retval->data; + *n_keys = retval->len; + } + else + { + *keys = NULL; + *n_keys = 0; + } + + g_array_free (retval, retval->len > 0 ? FALSE : TRUE); + + return *n_keys > 0; + } + else +#endif + { + return FALSE; + } +} + +void +clutter_keymap_x11_latch_modifiers (ClutterKeymapX11 *keymap_x11, + uint32_t level, + gboolean enable) +{ +#ifdef HAVE_XKB + ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend); + uint32_t modifiers[] = { + 0, + ShiftMask, + keymap_x11->level3_shift_mask, + keymap_x11->level3_shift_mask | ShiftMask, + }; + uint32_t value = 0; + + if (!backend_x11->use_xkb) + return; + + level = CLAMP (level, 0, G_N_ELEMENTS (modifiers) - 1); + + if (enable) + value = modifiers[level]; + else + value = 0; + + XkbLatchModifiers (clutter_x11_get_default_display (), + XkbUseCoreKbd, modifiers[level], + value); +#endif +} + +static uint32_t +clutter_keymap_x11_get_current_group (ClutterKeymapX11 *keymap_x11) +{ + ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend); + XkbStateRec state_rec; + + if (keymap_x11->current_group >= 0) + return keymap_x11->current_group; + + XkbGetState (backend_x11->xdpy, XkbUseCoreKbd, &state_rec); + return XkbStateGroup (&state_rec); +} + +gboolean +clutter_keymap_x11_keycode_for_keyval (ClutterKeymapX11 *keymap_x11, + guint keyval, + guint *keycode_out, + guint *level_out) +{ + ClutterKeymapKey *keys; + gint i, n_keys, group; + gboolean found = FALSE; + + g_return_val_if_fail (keycode_out != NULL, FALSE); + g_return_val_if_fail (level_out != NULL, FALSE); + + group = clutter_keymap_x11_get_current_group (keymap_x11); + + if (!clutter_keymap_x11_get_entries_for_keyval (keymap_x11, keyval, &keys, &n_keys)) + return FALSE; + + for (i = 0; i < n_keys && !found; i++) + { + if (keys[i].group == group) + { + *keycode_out = keys[i].keycode; + *level_out = keys[i].level; + found = TRUE; + } + } + + g_free (keys); + return found; +} diff --git a/clutter/clutter/x11/clutter-keymap-x11.h b/clutter/clutter/x11/clutter-keymap-x11.h index ad673a2a7..4b5b403c8 100644 --- a/clutter/clutter/x11/clutter-keymap-x11.h +++ b/clutter/clutter/x11/clutter-keymap-x11.h @@ -51,6 +51,14 @@ gboolean _clutter_keymap_x11_get_is_modifier (ClutterKeymapX11 *keymap, PangoDirection _clutter_keymap_x11_get_direction (ClutterKeymapX11 *keymap); +gboolean clutter_keymap_x11_keycode_for_keyval (ClutterKeymapX11 *keymap_x11, + guint keyval, + guint *keycode_out, + guint *level_out); +void clutter_keymap_x11_latch_modifiers (ClutterKeymapX11 *keymap_x11, + uint32_t level, + gboolean enable); + G_END_DECLS #endif /* __CLUTTER_KEYMAP_X11_H__ */ diff --git a/clutter/clutter/x11/clutter-virtual-input-device-x11.c b/clutter/clutter/x11/clutter-virtual-input-device-x11.c index 416c944b3..b86ded0d0 100644 --- a/clutter/clutter/x11/clutter-virtual-input-device-x11.c +++ b/clutter/clutter/x11/clutter-virtual-input-device-x11.c @@ -32,6 +32,8 @@ #include "clutter-virtual-input-device.h" #include "x11/clutter-virtual-input-device-x11.h" +#include "x11/clutter-backend-x11.h" +#include "x11/clutter-keymap-x11.h" struct _ClutterVirtualInputDeviceX11 { @@ -135,11 +137,25 @@ clutter_virtual_input_device_x11_notify_keyval (ClutterVirtualInputDevice *virtu uint32_t keyval, ClutterKeyState key_state) { - KeyCode keycode; + ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + ClutterKeymapX11 *keymap = backend_x11->keymap; + uint32_t keycode, level; + + if (!clutter_keymap_x11_keycode_for_keyval (keymap, keyval, &keycode, &level)) + { + g_warning ("No keycode found for keyval %x in current group", keyval); + return; + } + + if (key_state == CLUTTER_KEY_STATE_PRESSED) + clutter_keymap_x11_latch_modifiers (keymap, level, TRUE); - keycode = XKeysymToKeycode (clutter_x11_get_default_display (), keyval); XTestFakeKeyEvent (clutter_x11_get_default_display (), - keycode, key_state == CLUTTER_KEY_STATE_PRESSED, 0); + (KeyCode) keycode, + key_state == CLUTTER_KEY_STATE_PRESSED, 0); + + if (key_state == CLUTTER_KEY_STATE_RELEASED) + clutter_keymap_x11_latch_modifiers (keymap, level, FALSE); } static void