10

I have the following keyboard (HHKB JP).

image

I'm using Lubuntu 14.04, and I've edited /usr/share/X11/xkb/symbols/us to implement a custom layout.

Now, I want to use the following keys as 5th, 6th, 7th, ..., and 20th level modifiers. (On Windows, I accomplish this with AutoHotkey, and now I can't live without (at least some of) them.)

enter image description here

I have 2 problems.

  1. I don't know what the "names" for some of the non-standard keys on the keyboard (in order to add them to /usr/share/X11/xkb/symbols/us).
  2. I don't know how to add extra modifiers with xkb (I understand xmodmap is deprecated), specially "normal" keys like 9.

My questions are:

  1. How do I figure out the names/codes for the mystery keys (eg. key <AC01>)?
  2. How do I add an arbitrary number of modifier layers to any given key?

Note: perhaps this cannot be accomplished with XKB. I'd be delighted to know of some scripting tool that can implement this.

3
  • 1
    To find the XKB physical key names, you can use xev to find the keycodes then search through the XKB keycodes table. Mar 27, 2016 at 19:12
  • @Gilles That worked, thanks. But now I can't figure out how to extra levels of modifiers... Apr 8, 2016 at 11:50
  • 1
    I doubt that Xkb can offer that many different modifiers. Jul 21, 2017 at 21:58

2 Answers 2

8

XKB layouts are defined in a model called RMLVO: Rules, Model, Layout, Variant, Options. The system XKB database is usually in /usr/share/X11/xkb:

/usr/share/X11/xkb/
├── compat       # ??? dark magic here, avoid
├── geometry     # as in physical, eg for generating layout maps
├── keycodes     # helpful for translating keycodes (from xev) to <FOO>
├── rules        # "evdev" is the important one; *.lst & *.xml are descriptions
├── symbols      # main layouts, variants, optional overrides
└── types        # ??? dark magic here, avoid

The keycodes directory will define keynames like <CAPS> or <AC01> from scancodes that the keys are actually generating. setxkbmap -query -verbose 10 will show you exactly what files are accessed to build your current keymap; when it says keycodes: evdev+aliases(qwerty), it's loading the ../keycodes/evdev file and the qwerty stanza from the ../keycodes/aliases file. If you use tools like xev to determine what keycode a key is generating, those files will show what keyname is mapped to that code.


Your modifier question is tougher. I've only seen reference to 8 ISO levels, and I'm not sure if that's an XKB limitation or simply that no common layouts use more than that. (After examining this further, I'm pretty sure you'd have to alter XKB code to add additional

            symbol    press this
level 1       a       key
level 2       A       shift+key
level 3       á       <level3>+key
level 4       Á       <level3>+shift+key
level 5       ??      <level5>+key
level 6       ??      <level5>+shift+key
level 7       ??      <level5>+<level3>+key
level 8       ??      <level5>+<level3>+shift+key

As you can see, in an 8-level layout there's only 2 keys that act as "new modifier" keys. See /usr/share/X11/xkb/compat/level5 and /usr/share/X11/xkb/types/level5, where level5 shift, latch, and lock behaviors are defined; these are then attached to keys by symbol options defined in /usr/share/X11/xkb/symbols/level5.

If you defined your own level9 modifier, then, combining with existing levels would give you 8 more levels before you'd need another level modifier:

            symbol    press this
level 9       ??      <level9>+key
level 10      ??      <level9>+shift+key
level 11      ??      <level9>+<level3>+key
level 12      ??      <level9>+<level3>+shift+key
level 13      ??      <level9>+<level5>+key
level 14      ??      <level9>+<level5>+shift+key
level 15      ??      <level9>+<level5>+<level3>+key
level 16      ??      <level9>+<level5>+<level3>+shift+key

As you can see, this is starting to be a lot of keys pressed at once. Using locks and latches instead of shifts would cut that down, but this would be a very complicated layout, and we've only imagined adding one level chooser. Each additional chooser would double the existing levels.


The "modifiers" you envision are probably selecting one specific level, which is why you think you need so many. You might be able to define selection actions like that, and put them on, say, level3 of those keys, so level3+key9 gives you a level14_latch action, and then whatever key you press next will give you the 14th level of that key.

Edit: Another question asked specifically about making an ISO_Level4_{Shift,Latch,Lock}, and I demonstrated a fake ISO_Level4_Latch using existing XKB keysyms and actions. That method should be effective for levels 4, 6, 7 and 8.

2
  • I ended up buying a programmable keyboard. XKB sucks! And a large number of modifiers is a must if you want to control applications entirely with a keyboard! Have you seen the GUI of, say, GIMP, or Excel, or AutoCAD? Imagine mapping every single button/action to a key on your keyboard. Then you have all the application's power at your fingertips (literally). In practice, however, you end up limited by 8-bit keycodes... because applications usually can do more than 256 things. (But to this day a mind like mine doesn't need more, because they don't fit it my head.) Jan 5, 2021 at 20:19
  • Remember that a keyboard must be able to emit 256 keycodes by at most 2 key presses (ie. the modifier + the key). (Multiple modifiers (eg. shift+control) are evil.) Modern keyboards can typically emit much fewer. You need to go out of your way to cover the 8-bit spectrum with a keyboard (eg. buy a programmable one with lots of keys, or build one yourself). Jan 5, 2021 at 20:23
1

This is an old question, but since there were no proper answers and I was interested in this problem for my own reasons, I took the time to work this out. Configuring XKB can be tricky, but this can certainly be done.

For the first question, you can generate a picture with all the names of the keys on your currently configured keyboard with this magical command:

setxkbmap -print | xkbcomp - - | xkbprint -color -label name - - | ps2pdf - > keynames.pdf

and view the pdf file keynames.pdf

For the second question, we have to dive deep into XKB configuration. I believe the best source of information is the developers' documentation. The short story is that we need to map keys to combinations of "real modifiers" and we need to map these combinations to levels for the keys.

In the symbols file, we would like to write e. g.

key <AC01>  { [ a, A, b, B, c, C, d, D, e, E, f, F, g, G,
                h, i, j, k, l, m, n, o, p, q, r, s, t, u]}; 

which means that on the button for A, we have the symbol a on the first level, A on the second, b on the third, and so on, and then map keys to the various levels. As this example suggests, however, it would likely make sense to have every other level correspond to pressing a modifier key + a shift key for some modifier keys, and for some keys the same symbol will be produced regardless of whether shift is pressed. This is what we shall accomplish.

XKB uses "real" and "virtual" modifiers to keep track of modifier levels. The virtual ones can have any name, and we can have 16 of those. Each key will however need to correspond to a combination of the "real" modifiers, and since we will be using a large number of keys, we may as well use the real modifiers right away.

What complicates things is that there is a fixed list of "real modifiers", namely the eight Shift, Lock, Control and Mod1 to Mod5. Any combination of these can however also be used, so theoretically 2⁸ = 256 different levels for each key. In practice, however, some of these are already in use in various ways in the OS and in programs, so we shall have to be a little careful when selecting these. There is also a limit in XKB on 63 or 64 levels of symbols for each key, though one can use groups to multiply this number by four. We shall not use groups here.

The modifiers Control and Mod1 = Alt are commonly used by the OS and programs, so we shall not use these. Mod4 = Super is also used, but we can use it together with other modifiers. We first create a type file that will map combinations of modifiers to levels. Write the following in a file called multimod_type and place it in /usr/share/X11/xkb/types.

default partial xkb_types "multimod_type" {

 // We use real modifiers Shift, Mod2, Mod3, Mod4, Mod5.
 // Mod1 = Alt, so we do not use this.
 // In addition, Mod4 = Super is used by the OS, which causes
 // problems in four places. 

type "MULTIMOD" {
modifiers = Shift + Mod2 + Mod3 + Mod4 + Mod5;
map[None] = 1;
map[Shift] = 2;
map[Mod2] = 3;
map[Mod2 + Shift] = 4;
map[Mod3] = 5;
map[Mod3 + Shift] = 6;
map[Mod3 + Mod2] = 7;
map[Mod3 + Mod2 + Shift] = 8;
map[Mod4 + Mod3] = 9;
map[Mod4 + Mod3 + Shift] = 10;
map[Mod4 + Mod3 + Mod2] = 11;
map[Mod4 + Mod3 + Mod2 + Shift] = 12;
map[Mod5] = 13;
map[Mod5 + Shift] = 14;
map[Mod5 + Mod2] = 15;
map[Mod5 + Mod2 + Shift] = 16;
map[Mod5 + Mod3] = 17;
map[Mod5 + Mod3 + Shift] = 18;
map[Mod5 + Mod3 + Mod2] = 19;
map[Mod5 + Mod3 + Mod2 + Shift] = 20;
map[Mod5 + Mod4 + Mod3] = 21;
map[Mod5 + Mod4 + Mod3 + Shift] = 22;
map[Mod5 + Mod4 + Mod3 + Mod2] = 23;
map[Mod5 + Mod4 + Mod3 + Mod2 + Shift] = 24;
// Mod4 = Super is used
map[Mod4 + Shift] = 25;
// Mod4 + Mod2 is used
map[Mod4 + Mod2 + Shift] = 26;
// Mod5 + Mod4 is used
map[Mod5 + Mod4 + Shift] = 27;
// Mod5 + Mod4 + Mod2 is used
map[Mod5 + Mod4 + Mod2 + Shift] = 28;
// Level names are only for description. The values are not used.
level_name[1] = "Base";
level_name[2] = "Shift";
level_name[3] = "Alt1";
level_name[4] = "Alt1 + Shift";
level_name[5] = "Alt2";
level_name[6] = "Alt2 + Shift";
level_name[7] = "Alt3";
level_name[8] = "Alt3 + Shift";
level_name[9] = "Alt4";
level_name[10] = "Alt4 + Shift";
level_name[11] = "Alt5";
level_name[12] = "Alt5 + Shift";
level_name[13] = "Alt6";
level_name[14] = "Alt6 + Shift";
level_name[15] = "Alt7";
level_name[16] = "Alt8";
level_name[17] = "Alt9";
level_name[18] = "Alt10";
level_name[19] = "Alt11";
level_name[20] = "Alt12";
level_name[21] = "Alt13";
level_name[22] = "Alt14";
level_name[23] = "Alt15";
level_name[24] = "Alt16";
level_name[25] = "Alt17";
level_name[26] = "Alt18";
level_name[27] = "Alt19";
level_name[28] = "Alt20";
};
};

As the names suggests, we will have 20 extra modifier buttons, and the first six of these can be used in combination with shift.

To use this, modify this minimal working example of a symbols file. I call this multimod_se. Save it in /usr/share/X11/xkb/symbols as usual.

default partial alphanumeric_keys xkb_symbols "multimod_se" {

// Include your favourite symbols
include "se"

name[Group1]="Swedish with many modifiers on A";

key.type[Group1] = "MULTIMOD";

key <AC01>  { [ a, A, b, B, c, C, d, D, e, E, f, F, g, G,
                h, i, j, k, l, m, n, o, p, q, r, s, t, u]}; 

// ----- Modifiers -----
key.type = "ONE_LEVEL";
key.repeats = False;

// We will map F1-F10 and 1-9 and 0 to the modifier keys
// The keys F1-F6 produce different symbols with shift pressed
key <FK01> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2)]};
key <FK02> { [ Hyper_R ], actions = [SetMods(modifiers=Mod3)]};
key <FK03> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod3)]};
key <FK04> { [ Hyper_R ], actions = [SetMods(modifiers=Mod3+Mod4)]};
key <FK05> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod3+Mod4)]};
key <FK06> { [ Hyper_R ], actions = [SetMods(modifiers=Mod5)]};

// We can bind buttons to include shift in order to get a
// shortcut to a modifier button + shift
key <FK07> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod5)]};
key <FK08> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod5+Shift)]};
key <FK09> { [ Hyper_R ], actions = [SetMods(modifiers=Mod3+Mod5)]};
key <FK10> { [ Hyper_R ], actions = [SetMods(modifiers=Mod3+Mod5+Shift)]};
key <AE01> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod3+Mod5)]};
key <AE02> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod3+Mod5+Shift)]};
key <AE03> { [ Hyper_R ], actions = [SetMods(modifiers=Mod3+Mod4+Mod5)]};
key <AE04> { [ Hyper_R ], actions = [SetMods(modifiers=Mod3+Mod4+Mod5+Shift)]};
key <AE05> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod3+Mod4+Mod5)]};
key <AE06> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod3+Mod4+Mod5+Shift)]};

// The following four must be defined with shift
key <AE07> { [ Hyper_R ], actions = [SetMods(modifiers=Mod4+Shift)]};
key <AE08> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod4+Shift)]};
key <AE09> { [ Hyper_R ], actions = [SetMods(modifiers=Mod4+Mod5+Shift)]};
key <AE10> { [ Hyper_R ], actions = [SetMods(modifiers=Mod2+Mod4+Mod5+Shift)]};
};

This creates a keymap where F1 to F10 and 1 to 9 as well as 0 are extra modifier keys. Pressing these together with the button A will print the letters a-u, and together with shift we also get A-G. Load the symbols together with the type file with the command

setxkbmap -types complete+multimod_type multimod_se

We include the standard type file complete since we use the type ONE_LEVEL for the modifier keys, and some other types in the included symbols file.

Some comments: The standard way to treat modifiers is with rules in a compat file, but this requires a unique symbol, which does not do anything else, for each modifier key. We can come around this by defining the modifier rule directly in the symbols file, as above. As you can see all modifier keys give the symbol Hyper_R.

If you need more modifier keys, you can also include combinations with Mod1, Control and Lock that are not used by the OS or programs to represent something else.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .