You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
317 lines
10 KiB
317 lines
10 KiB
From 8dfcfa0607754caab5032532ccc9d97b4393708e Mon Sep 17 00:00:00 2001 |
|
From: Carlos Garnacho <carlosg@gnome.org> |
|
Date: Fri, 29 Jun 2018 14:31:23 +0200 |
|
Subject: [PATCH] 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 |
|
|
|
(cherry picked from commit 85284acb000ddc70afcf716b6c198b4b5bf5741e) |
|
--- |
|
clutter/clutter/x11/clutter-keymap-x11.c | 178 +++++++++++++++++- |
|
clutter/clutter/x11/clutter-keymap-x11.h | 8 + |
|
.../x11/clutter-virtual-input-device-x11.c | 22 ++- |
|
3 files changed, 204 insertions(+), 4 deletions(-) |
|
|
|
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 |
|
-- |
|
2.19.0.rc0 |
|
|
|
|