Commit 9e5052259a0f5d3655cff1c6aa36cc303c866e47

Ran Benita 2012-07-12T19:28:52

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>

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> };