clutter/x11: Implement keycode lookup from keysyms on virtual key devices

Unfortunately XKeysymToKeycode() falls short in that it coalesces keysyms
into keycodes pertaining to the first level (i.e. lowercase). Add a
ClutterKeymapX11 method (much alike its GdkKeymap counterpart) to look up
all matches for the given keysym.

Two other helper methods have been added so the virtual device can fetch
the current keyboard group, and latch modifiers for key emission. Combining
all this, the virtual device is now able to handle keycodes in further
levels.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/135
This commit is contained in:
Carlos Garnacho 2018-06-29 14:31:23 +02:00
parent 388d065699
commit 85284acb00
3 changed files with 204 additions and 4 deletions

View File

@ -38,6 +38,14 @@
typedef struct _ClutterKeymapX11Class ClutterKeymapX11Class; typedef struct _ClutterKeymapX11Class ClutterKeymapX11Class;
typedef struct _DirectionCacheEntry DirectionCacheEntry; typedef struct _DirectionCacheEntry DirectionCacheEntry;
typedef struct _ClutterKeymapKey ClutterKeymapKey;
struct _ClutterKeymapKey
{
guint keycode;
guint group;
guint level;
};
struct _DirectionCacheEntry struct _DirectionCacheEntry
{ {
@ -59,6 +67,7 @@ struct _ClutterKeymapX11
ClutterModifierType num_lock_mask; ClutterModifierType num_lock_mask;
ClutterModifierType scroll_lock_mask; ClutterModifierType scroll_lock_mask;
ClutterModifierType level3_shift_mask;
PangoDirection current_direction; PangoDirection current_direction;
@ -69,6 +78,7 @@ struct _ClutterKeymapX11
Atom current_group_atom; Atom current_group_atom;
guint current_cache_serial; guint current_cache_serial;
DirectionCacheEntry group_direction_cache[4]; DirectionCacheEntry group_direction_cache[4];
int current_group;
#endif #endif
guint caps_lock_state : 1; guint caps_lock_state : 1;
@ -198,6 +208,9 @@ get_xkb (ClutterKeymapX11 *keymap_x11)
if (keymap_x11->scroll_lock_mask == 0) if (keymap_x11->scroll_lock_mask == 0)
keymap_x11->scroll_lock_mask = XkbKeysymToModifiers (backend_x11->xdpy, keymap_x11->scroll_lock_mask = XkbKeysymToModifiers (backend_x11->xdpy,
XK_Scroll_Lock); 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; return keymap_x11->xkb_desc;
} }
@ -469,6 +482,7 @@ static void
clutter_keymap_x11_init (ClutterKeymapX11 *keymap) clutter_keymap_x11_init (ClutterKeymapX11 *keymap)
{ {
keymap->current_direction = PANGO_DIRECTION_NEUTRAL; keymap->current_direction = PANGO_DIRECTION_NEUTRAL;
keymap->current_group = -1;
} }
static ClutterTranslateReturn static ClutterTranslateReturn
@ -498,7 +512,8 @@ clutter_keymap_x11_translate_event (ClutterEventTranslator *translator,
{ {
case XkbStateNotify: case XkbStateNotify:
CLUTTER_NOTE (EVENT, "Updating keyboard state"); 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); update_locked_mods (keymap_x11, xkb_event->state.locked_mods);
retval = CLUTTER_TRANSLATE_REMOVE; retval = CLUTTER_TRANSLATE_REMOVE;
break; break;
@ -665,3 +680,164 @@ _clutter_keymap_x11_get_direction (ClutterKeymapX11 *keymap)
#endif #endif
return PANGO_DIRECTION_NEUTRAL; 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;
}

View File

@ -51,6 +51,14 @@ gboolean _clutter_keymap_x11_get_is_modifier (ClutterKeymapX11 *keymap,
PangoDirection _clutter_keymap_x11_get_direction (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 G_END_DECLS
#endif /* __CLUTTER_KEYMAP_X11_H__ */ #endif /* __CLUTTER_KEYMAP_X11_H__ */

View File

@ -32,6 +32,8 @@
#include "clutter-virtual-input-device.h" #include "clutter-virtual-input-device.h"
#include "x11/clutter-virtual-input-device-x11.h" #include "x11/clutter-virtual-input-device-x11.h"
#include "x11/clutter-backend-x11.h"
#include "x11/clutter-keymap-x11.h"
struct _ClutterVirtualInputDeviceX11 struct _ClutterVirtualInputDeviceX11
{ {
@ -135,11 +137,25 @@ clutter_virtual_input_device_x11_notify_keyval (ClutterVirtualInputDevice *virtu
uint32_t keyval, uint32_t keyval,
ClutterKeyState key_state) 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 (), 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 static void