♪♫ I've got the key // to the highway


There's no such thing as too many frobbin' f-keys.

In a hopefully useful contribution, let's look at making extra keyboards (pictured to the left) do something useful under X.
The following is cutting all the arcane (and often ever-changing) corners to give results.

Step 1: Finding out what your keyboard is called.

This is most easy to do by unplugging it, running xinput -list, replugging it, running xinput -list again and seeing what's new.

Example output (I think my encoding needs fixing...):

[0 1065 20:04:14] ~ % xinput -list
â¡ Virtual core pointer                        id=2    [master pointer  (3)]
â   â³ Virtual core XTEST pointer                    id=4    [slave  pointer  (2)]
â   â³ Microsoft Microsoft Trackball Optical®       id=8    [slave  pointer  (2)]
⣠Virtual core keyboard                       id=3    [master keyboard (2)]
    â³ Virtual core XTEST keyboard                     id=5    [slave  keyboard (3)]
    â³ Power Button                                    id=6    [slave  keyboard (3)]
    â³ Power Button                                    id=7    [slave  keyboard (3)]
    â³   USB Keyboard                                  id=9    [slave  keyboard (3)]
    â³   USB Keyboard                                  id=10   [slave  keyboard (3)]
    â³ AT Translated Set 2 keyboard                    id=11   [slave  keyboard (3)]

In my case, the keyboard shows up as two entries, ids 9 and 10. I have no idea why, probably because it's the cheapest model I could find, so cheap the Scroll Lock key actually reads "Scroll Look". We'll need the id, kinda, but in the end we'll use the name before that to find it, as my understanding is that the id can change from boot to boot, or if you unplug and replug stuff.

But first, we need to find out the keycodes of the keypad. This is the lowest level in a multistep process, a keycode is just a meaningless number. The xev program will show it, amongst much other stuff:

[130 1074 20:16:15] ~ % xev | grep -i key
[...]
KeyPress event, serial 34, synthetic NO, window 0x2400001,
state 0x10, keycode 90 (keysym 0x1008ff65, XF86MenuKB), same_screen YES,
KeyRelease event, serial 35, synthetic NO, window 0x2400001,
state 0x10, keycode 90 (keysym 0x1008ff65, XF86MenuKB), same_screen YES,

This was me, pressing the 0 key on the keypad. We can see that the keycode for this key is 90. (The keysym shown afterwards shows I have this key already mapped to a functionesque key. For the "a" key, it would read (keysym 0x61, a)

Now go through all your keypad keys and jot down the keycodes they generate.

Step 2: Finding unused keysyms you can use.

We want to keep things simple, so we won't define a whole new keyboard layout that consists of your keypad. (If you want to do that, you might want to read Doug Palmer's Unreliable Guide to XKB configuration. It's a decade old, but its advice is still good. What has changed is where you tell X the layout of your keyboard, since we've gone from settings in xorg.conf to autodetection and keyboard settings via HAL to deprecating HAL and doing it with udev since then, and KDE and GNOME might have another layer above that. I'll show you a dirty hack that works below.) Instead, we'll exploit that many modern crappy keyboards have "internet keys" and "media keys", and we'll pretend your 16 or so keypad keys are 16 of those.

So, how do we find out what "media keys" are recognized? We first check what keyboard configuration we have:

[0 1083 20:30:44] ~ % setxkbmap -query
rules:      evdev
model:      evdev
layout:     de(nodeadkeys)
options:    terminate:ctrl_alt_bksp

So I've got a model detected via evdev, with German layout. For us, the interesting bit is the first two lines. The configuration is done via the rules for evdev, so you should find a file evdev in /usr/share/X11/xkb/rules. Within this file, we look for the model name, evdev, and find a bit that reads

! model         =       keycodes
pc98          =       evdev(pc98)
*             =       evdev

by chance. The * = is a wildcard that tells us any keyboard not explicitly another model has keycodes going by the name of evdev (inventive bunch, eh?). You can skim through the rest of the file, it tells you what happens for each model, layout and variant.

But since there's nothing evdevish there, we conclude that the keycodes for model evdev are the set called evdev. Now, we want to look at how these codes map to symbols. Looking into /usr/share/X11/xkb/symbols.dir, we find

--p----- a------- inet(evdev)

which tells us that evdev is a partial map that comes from symbols/inet. Looking into /usr/share/X11/xkb/symbols/inet, we find:

// Evdev Standardized Keycodes
partial alphanumeric_keys
xkb_symbols "evdev" {
key <MUTE>   {      [ XF86AudioMute         ]       };
key <VOL->   {      [ XF86AudioLowerVolume  ]       };
[...]

Which tells us the key "Mute" should generate the KeySym XF86AudioMute when pressed, regardless of shift key or other modifiers. (Then, there would be more than one entry in the braces, like

key <I172>   {      [ XF86AudioPlay, XF86AudioPause ] };

Now, find however many < keys > you need, probably from the one-function variety, and make sure the functions on the right are unique. (XF86AudioPlay occurs at least three times.)

Step 3: Mapping the keycodes to the keys.

Now, we can configure our keypad. We tell the system it's a funny keyboard, so when it sends keycode 90, it really means <I147>, which is XF86MenuKB under the evdev rules in symbols/inet. To this end, we create a new file logilink in /usr/share/X11/xkb/keycodes, that looks like this:

default xkb&#95;keycodes "logolinkpad" {

minimum= 8;
maximum= 255;

// kp 0 decimal
<I147> =  90;
<I148> =  91;
// kp 123
<I151> =  87;
//...
};

Ignoring the boilerplate, it can be read as "<I147> generated by keycode 90, <I148> generated by keycode 91" and so on.

This file needs to be compiled, so we call xkbcomp -xkb logilink, which creates a file logilink.xkb for us. To install this file, call setxkbmap with the device number from teh very beginning:

bash-4.1# setxkbmap -v -v -device 9 -keycodes logilink
Warning! Multiple definitions of keycodes
         Using command line, ignoring rules file
Applied rules from evdev:
rules:      evdev
model:      evdev
layout:     de(nodeadkeys)
options:    terminate:ctrl_alt_bksp
Trying to build keymap using the following components:
keycodes:   logilink
types:      complete
compat:     complete
symbols:    pc+de(nodeadkeys)+inet(evdev)+terminate(ctrl_alt_bksp)
geometry:   pc(pc104)

This looks good (don't bother about the warning): it's taking the keycodes from the logilink file, and symbols from inet(evdev), so it'll recognize that maps to XF86MenuKB. Run xev again to verify that you now get the keysyms you expect.

Congratulations, you can now configure your desktop environment, or your editor, or whatever, to use your keys. (It might already have been preconfigured to do something, like starting your browser, manipulating audio volume, ejecting CDs, depending on what keysyms you mapped to.)

Step 4: Doing this every time.

So far, these settings do not persist between boots, so you'd have to run the setxkb line every time. (At least, you don't need to be root to do that.) There is probably a good way to do that, which involves manipulating HAL or udev into doing something which will then tell X that that keyboard uses funny scancodes. But they'll replace udev with something else as soon as you have figured it out, so why not take the dirty road? Stick this into your ~/.xsession:

 for d in `xinput -list | sed -ne 's/.*  USB Keyboard.*id=([0-9]*).*keyboard.*/1/p'`; do  setxkbmap -device "$d" -keycodes logilink; done

What this does is: from all lines of xinput -list that match " USB Keyboard", get the id number, and run setxkbmap for each of those. Of course, you'll need to adapt the " USB Keyboard" bit. .xsession should be run each time you logon as your user, if not, find something in your desktop environment that works like that.

Congratulations, many f words later, you now have more f keys. You can now think about being fancy and putting proper labels on those keys. ;)