Shell Person Help me keep the shell people alive.

5Jul/101

Keyboard Hotkeys with Debian Lenny

Here are some notes on assigning functions to those special keys that come on so many keyboards. My Gateway SK-9920 has 12 of these hotkeys: volume up, down, and mute; play, stop, next, and last track; internet, help, mail, shopping cart (?), and back (presumably). I don't even know what the creators of this keyboard envisioned for the "shopping cart" key. Often these keys aren't recognized out-of-the-box in Windows, much less in Linux.

Like usual, there are almost certainly specific GNOME or KDE tools to accomplish this goal. Since I'm not running those, however, I'll stick to tools available in almost any X.Org environment (I happen to be using Ratpoison).

First, some terminology. A scancode is the lowest level of identification for a key. If your key doesn't have a scancode, you're out of luck - the kernel doesn't recognize the key at all. A keycode is the next level of identification. If your key's scancode is identified with a keycode, then there's just one more step to make it do what you want. The last level of identification is a symbol. A scancode is mapped to a keycode, which is mapped to a symbol (for example, the letter "e" or "Caps Lock").

Keycodes

Starting out, none of my hotkeys were mapped to symbols, some were mapped to keycodes, and some only had scancodes (thankfully there were no completely "dead" keys). You can check if a key has a keycode by using xbindkeys. In a terminal (in X - not a console) type

xbindkeys -k

followed by the key you want to check. Here's an example of the output from my "Back" hotkey (before I bound it using xbindkeys, but after I assigned a keycode to the scancode, as described at the end of this post):

james@tv:~$ xbindkeys -k
Press combination of keys or/and click under the window.
You can use one of the two lines after "NoCommand"
in $HOME/.xbindkeysrc to bind a key.
"(Scheme function)"
    m:0x10 + c:211
    Mod2 + NoSymbol

Binding keycodes to actions

Using the output from xbindkeys -k, it's easy to bind a key to an action. This will be configured in the ~/.xbindkeysrc file (you may need to create it). It will take effect when you run xbindkeys. You'll probably want to call it in your ~/.xinitrc file. Here's an example of the format from my .xbindkeysrc:

# internet key
"firefox"
  m:0x10 + c:178

# play/pause key
"mpc toggle"
  m:0x10 + c:162

# stop key
"mpc stop"
  m:0x10 + c:164

# previous track key
"mpc prev"
  m:0x10 + c:144

# next track key
"mpc next"
  m:0x10 + c:153

# volume up key
"amixer set 'Headphone' 1%+ ; amixer set 'PCM' 1%+"
  m:0x10 + c:176

# volume down key
"amixer set 'Headphone' 1%- ; amixer set 'PCM' 1%-"
  m:0x10 + c:174

# mute key
"amixer set 'Headphone' toggle ; amixer set 'PCM' toggle"
  m:0x10 + c:160

It's a pretty simple format. Obviously the "#" precedes a comment (and is optional, but recommended). The next line is the command you want the hotkey to run (surrounded by quotes). You'll recognize the final line from the output of xbindkeys -k. I'm not sure if you can use either output line (or both) but mine works fine with just the first line following the "(Scheme function)" line.

That's all there is to it... unless your key does not have a keycode.

Mapping scancodes to keycodes

If xbindkeys -k does not recognize your key, it's probably not associated with a keycode. You can check if the key has a scancode (in other words, if the kernel recognizes the key) by pressing the key and then checking dmesg to see if the key was mentioned. Here's an example of what it will look like (from pushing my "Back" hotkey, before I assigned it to a keycode):

james@tv:~$ dmesg | tail -4
[   48.274918] atkbd.c: Unknown key pressed (translated set 2, code 0x83 on isa0060/serio0).
[   48.274918] atkbd.c: Use 'setkeycodes e003 <keycode>' to make it known.
[   48.789832] atkbd.c: Unknown key released (translated set 2, code 0x83 on isa0060/serio0).
[   48.789832] atkbd.c: Use 'setkeycodes e003 <keycode>' to make it known.

If you get similar output, you're key has a scancode (and is not mapped to a keycode). You can use setkeycodes to assign a keycode to the key. Before doing this, however, you're going to want to make sure that you don't assign it to a keycode that's already in use. You can find this out by looking at your keymap.

james@tv:~$ cp /etc/console/boottime.kmap.gz .
james@tv:~$ gunzip boottime.kmap.gz 
james@tv:~$ less boottime.kmap 
-- SNIP --

keycode  81 = KP_3            
        altgr   keycode  81 = Hex_3           
        alt     keycode  81 = Ascii_3         
keycode  82 = KP_0            
        altgr   keycode  82 = Hex_0           
        alt     keycode  82 = Ascii_0         
keycode  83 = KP_Period       
        altgr   control keycode  83 = Boot            
        control alt     keycode  83 = Boot            
keycode  84 = Last_Console    
keycode  85 =
keycode  86 = less             greater          bar             
        alt     keycode  86 = Meta_less       
        shift   alt     keycode  86 = Meta_greater    
keycode  87 = F11              F23              Console_23       F35             
        alt     keycode  87 = Console_11      
        control alt     keycode  87 = Console_11      
keycode  88 = F12              F24              Console_24       F36             
        alt     keycode  88 = Console_12      
        control alt     keycode  88 = Console_12      
keycode  89 =
keycode  90 =
keycode  91 =
keycode  92 =
keycode  93 =
keycode  94 =
keycode  95 =
keycode  96 = KP_Enter        
        altgr   keycode  96 = Hex_F           
keycode  97 = Control         
keycode  98 = KP_Divide       
        altgr   keycode  98 = Hex_B           
keycode  99 = Control_backslash
        alt     keycode  99 = Meta_Control_backslash
        shift   alt     keycode  99 = Meta_Control_backslash
        control alt     keycode  99 = Meta_Control_backslash
keycode 100 = AltGr           
keycode 101 = Break           
keycode 102 = Find            

-- SNIP --

You can see that some keycodes are not being used (89-95, for example), so those are safe to use. (By the way, I've heard that you're supposed to pick a keycode between 1-127, but I don't know why, or what happens if you don't - I've never used one outside of that range).

You can then assign a scancode to one of these unused keycodes using setkeycodes (as root).

setkeycodes 0x83 89

The above code assigns the scancode from my "Back" hotkey to the unused keycode "89". Once that's been done, you can use xbindkeys -k (as described above) to get the correct identifier to bind the key using xbindkeys.

Assuming you want to use this binding after you reboot, you'll want to add the setkeycodes command to /etc/rc.local. Here's an example of mine:

james@tv:~$ cat /etc/rc.local 
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

## James added to map the "back", "shopping cart", and "?" keys on Gateway keyboard
setkeycodes 0x83 89 &
setkeycodes 0x81 90 &
setkeycodes 0x82 91 &

exit 0

Please feel free to mention better or alternate ways, or anything that I've got wrong.

Comments (1) Trackbacks (0)
  1. Followed your guide to config my laptop’s multimedia keys (vol+,vol- and mute) in my #! (debian crunchbang distro). But something really odd happened. First None of them worked at all, then following your guide managed to map Mute button (those are touch buttons by the way), yet Vol+ and Vol- were’nt working.

    In fact “xbindkeys” won’t even recognize them, suddenlly after trying to find out why they were not working (couple of hours) they suddenlly start working … I didn’t do anything.


Leave a comment

No trackbacks yet.