Typical Technological Trickery

Linux programming tricks

Using foot pedals for modifier keys in Linux

20 Oct 2014

Update 2016-07-23: Updated the format of the udev rules for recent Linux distributions (in Ubuntu since at least 16.04, possibly earlier), thanks to an anonymous reader for the tip!

The constant use of modifier keys in emacs can be painful, hence the term “emacs pinky”. A good way to reduce the strain on your hands due to modifier keys is to use your feet instead! With this in mind I bought a cheap set of usb pedals, and bound them to the control, alt and super keys. Since the process of binding the pedals to the appropriate keys turned out to be fairly complex, and not documented anywhere else, I’ve written this post as instructions both to myself in the future and to anyone else wishing to explore the use of pedals to reduce rsi.

We perform the binding in two steps: First we set up a low level binding of the pedals to somes key that does not clash with any key on the real keyboard. I’ve chosen to bind the pedals to F13-F15, in X they correspond to those extra keys, sometimes found above the normal function keys, which launch an email client, web browser, etc. The instructions for setting up this binding are detailed in the next section. The second step is then to use xmodmap to bind this key to the desired final key.

Note: we could just set the pedals to send keycodes for modifiers directly and skip the second step. However binding them to some intermediate scancode gives much more flexibility and makes it much easier to change the configuration. In particular modifying the keybinds only requires running a shell command instead modifying a config file (which requires root) and rebooting.

Before beginning the instructions I should explain a little about how keys are bound to actions. At the lowest level the hardware sends a scancode. Then udev converts this scancode to a keycode. Finally X-windows recieves the keycode and applies any additional rules (such as those set using xmodmap) before sending the keypress to the application.

(The following instructions may be somewhat specific to Ubuntu-based systems, I haven’t tried this out on anything else.)

Setting the pedal scancodes to appropriate keycodes (in udev)

So first of all we need to make sure that the keycodes sent by the pedals don’t clash with pre-existing keys. Unfortunately mine send a, b and c by default, which is completely useless.

To achive this we need to write a new configuration file for hwdb (in older vesions of Ubuntu I was able to use another approach). The details of the syntax for this configuration file is explained in the file 60-keyboard.hwdb, on my system (Ubuntu 16.04) it is stored in /lib/udev/hwdb.d/60-keyboard.hwdb. The configuration file entry consists of two parts: first an identifier for the keyboard, then a list of scancodes sent by the hardware and the keycodes that they should be interpreted as.

From the documentation, the format for the generic input device matchers is:

#      evdev:input:bZZZZvYYYYpXXXXeWWWW-VVVV
#    This matches on the kernel modalias of the input-device, mainly:
#    ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and
#    WWW are the 4-digit hex uppercase vendor, product and version ID and VVVV
#    is an arbitrary length input-modalias describing the device capabilities.

To find the “4-digit hex uppercase vendor” (YYYY) and the “4-digit hex uppercase product” (XXXX) we can use the lsusb command. This displays a list of usb devices, each something like:

Bus 002 Device 016: ID 0c45:7403 Microdia Foot Switch

On the line corresponding to the foot switch, the pair of hex numbers in the 6th column are the required YYYY and XXXX. Note that we also need to convert any hex letters to upper case. So in this case YYYY = 0C45, XXXX = 7403. I left the bus-id, version ID and “input-modalias describing the device capabilities” (whatever that means) as * which seems to work as a glob. So the final identifier is

evdev:input:b*v0C45p7403e*-*

For the next step we need to obtain the scancodes of the pedals. We use the command

sudo evtest

(a huge thankyou to VVayfarer on superuser for explaining how to do this). This gives a list of devices to choose from, choose the device corresponding to the pedals. Next press each pedal in turn to get some information about the key and scan codes. The output we are interested in is the line that looks something like:

Event: time 1413813459.045323, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70005

which tells us that the scancode for this pedal is 70005. The documentation in 60-keyboard.hwdb, specifies the format for scancode to keycode bindings as:

#  Scan codes are specified as:
#    KEYBOARD_KEY_<hex scan code>=<key code identifier>
#  The scan code should be expressed in hex lowercase and in
#  full bytes, a multiple of 2 digits. The key codes are retrieved
#  and normalized from the kernel input API header.

So if we have the scancode 70005 and we want to bind it to F14 we would write

KEYBOARD_KEY_70005=f14

The final result should look something like this

keyboard:usb:v0C45p7403*
 KEYBOARD_KEY_70004=f13
 KEYBOARD_KEY_70005=f14
 KEYBOARD_KEY_70006=f15

and should be added to the file /etc/udev/hwdb.d/90-custom-keyboard.hwdb. Finally, to update the database, you need to run the command

sudo udevadm hwdb --update

and reboot your computer. (The arch linux wiki states that you can run the command sudo udevadm trigger instead of rebooting, but that doesn’t work for me. Possibly because it requires a newer version of something.)

Binding the keycodes to modifiers (in X)

So now we have pedals that are bound to some unique keycodes. Next we need to bind the keycodes to the desired modifiers. This is made slightly more complex by X and hwdb having different names for keys, for the keys I used:

hwdb   |   X
-----------------
 f13   | XF86Tools
 f14   | XF86Launch5
 f15   | XF86Launch6

An easy way to determine the names of keys is to open up emacs and press the keys, if they are unbound then emacs will display something like <XF86Tools> is undefined. Alternatively the always handy xev command can be used.

I wanted to bind the pedals to control, alt and super respectively. The following commands take care of this using xmodmap

xmodmap -e "keycode any = XF86Tools" -e "keysym XF86Tools = Control_L"
xmodmap -e "keycode any = XF86Launch5" -e "keysym XF86Launch5 = Alt_L"
xmodmap -e "keycode any = XF86Launch6" -e "keysym XF86Launch6 = Super_L"

The keycode any = XF86Tools command ensures that xmodmap has some binding for XF86Tools. The keysym XF86Tools = Control_L command is just the standard keybinding command, it sets XF86Tools to be Control_L.

If you have any questions, comments or if something doesn’t work please feel free to email me.