Xkb: Assign a new shift level to function keys

keyboard-layoutxkbxorg

I'm trying to get some more use out of my function keys. I would like the physical F1 to F12 keys to appear as F21 to F32, so I can assign functions to F21 etc. in my window manager (e.g. switch to the active chat window whenever I press F22.)

However I still want access to the normal function keys, so I would like to be able to hold the physical Caps Lock key and press F1 to generate an F1 keypress. As far as the other modifiers go, it would be nice if they were left unchanged, so Shift + F1 produces Shift + F1 (not Shift + F21). In summary:

  • F1 -> F21
  • Caps + F1 -> F1
  • Shift + F1 -> Shift + F1 (unchanged)
  • Alt + F1 -> Alt + F1 (unchanged)
  • etc.

So only the first two dot points require changes from the default layout.

I have attempted to start this with the following XKB snippet, and while it produces F21 for F1, it does not produce the original F-keys when Caps Lock is held:

partial
xkb_types "hyper" {
    virtual_modifiers Alt,Hyper;
    type "HYPER" {
        modifiers= Hyper+Control+Alt;
        map[Hyper]= Level2;
        map[Control+Alt]= Level3;
        level_name[Level1]= "Extra";
        level_name[Level2]= "Normal";
        level_name[Level3]= "Normal+Ctrl+Alt";
    };
};

partial function_keys
xkb_symbols "hyper_f21" {

    replace key <FK01> {
        type= "HYPER",
        symbols[Group1]= [           F21,             F1, XF86Switch_VT_1 ]
    };
    replace key <FK02> {
        type= "HYPER",
        symbols[Group1]= [           F22,             F2, XF86Switch_VT_2 ]
    };
    ...
};

partial modifier_keys
xkb_symbols "caps_hyper" {
    replace key <CAPS> {
        [ Hyper_L, Hyper_L ]
    };
    modifier_map Lock { <KPDL> };  # Not sure how to clear modifiers, so assign an unused key
    modifier_map Mod3 { <CAPS> };
};

When I try to load it, I get this warning:

Warning:          Type "HYPER" has 3 levels, but <FK01> has 5 symbols
                  Ignoring extra symbols

I don't really understand why I get the error, because although the default <FK01> has five levels, my redefined one only has three.

When I test out this configuration, sure enough I get F21 when pressing the physical F1 key, and I get Hyper_L (and Mod3) set when pressing Caps Lock. However pressing Caps Lock + F1 produces Mod3 + F21 instead of F1.

Best Answer

As always, figured this one out moments after posting the question. The problem was simply that I was using Hyper_L which by default is assigned to Mod4. By changing Caps Lock to Hyper_R instead, it worked. It still had to be bound to Mod3 though as a real modifier is still needed.

Here's the updated config which now produces the desired behaviour:

partial
xkb_types "hyper" {
    virtual_modifiers Alt,Hyper;

    type "HYPER" {
        modifiers= Shift+Control+Alt+Hyper;

        map[Shift]= Level2;
        preserve[Shift]= Shift;

        map[Control]= Level2;
        preserve[Control]= Control;

        map[Shift+Control]= Level2;
        preserve[Shift+Control]= Shift+Control;

        map[Alt]= Level2;
        preserve[Alt]= Alt;

        map[Shift+Alt]= Level2;
        preserve[Shift+Alt]= Shift+Alt;

        map[Control+Alt]= Level3;
        preserve[Control+Alt]= Control+Alt;

        map[Shift+Control+Alt]= Level2;
        preserve[Shift+Control+Alt]= Shift+Alt;

        map[Hyper]= Level2;

        level_name[Level1]= "Extra";
        level_name[Level2]= "Normal";
        level_name[Level3]= "Ctrl+Alt";
    };
};


partial function_keys
xkb_symbols "hyper_f21" {

    replace key <FK01> {
        type= "HYPER",
        symbols[Group1]= [           F21,             F1, XF86Switch_VT_1 ]
    };
    replace key <FK02> {
        type= "HYPER",
        symbols[Group1]= [           F22,             F2, XF86Switch_VT_2 ]
    };
    ...
};

partial modifier_keys
xkb_symbols "caps_hyper" {
    replace key <CAPS> {
        [ Hyper_R ]
    };
    # Remove Hyper (Hyper_L/Hyper_R) from Mod4, was added by "pc" layout
    modifier_map none { <HYPR> };
    # Now make physical caps lock (mapped to Hyper_R above) control Mod3.  Mod3 is
    # associated with the virtual modifier "Hyper" because the existing XKB config
    # links Hyper_L and Hyper_R with this virtual modifier.  Therefore Mod3 becomes
    # the virtual modifier "Hyper" because they both share the same keysym Hyper_R.
    modifier_map Mod3 { <CAPS> };
};

Again, this does:

  • F1 -> F21
  • Caps + F1 -> F1
  • Shift + F1 -> Shift + F1 (unchanged)
  • Alt + F1 -> Alt + F1 (unchanged)
  • etc. - all others unchanged