How to make ISO_Level5_Shift work

xkb

I wanted to add more levels to my keyboard layouts, so I decided to enable level5 key. I’ve already had level3 on Menu, and would like to map RAlt to ISO_Level5_Shift.

However, setxkbmap has no option for that. /usr/share/X11/xkb/rules/evdev.lst contains only the following options in regard to lv5 switch:

lv5:lsgt_switch_lock
lv5:ralt_switch_lock
lv5:lwin_switch_lock
lv5:rwin_switch_lock

As if mapping a simple key is not possible. This already hinted on troubles.
Knowing what setxkbmap does internally, it’s easy to construct an option to enable lv5 switch and set to right Alt:

-option "lv5:ralt_switch"

And, despite that setxkbmap takes this option and returns with success, there’s no mapping.


After finding an answer to a similar question here I’ve constructed a custom xkbcomp file.

xkb_keymap {

    xkb_geometry {
        include "pc(pc104)"
    };


    xkb_keycodes {
        include "evdev"
        include "aliases(qwerty)"
    };


    xkb_types {
        include "complete"
    };


    xkb_compatibility {
        include "complete"
        include "ledscroll(group_lock)"
    };


    xkb_symbols {
        include "pc+us+ru:2+inet(evdev)"
        include "group(rctrl_rshift_toggle)"
        include "ctrl(swapcaps)"
        include "keypad(oss)"
        include "kpdl(dot)"
        include "terminate(ctrl_alt_bksp)"
        include "level3(modifier_mapping)"
        include "level3(menu_switch)"
        include "level5(modifier_mapping)"
        include "level5(ralt_switch)"

        key <AB06> {
            type= "EIGHT_LEVEL",
            symbols[Group1]= [               n,               N, n, n, u, i, o, p ],
            symbols[Group2]= [     Cyrillic_te,     Cyrillic_TE, n, n, u, i, o, p ]
        };

    };


};

This should assign Menu key to be ISO_Level3_Shift and RAlt to be ISO_Level5_Shift. And the keys are assigned, but there is an error:

$ xkbcomp  ~/new.xkbcomp.xkb :0
Error:            Key <MDSW> added to map for multiple modifiers
                  Using Mod3, ignoring Mod5.

xkbcomp returns with code 0, despite that there was an error.

The mapping on key N should have made levels 5 to 8 “u”, “i”, “o” and “p”, but instead RAlt sends the usual n and N on all eight levels (if levels >4 actually work).

In attempt to determine, how to solve this error, I’ve found, that <MDSW> is set in… the very rules file for “pc”.

/usr/share/X11/xkb/symbols/pc:

xkb_symbols "pc105" {
    …
    // Fake keys for virtual<->real modifiers mapping:
    key <LVL3> {    [ ISO_Level3_Shift  ]   };
    key <MDSW> {    [ Mode_switch       ]   };
    modifier_map Mod5   { <LVL3>, <MDSW> };
    …
};

And here is another weird thing: I include pc104 keyboard model, but this code refers to pc105 and shouldn’t be sourced at all. However… xkb_symbols "pc104" is nowhere to be found. And this “symbols/pc” file is the only other file in /usr/share/X11/xkb/ that sets or modifies <MDSW> assignment to Mod* keys. The other file being “symbols/level5” file, which does it in xkb_symbols "modifier_mapping".

// Ensure a mapping to a real modifier for LevelFive.
partial modifier_keys
xkb_symbols "modifier_mapping" {
  replace key <MDSW> {
    type[Group1] = "ONE_LEVEL",
    symbols[Group1] = [ ISO_Level5_Shift ]
  };
  modifier_map Mod3 { <MDSW> };
};

I think, that this conflict may prevent both lv3 switch and lv5 switch to work at the same time, but I’m not sure. It’s concerning, that in another answer, Mode_switch key is present among Mod* keys, and after loading my new.xkbcomp.xkb I don’t see it anywhere:

$  xmodmap -pm
xmodmap:  up to 4 keys per modifier, (keycodes in parentheses):

shift       Shift_L (0x32),  Shift_R (0x3e)
lock        Caps_Lock (0x25)
control     Control_L (0x42),  Control_R (0x69)
mod1        Alt_L (0x40),  Meta_L (0xcd)
mod2        Num_Lock (0x4d)
mod3        ISO_Level5_Shift (0xcb)
mod4        Super_L (0x85),  Super_R (0x86),  Super_L (0xce),  Hyper_L (0xcf)
mod5        ISO_Level3_Shift (0x5c)

Best Answer

Here is my working solution for putting ISO_Level5_Shift on Control_R. You would need to apply the same logic.

  • Run xev to find the keycode of the key you want to associate with ISO_Level5_Shift. Move the mouse cursor over the little scare in the window, press a key to see its keycode in the output. In my case the code was 105.
  • Second, I ran xmodmap. I saw that Control_R was associated with mod4.
  • So I did xmodmap -e "clear mod4 to free the key. Note that it frees all keys so you may need to associate the other keys back if you need them.
  • Then xmodmap -e "keycode 105 = ISO_Level5_Shift" to associate Control_R with the modifier.
  • And finally, xmodmap -e "add mod3 = ISO_Level5_Shift" to enable level 5.
Related Question