August 23rd, 2011

nepal

A way of a key, or how I achieved success configuring multimedia keys on my keyboard

When I bought a new keyboard last week and those multimedia/internet keys were not working as expected, I learned a bit too much about how a key press event goes all the way from the hardware to the Linux kernel to X11. Actually, much more than I even wanted to know. I do not believe in humanity any more. Well, time will definitely heal me, but let me share the information I found before I forgot it for good.

My distro is Fedora 14, kernel 2.6.35, Xorg-1.9.5, xkeyboard-config 1.9. Desktop environment is XFCE 4.6. My new keyboard is Genius SlimStar 320. I use quite an old Logitech Trackball ("Cordless TrackMan FX, T-RA17) as a primary way to control on-screen pointer. This trackball was produced before mouse scroll wheels became a commodity so it lacks one. Fortunately the keyboard is equipped with a four-way " turbo scroll" button which in reality is just a circular four-key button.

Here comes the first problem: keyboard sends very unusual scancodes for most of the multimedia keys. For example, the "turbo scroll" sends codes 495 to 498. Most of the other multimedia keys send similar codes for example 499 (for "Presentation" key) or 506 (for "Yahoo Messenger" key). You can see those using 'showkey' utility from the 'kdb' package (make sure to run in from a text terminal, i.e. not from X Window).

Fortunately, every problem in computing can be solved by introducing an additional abstraction level (unfortunately, the problem of too many abstraction levels can't be solved using this method -- but this is out of scope of this blog entry). Now, the kernel already maps raw scan codes from the keyboard to so called key codes. We need to modify this table to map the multimedia key scancodes to more appropriate (more standard) key codes. As usual in UNIX systems, there are many ways to achieve that, but it looks like the preferred way of the day is to use udev 'keymaps' extension. Whenever udev sees a particular keyboard model (identified by USB vendor_id and product_id numbers) appearing in the system, it can apply a particular map for this keyboard. For key code names used in udev keymaps, see /usr/include/linux/input.h (look for #define KEY_* macros). To make a long story short for a busy reader, here (http://kan.gd/x9q) Is my patch to udev which fixes the bad key codes for my keyboard.

After using this one, some keys (like 'screenlock') started to work in XFCE, while some others were still dead. Well, not completely dead -- you can see the new codes using showkey, or, yet better, evtest utility. Next problem lies in the next level of abstraction called xkb (X Keyboard Extension).

First of all, due to someone thinking there will never be more than 256 keys, a keycode is one-byte, so values above 255 are not supported, X Window just can not see those. OK, this can be kludged by using codes in the range 1-255 in the above (udev keymap) step. But you still can see those that are within the one byte range -- use evdev utility for that. XKB translates the keycode into internal key name (such as I24 or K6C -- such translation is (mostly) defined in XKB's /usr/share/X11/xkb/keycodes/xfree86 file) and then into X11 key event name (which, for a multimedia key, is as cool as XF86AudioRaiseVolume or XF86ScrollUp -- such translation is (mostly) defined in XKB's /usr/share/X11/xkb/keymap/inet).

Now, you can assume that if you see keypress events with such descriptive key names you are all set. Hold on...

Next thing is to make your system react to those keys. In XFCE, go to Preferences -> Keyboard and select the Application Shortcuts tab. You will see quite a few key bindings already set for you, for example XF86WWW opens a web browser. While at it, you can learn some handy key combos for you.

Please note that if you are going to use keys to control volume, XFCE configures aumix for XF86AudioRaiseVolume and ...LowerVolume. First, aumix is not installed, but even if installed it can't deal with the default PulseAudio mixer configured in Fedora 14. The fix is to use amixer instead, here are the relevant commands (you can try if it works for you from the command line and adjust accordingly):

Raise: amixer -q set Master 2%+ unmute
Lower: amixer q set Master 2% unmute
Mute: amixer -q set Master toggle

Next thing is, in order for scrolling (XF86ScrollUp and ...Down) to work, you need something very kludgy -- transform keyboard events into mouse events. Again, there is more one way of doing it; I used the xte tool (from the package of the same name). Here are the relevant rules:

XF86ScrollUp: xte 'mouseclick 4'
XF86ScrollDown: xte 'mouseclick 5'

If you are not an XFCE user, you can use xbindkeys for the same purpose.