clutter/x11: Implement keycode remap to keysyms on virtual key devices
Keycode lookup can fail for serveral reasons, e.g. if there is no combination of modifiers and keycodes that can produce the target keysym with the current keyboard layout. In case the keycode lookup fails, remap temporarily the keysym to an unused keycodes. Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/109
This commit is contained in:
parent
fda2e798bb
commit
e3e933c47a
@ -76,6 +76,9 @@ struct _ClutterKeymapX11
|
||||
DirectionCacheEntry group_direction_cache[4];
|
||||
int current_group;
|
||||
|
||||
GHashTable *reserved_keycodes;
|
||||
GQueue *available_keycodes;
|
||||
|
||||
guint caps_lock_state : 1;
|
||||
guint num_lock_state : 1;
|
||||
guint has_direction : 1;
|
||||
@ -422,15 +425,97 @@ clutter_keymap_x11_set_property (GObject *gobject,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_keymap_x11_refresh_reserved_keycodes (ClutterKeymapX11 *keymap_x11)
|
||||
{
|
||||
Display *dpy = clutter_x11_get_default_display ();
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
g_hash_table_iter_init (&iter, keymap_x11->reserved_keycodes);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
guint reserved_keycode = GPOINTER_TO_UINT (key);
|
||||
guint reserved_keysym = GPOINTER_TO_UINT (value);
|
||||
guint actual_keysym = XkbKeycodeToKeysym (dpy, reserved_keycode, 0, 0);
|
||||
|
||||
/* If an available keycode is no longer mapped to the stored keysym, then
|
||||
* the keycode should not be considered available anymore and should be
|
||||
* removed both from the list of available and reserved keycodes.
|
||||
*/
|
||||
if (reserved_keysym != actual_keysym)
|
||||
{
|
||||
g_hash_table_iter_remove (&iter);
|
||||
g_queue_remove (keymap_x11->available_keycodes, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_keymap_x11_replace_keycode (ClutterKeymapX11 *keymap_x11,
|
||||
KeyCode keycode,
|
||||
KeySym keysym)
|
||||
{
|
||||
if (CLUTTER_BACKEND_X11 (keymap_x11->backend)->use_xkb)
|
||||
{
|
||||
Display *dpy = clutter_x11_get_default_display ();
|
||||
XkbDescPtr xkb = get_xkb (keymap_x11);
|
||||
XkbMapChangesRec changes;
|
||||
|
||||
XFlush (dpy);
|
||||
|
||||
xkb->device_spec = XkbUseCoreKbd;
|
||||
memset (&changes, 0, sizeof(changes));
|
||||
|
||||
if (keysym != NoSymbol)
|
||||
{
|
||||
int types[XkbNumKbdGroups] = { XkbOneLevelIndex };
|
||||
XkbChangeTypesOfKey (xkb, keycode, 1, XkbGroup1Mask, types, &changes);
|
||||
XkbKeySymEntry (xkb, keycode, 0, 0) = keysym;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reset to NoSymbol */
|
||||
XkbChangeTypesOfKey (xkb, keycode, 0, XkbGroup1Mask, NULL, &changes);
|
||||
}
|
||||
|
||||
changes.changed = XkbKeySymsMask | XkbKeyTypesMask;
|
||||
changes.first_key_sym = keycode;
|
||||
changes.num_key_syms = 1;
|
||||
changes.first_type = 0;
|
||||
changes.num_types = xkb->map->num_types;
|
||||
XkbChangeMap (dpy, xkb, &changes);
|
||||
|
||||
XFlush (dpy);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_keymap_x11_finalize (GObject *gobject)
|
||||
{
|
||||
ClutterKeymapX11 *keymap;
|
||||
ClutterEventTranslator *translator;
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
keymap = CLUTTER_KEYMAP_X11 (gobject);
|
||||
translator = CLUTTER_EVENT_TRANSLATOR (keymap);
|
||||
|
||||
clutter_keymap_x11_refresh_reserved_keycodes (keymap);
|
||||
g_hash_table_iter_init (&iter, keymap->reserved_keycodes);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
guint keycode = GPOINTER_TO_UINT (key);
|
||||
clutter_keymap_x11_replace_keycode (keymap, keycode, NoSymbol);
|
||||
}
|
||||
|
||||
g_hash_table_destroy (keymap->reserved_keycodes);
|
||||
g_queue_free (keymap->available_keycodes);
|
||||
|
||||
_clutter_backend_remove_event_translator (keymap->backend, translator);
|
||||
|
||||
if (keymap->xkb_desc != NULL)
|
||||
@ -462,6 +547,8 @@ clutter_keymap_x11_init (ClutterKeymapX11 *keymap)
|
||||
{
|
||||
keymap->current_direction = PANGO_DIRECTION_NEUTRAL;
|
||||
keymap->current_group = -1;
|
||||
keymap->reserved_keycodes = g_hash_table_new (NULL, NULL);
|
||||
keymap->available_keycodes = g_queue_new ();
|
||||
}
|
||||
|
||||
static ClutterTranslateReturn
|
||||
@ -731,6 +818,72 @@ clutter_keymap_x11_get_entries_for_keyval (ClutterKeymapX11 *keymap_x11,
|
||||
}
|
||||
}
|
||||
|
||||
static guint
|
||||
clutter_keymap_x11_get_available_keycode (ClutterKeymapX11 *keymap_x11)
|
||||
{
|
||||
if (CLUTTER_BACKEND_X11 (keymap_x11->backend)->use_xkb)
|
||||
{
|
||||
clutter_keymap_x11_refresh_reserved_keycodes (keymap_x11);
|
||||
|
||||
if (g_hash_table_size (keymap_x11->reserved_keycodes) < 5)
|
||||
{
|
||||
Display *dpy = clutter_x11_get_default_display ();
|
||||
XkbDescPtr xkb = get_xkb (keymap_x11);
|
||||
guint i;
|
||||
|
||||
for (i = xkb->max_key_code; i >= xkb->min_key_code; --i)
|
||||
{
|
||||
if (XkbKeycodeToKeysym (dpy, i, 0, 0) == NoSymbol)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return GPOINTER_TO_UINT (g_queue_pop_head (keymap_x11->available_keycodes));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gboolean clutter_keymap_x11_reserve_keycode (ClutterKeymapX11 *keymap_x11,
|
||||
guint keyval,
|
||||
guint *keycode_out)
|
||||
{
|
||||
g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap_x11), FALSE);
|
||||
g_return_val_if_fail (keyval != 0, FALSE);
|
||||
g_return_val_if_fail (keycode_out != NULL, FALSE);
|
||||
|
||||
*keycode_out = clutter_keymap_x11_get_available_keycode (keymap_x11);
|
||||
|
||||
if (*keycode_out == NoSymbol)
|
||||
{
|
||||
g_warning ("Cannot reserve a keycode for keyval %d: no available keycode", keyval);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!clutter_keymap_x11_replace_keycode (keymap_x11, *keycode_out, keyval))
|
||||
{
|
||||
g_warning ("Failed to remap keycode %d to keyval %d", *keycode_out, keyval);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_hash_table_insert (keymap_x11->reserved_keycodes, GUINT_TO_POINTER (*keycode_out), GUINT_TO_POINTER (keyval));
|
||||
g_queue_remove (keymap_x11->available_keycodes, GUINT_TO_POINTER (*keycode_out));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void clutter_keymap_x11_release_keycode_if_needed (ClutterKeymapX11 *keymap_x11,
|
||||
guint keycode)
|
||||
{
|
||||
g_return_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap_x11));
|
||||
|
||||
if (!g_hash_table_contains (keymap_x11->reserved_keycodes, GUINT_TO_POINTER (keycode)) ||
|
||||
g_queue_index (keymap_x11->available_keycodes, GUINT_TO_POINTER (keycode)) != -1)
|
||||
return;
|
||||
|
||||
g_queue_push_tail (keymap_x11->available_keycodes, GUINT_TO_POINTER (keycode));
|
||||
}
|
||||
|
||||
void
|
||||
clutter_keymap_x11_latch_modifiers (ClutterKeymapX11 *keymap_x11,
|
||||
uint32_t level,
|
||||
|
@ -58,7 +58,11 @@ gboolean clutter_keymap_x11_keycode_for_keyval (ClutterKeymapX11 *keymap_x11,
|
||||
void clutter_keymap_x11_latch_modifiers (ClutterKeymapX11 *keymap_x11,
|
||||
uint32_t level,
|
||||
gboolean enable);
|
||||
|
||||
gboolean clutter_keymap_x11_reserve_keycode (ClutterKeymapX11 *keymap_x11,
|
||||
guint keyval,
|
||||
guint *keycode_out);
|
||||
void clutter_keymap_x11_release_keycode_if_needed (ClutterKeymapX11 *keymap_x11,
|
||||
guint keycode);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CLUTTER_KEYMAP_X11_H__ */
|
||||
|
@ -141,8 +141,13 @@ clutter_virtual_input_device_x11_notify_keyval (ClutterVirtualInputDevice *virtu
|
||||
|
||||
if (!clutter_keymap_x11_keycode_for_keyval (keymap, keyval, &keycode, &level))
|
||||
{
|
||||
g_warning ("No keycode found for keyval %x in current group", keyval);
|
||||
return;
|
||||
level = 0;
|
||||
|
||||
if (!clutter_keymap_x11_reserve_keycode (keymap, keyval, &keycode))
|
||||
{
|
||||
g_warning ("No keycode found for keyval %x in current group", keyval);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_clutter_keymap_x11_get_is_modifier (keymap, keycode) &&
|
||||
@ -153,9 +158,13 @@ clutter_virtual_input_device_x11_notify_keyval (ClutterVirtualInputDevice *virtu
|
||||
(KeyCode) keycode,
|
||||
key_state == CLUTTER_KEY_STATE_PRESSED, 0);
|
||||
|
||||
if (!_clutter_keymap_x11_get_is_modifier (keymap, keycode) &&
|
||||
key_state == CLUTTER_KEY_STATE_RELEASED)
|
||||
clutter_keymap_x11_latch_modifiers (keymap, level, FALSE);
|
||||
|
||||
if (key_state == CLUTTER_KEY_STATE_RELEASED)
|
||||
{
|
||||
if (!_clutter_keymap_x11_get_is_modifier (keymap, keycode))
|
||||
clutter_keymap_x11_latch_modifiers (keymap, level, FALSE);
|
||||
clutter_keymap_x11_release_keycode_if_needed (keymap, keycode);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user