symbols: fix bug in modifier_map handling The code used to match a keysym to a keycode (see added comment) differed in behavior from xkbcomp, always taking the first key it found. This caused some incorrect interpretation of the xkeyboard-config data, for example the one corrected in dump.data (see the diff): since the de-neo layout sets the both_capslock option, the Left Shift key (LFSH) has the Caps_Lock keysym in group 4 level 2; now since keycode(Left Shift) = 50 < keycode(Caps Lock) = 64 the Left Shift one was picked, instead of the Caps Lock one which is group 1 level 1. The correct behavior is to pick according to group, level, keycode. Signed-off-by: Ran Benita <ran234@gmail.com>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c
index 8de5cc7..41957f1 100644
--- a/src/xkbcomp/symbols.c
+++ b/src/xkbcomp/symbols.c
@@ -24,6 +24,8 @@
********************************************************/
+#include <limits.h>
+
#include "xkbcomp-priv.h"
#include "parseutils.h"
#include "action.h"
@@ -1557,12 +1559,23 @@ HandleSymbolsFile(XkbFile *file, struct xkb_keymap *keymap,
}
}
+/**
+ * Given a keysym @sym, find the keycode which generates it
+ * (returned in @kc_rtrn). This is used for example in a modifier
+ * map definition, such as:
+ * modifier_map Lock { Caps_Lock };
+ * where we want to add the Lock modifier to the modmap of the key
+ * which matches the keysym Caps_Lock.
+ * Since there can be many keys which generates the keysym, the key
+ * is chosen first by lowest group in which the keysym appears, than
+ * by lowest level and than by lowest key code.
+ */
static bool
FindKeyForSymbol(struct xkb_keymap *keymap, xkb_keysym_t sym,
xkb_keycode_t *kc_rtrn)
{
xkb_keycode_t key;
- unsigned int group, level;
+ unsigned int group, level, min_group = UINT_MAX, min_level = UINT_MAX;
for (key = keymap->min_key_code; key <= keymap->max_key_code; key++)
{
@@ -1574,13 +1587,27 @@ FindKeyForSymbol(struct xkb_keymap *keymap, xkb_keysym_t sym,
if (XkbKeyNumSyms(keymap, key, group, level) != 1 ||
(XkbKeySymEntry(keymap, key, group, level))[0] != sym)
continue;
- *kc_rtrn = key;
- return true;
+
+ /*
+ * If the keysym was found in a group or level > 0, we must
+ * keep looking since we might find a key in which the keysym
+ * is in a lower group or level.
+ */
+ if (group < min_group ||
+ (group == min_group && level < min_level)) {
+ *kc_rtrn = key;
+ if (group == 0 && level == 0) {
+ return true;
+ } else {
+ min_group = group;
+ min_level = level;
+ }
+ }
}
}
}
- return false;
+ return min_group != UINT_MAX;
}
/**
diff --git a/test/data/keymaps/dump.data b/test/data/keymaps/dump.data
index 5ff80c1..13d04cc 100644
--- a/test/data/keymaps/dump.data
+++ b/test/data/keymaps/dump.data
@@ -1878,10 +1878,10 @@ xkb_keymap {
key <I246> { [ XF86WLAN ] };
modifier_map Control { <LCTL> };
modifier_map Shift { <LFSH> };
- modifier_map Lock { <LFSH> };
modifier_map Mod5 { <BKSL> };
modifier_map Shift { <RTSH> };
modifier_map Mod1 { <LALT> };
+ modifier_map Lock { <CAPS> };
modifier_map Mod2 { <NMLK> };
modifier_map Mod5 { <LVL3> };
modifier_map Mod3 { <LSGT> };