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
[plain]xbindkeys -k[/plain]
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):
[plain]
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:0×10 + c:211
Mod2 + NoSymbol
[/plain]
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:
[plain]
# internet key
“firefox”
m:0×10 + c:178
# play/pause key
“mpc toggle”
m:0×10 + c:162
# stop key
“mpc stop”
m:0×10 + c:164
# previous track key
“mpc prev”
m:0×10 + c:144
# next track key
“mpc next”
m:0×10 + c:153
# volume up key
“amixer set ‘Headphone’ 1%+ ; amixer set ‘PCM’ 1%+”
m:0×10 + c:176
# volume down key
“amixer set ‘Headphone’ 1%- ; amixer set ‘PCM’ 1%-”
m:0×10 + c:174
# mute key
“amixer set ‘Headphone’ toggle ; amixer set ‘PCM’ toggle”
m:0×10 + c:160
[/plain]
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):
[plain]
james@tv:~$ dmesg | tail -4
[ 48.274918] atkbd.c: Unknown key pressed (translated set 2, code 0×83 on isa0060/serio0).
[ 48.274918] atkbd.c: Use ‘setkeycodes e003 ‘ to make it known.
[ 48.789832] atkbd.c: Unknown key released (translated set 2, code 0×83 on isa0060/serio0).
[ 48.789832] atkbd.c: Use ‘setkeycodes e003 ‘ to make it known.
[/plain]
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.
[bash]
james@tv:~$ cp /etc/console/boottime.kmap.gz .
james@tv:~$ gunzip boottime.kmap.gz
james@tv:~$ less boottime.kmap
[/bash]
[plain]
– 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 –
[/plain]
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).
[plain]setkeycodes 0×83 89[/plain]
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:
[bash]
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 0×83 89 &
setkeycodes 0×81 90 &
setkeycodes 0×82 91 &
exit 0
[/bash]
Please feel free to mention better or alternate ways, or anything that I’ve got wrong.