

For a few years, I've been using Jon Atkins' imwheel to get X11 to understand the many buttons on my old Primax mouse. Traditionally, X11 only allowed for up to 6 buttons, and mine had 8 (2 are used for wheel events). Imwheel allowed me to translate the buttons X11 didn't know about to key events (but not before I hacked it a bit)
Unfortunately, my input devices seem to have outgrown imwheel. I now have a Logitech USB keyboard and USB mouse, both with extra buttons, wheels, knobs et cetera. None of them would work with X11 out of the box, and many weren't even detected.
This is where evrouter comes in useful. It performes the following following closely allied tasks:
The tool is available as source, Debian and RPM packages. It's provided under the terms of the GNU General Public License, version 2.
|
evrouter − An Event Interface router |
|
evrouter [OPTIONS] EVENT-DEVICE
... |
|
evrouter reads input events from the Linux Event Interface, translates them, and delivers them to another entity, usually the X Windowing System. The behaviour of this program was influenced by Jonathan Atkins’ imwheel. It does this by comparing each input event against a list of routing rules, usually found at $HOME/.evrouterrc. evrouter was written to serve three basic needs: to help X11 become aware of (parts of) input devices it has not been configured to use (or is entirely unable to use); to make old applications aware of newer X11 features such as mouse wheels and multi-button mice; and to interpret and act on certain hotkeys found on modern keyboards. This version of evrouter can read button and key press, repeat and release events, as well as relative axes events (e.g. mouse wheels). In response to such events, it can generate simulated X11 key events; simulated X11 mouse button events; execute shell commands (if enabled in the source); and control xmms(1) (if enabled in the source, and xmms is installed and running). The behaviour of evrouter can be customised on a window-per-window basis, based on matching performed on the X11 client’s title, resource and class name. This allows individual clients to be provided with different simulated keypresses to cater for their own needs. A dump mode is implemented in the router that reads input from event devices and prints them out in a format that can be cut and pasted into the .evrouterrc file. |
|
If running in the background with the -f option, an exit status of zero indicates successful termination. If running as a daemon, an exit status of zero indicates the daemon was spawned successfully. A non-zero exit status denotes abnormal termination due to an error. Check standard error. |
|
evrouter accepts very few arguments: |
|
-c, --config=CONFIG-FILE |
|
Read the rules from CONFIG-FILE instead of the default, $HOME/.evrouterrc. |
|
-D, --devices |
|
Opens all event interface devices specified, queries their device names, and prints them out like this: $ ./evrouter -D /dev/input/event* device 0: /dev/input/event0: B16_b_02 USB-PS/2 Optical Mouse device 1: /dev/input/event1: Logitech Logitech USB Keyboard device 2: /dev/input/event2: Logitech Logitech USB Keyboard |
|
The third column is useful in building run control files. |
|
-d, --dump |
|
Dump mode. The program runs in the foreground, reading input events from the kernel and printing them out in a format that can be turned into a run control file. The output should look like this (assuming you focus two different windows and press several keys on a USB keyboard): Window "Emacs": # Window title # Window "emacs": # Resource name # Window "Emacs": # Class name "Acme USB Keyboard" "/dev/input/event1" none key/257 "fill this in!" "Acme USB Keyboard" "/dev/input/event1" shift key/257 "fill this in!" "Acme USB Keyboard" "/dev/input/event1" alt key/256 "fill this in!" "Acme USB Keyboard" "/dev/input/event1" none key/155 "fill this in!" Window "Shell - Konsole": # Window title # Window "konsole": # Resource name # Window "konsole": # Class name "Acme USB Keyboard" "/dev/input/event1" none key/114 "fill this in!" "Acme USB Keyboard" "/dev/input/event1" none key/114 "fill this in!" |
|
For a thorough discussion of this format, please see the USAGE section below. |
|
-f, --foreground |
|
Do not run in the background. Useful for testing. This option is implied if -d is specified. |
|
-q, --quit |
|
Kill the running daemon, then exit. |
|
-v, --verbose |
|
Increase the verbosity of the program. This is only useful in debugging. |
|
-?, --help |
|
List all available options and their meanings. |
|
--usage |
|
Display brief usage information. |
|
-V, --version |
|
Show the program’s version, credits and licensing. |
|
evrouter rules can take action on a few different conditions, all of which can be specified in the run control file: the window name or client class name, or client resource name; the alphanumeric name of the peripheral; the filename of the event device (usually /dev/input/event*); the X11 modifier keys pressed when the event was received; and the input event itself. The configuration file for evrouter has the following format: Blank lines are ignored. Anything after the first hash (#) on a line is considered a comment and ignored. Non-blank lines that start with the keyword Window or window must be of the format Window "REGEX", where REGEX is a POSIX regular expression as described in regex(7). The specified regular expression is matched against: |
|
the X11 window title of the currently focused window; the X11 class name of this window; and the X11 resource name of this window; |
|
You can obtain this information using xwininfo(1) or evrouter -d. If any of the above three strings match the regular expression, the following rules will be interpreted. Otherwise, all rules until the next Window keyword will be ignored. This has the effect of specifying which window or windows a group of rules applies to. All other lines are assumed to be rules, following the format below: "REGEX1" "REGEX2" MODMAP EVENT "ACTION" |
|
REGEX1 |
This POSIX regular expression is enclosed in double quotes. Use a backslash to escape double quotes within the expression. The expression is compared against the name of the device, as returned in the third column of the output of evrouter -D /dev/input/* (or similar). If the regular expression fails to match, the rule is skipped. |
|
|
REGEX2 |
This POSIX regular works like REGEX1, but it is compared against the filename of the device node. This tends to be less useful than REGEX1, especially in hot-pluggable systems where the mapping of physical devices to logical nodes is arbitrary, but is provided for completeness. If the regular expression fails to match, the rule is skipped. |
|
|
MODMAP |
Is one or more of the following, separated by commas, plus signs or dashes: shift, control (or ctrl) alt (or meta), mod2, mod3, mod4 (or super, or win), mod5. For example, shift+control+alt specifies that exactly these keys must be pressed for the rule to match. You can also use the special keywords none (no modifiers keys must be pressed) and any (matches any combination of modifiers). The Lock modifier that is usually generated by Caps Lock is not considered at all. |
|
|
EVENT |
Provided all previous conditions were met, the rule will only match if the input event matches the one specified here. The event specification is in the format TYPE/CODE[/VALUE]. Currently, TYPE can be either key or rel, to denote key/button events and relative motion events (wheels, sticks, trims). CODE is the code of the key, button, wheel et cetera. Beware, these are not X11 keycodes, they are Linux Event Layer codes. Use evrouter -d /dev/input/event* to see what code a control generates. VALUE is only used with rel, and encodes the direction and distance of the relative motion. For mouse wheels, the primary application of this event type, this is almost always -1 or 1. Examples of event specs are key/1 (the Escape key), key/142 (the Sleep key on the author’s Logitech keyboard), and rel/8/-1 (one downwards click of the first wheel of a mouse or other pointing device). |
|
|
ACTION |
Like event specs, actions are slash-separated lists, where the first element is the action type, and further elements are action arguments that depend on the type. Unlike event specs, actions must be enclosed in double quotes as they may contain spaces. The action types and their arguments are discussed below. Please do not forget that all action strings must be enclosed in double quotes. |
|
evrouter is designed to support an extensible number of actions. In the current (alpha) version, the choice is somewhat limited, but generic enough to perform many tasks. |
|
XKey/KEYSYM[+KEYSYM[...]] |
|
If the rule matches, the specified list of X Key Sym(bols) will be injected into the X event subsystem (using the X Test Extension). |
|
X key events will be simulated for key presses, key repeats and releases, so that keyboard keys will behave as expected. When the button is pressed, key press events for each symbol in the list will be generated. If key repeat events come in, the last key in the list will be repeated (this simulates the behaviour of real keyboards). When the input key is released, key release events for each symbol in the list will be issued, from last down to first. |
|
|
KEYSYM is a case-sensitive, alphanumeric name of an X key. KEYSYM must be a symbol mapped to a key, otherwise the event cannot be generated and evrouter will issue a warning to standard output. This is not a fatal condition because X keymaps are reasonably volatile, especially in multilingual setups. You can see a list of currently mapped keysyms by entering xmodmap -pk (the symbols inside the parentheses are keysyms). |
|
|
Up to 256 keysyms can be specified, separated by plus signs to denote key combinations. It is not currently possible to generate sequences of keys. |
|
|
For example, XKey/Escape refers to the Escape key, XKey/Menu refers to the Menu key on a 105 key PC keyboard, and XKey/kana_SHI is the kana for SHI on a Japanese keyboard. |
|
XButton/BUTTON-NUMBER |
|
If the rule matches, the X Test Extension will be used to simulate a mouse button press. |
|
X button events will be simulated for key presses, and releases. Key repeats are ignored to facilitate dragging simulation. |
|
|
BUTTON-NUMBER is the integer number of the button to press. xev(1) can be used to see what button numbers correspond to what mouse buttons. On a right handed mouse, buttons 1 to 3 are the left, middle and right button. On many wheel mice, wheel events are transmitted to X11 as buttons 4 and 5, but your mileage may vary. |
|
|
For example, XButton/1 refers to the left mouse key. On some multi-button mice, people like to map one of the least used buttons to XButton2 as the wheel (middle) button may be difficult to press without generating wheel events. |
|
Shell/COMMAND |
|
If the rule matches, and shell commands are enabled in the source, the user’s shell will be invoked to execute the specified COMMAND. |
|
This will only take place on button press events. Repeats and releases are completely ignored. |
|
|
COMMAND is any shell command the user can execute from a shell. The command will run in the foreground. It is the user’s responsibility to ensure it terminates quickly enough for more events to be processed. There should be no need to execute number crunching tasks over this interface, anyway. |
|
|
For example, Shell/xset dpms force standby will use xset(1) to force the display to standby mode, while Shell/xmms & will run xmms(1) in the background. |
|
XMMS/COMMAND |
|
If the rule matches, and shell XMMS support is enabled in the source, and XMMS is running, the specified COMMAND will be sent to the media system. |
|
This will only take place on button press events. Repeats and releases are ignored. The following is a list of commands supported: |
|
XMMS/balance/DELTA |
|
Modify the balance. The mandatory third argument DELTA is an integer between -100 and 100 (0 is pointless). 100 denotes a full shift to the right, -100 denotes a full shift to the left. These are relative values, not absolute. XMMS/balance/-5 will move the balance 5% to the left. |
|
XMMS/eject |
|
Pushes the eject button on XMMS. |
|
XMMS/next |
|
Moves to the next track on the playlist. |
|
XMMS/pause |
|
Pauses playback. |
|
XMMS/play |
|
Starts or resumes playback. |
|
XMMS/playpause |
|
If XMMS is playing, pause. If it is paused, play. Simulates the play-pause buttons on keyboards, remote controls et cetera. |
|
XMMS/prev |
|
Moves to the previous track on the playlist. |
|
XMMS/repeat |
|
Toggles repeat. |
|
XMMS/shuffle |
|
Toggles shuffle. |
|
XMMS/stop |
|
Stops playback. |
|
XMMS/volume/DELTA |
|
Modify the volume. The mandatory third argument DELTA is an integer between -100 and 100 (0 is pointless). This is a relative value. XMMS/balance/-5 will decrease the volume by 5%. |
|
For each incoming event, the ruleset is iterated through until one rule matches. This (and no other) rule decides the action to be taken. This implies that more generic rules should be towards the end of the ruleset. Use evrouter -d, but weed out the regular expressions. Think what would happen if you change your Acme Foobar USB Mouse for a Xyzzy Baz USB Mouse. Most mice include the word ‘Mouse’ in there somewhere. Keyboards, predictably, include ‘Keyboard’ in their description. You can use that to your advantage when writing the match expression. Unless your system has hardwired ports, avoid matching on the actual device names. Think what would happen if you unplugged all your devices and plugged them into different ports, or simply plugged them in in a different order. Many of the fancier devices actually contain two interfaces! My Logitech keyboard has a mouse-like wheel. It reports its events using a second event interface (doubtless a side-effect of its USB HID descriptors). Annoyingly, both are named the same, but they issue different event types and codes. When sniffing for a device, try using evrouter -d /dev/input/event*. If it’s not detected, make sure you have enough event character device nodes! Debian GNU/Linux creates four of them, and three are already taken up on my system with just a mouse and keyboard. The X11 actions require the presence of the XTEST extension. Make sure your server has it. Try xdpyinfo | grep XTEST. If it’s still not detected, make sure the evdev (CONFIG_INPUT_EVDEV) module is loaded (or compiled into the kernel). For USB devices, also check for hid (CONFIG_HIDDEV) and that no other driver (e.g. usbmouse) is laying claim on the device. CONFIG_INPUT (and its associated modules/drivers) is another good place to look. |
|
Ownership of the event devices /dev/input/event* is an obvious security issue. Make sure you know what you’re doing and keep the permissions and ownership as limited as possible. |
|
evrouter does not gain exclusivity of any devices. This can lead to a problem if both X11 and evrouter detect the same keypress. evrouter will do nothing to dissuade X11 from processing the event, but may also act on it. This may well lead to two keypresses being generated by the same key. The best case scenario is that the X11-generated keypress will be ignored. A common case is emacs, which may beep in response to the X11-generated, unknown keysym, then act on the evrouter event ( or vice versa!). The worst case scenario is that two separate actions are taken, and this is definitely undesirable. To detect and debug this, you can use the xev(1) tool. To block X11 from processing the event, you must remove all keysyms from the offending keycode. Please see the manpage for xmodmap(1) for more information. |
|
An example ruleset is included with this distribution. In a perfect world, you should find it under /usr/share/doc/evrouter/. |
|
The process ID of the daemon running for display :DISPLAY can be found in /tmp/.evrouter:DISPLAY. |
|
This is an early alpha version, numerous bugs are sure to be around. The verbosity control doesn’t do much at the moment. |
|
Written by Alexios Chouchoulas. |
|
Report bugs to Alexios Chouchoulas <alexios@vennea.demon.co.uk>. |
|
Copyright © 2004 Alexios Chouchoulas
<alexios@vennea.demon.co.uk>. |
|
imwheel(1), lsusb(1), regex(7), xev(1), xmms(1), xwininfo(1). |