symbols: fix bug in modifier_map handling
authorRan Benita <ran234@gmail.com>
Thu, 12 Jul 2012 16:28:52 +0000 (19:28 +0300)
committerDaniel Stone <daniel@fooishbar.org>
Thu, 12 Jul 2012 17:08:00 +0000 (18:08 +0100)
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>
src/xkbcomp/symbols.c
test/data/keymaps/dump.data

index 8de5cc7..41957f1 100644 (file)
@@ -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;
 }
 
 /**
index 5ff80c1..13d04cc 100644 (file)
@@ -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> };