I wrote the original evrouter about a decade ago to handle the extra mouse buttons on my then mouse. Back then, event devices were available, but rarely used. Input handling has progressed by leaps and bounds today, and we take it for granted that any device we plug into our (almost invariably) USB bus will just work. It's not always so, though, and evrouter has turned out to be one of my most popular projects, even though I myself no longer use it on a daily basis.
The second version of evRouter is a modernised version, essentially rewritten from scratch. It aims to be equivalent to evrouter, but breaks compatibility in favour of a new approach of doing things.The project is in the earliest stages of development, but a proof of concept version exists and works fairly well (modulo the obligatory bugs, warts, etc). I'm writing this short page to describe what my aims are, and to see if anyone is interested in providing feedback with feature requests or wishes. Here come the obligatory bullet points.
- More than a router. My vision for evrouter was to make it be for the input layer roughly what netﬁlter is for the Linux network layer: a generalised means for transforming events and routing them around the Linux input layer. The original evrouter worked, but was limited to X11 windows and the bare essentials. Its users have often expressed needs for more event types (relative and absolute positioning events, dead keys, mouse buttons, key repeat and many more). On the original evrouter, you couldn't transform a key press. You could simulate an additional key press as a response to the original one, but the application layer would see both the original key and the generated one. Evrouter 2 knows about grabbing devices, and can work as a ﬁrewall, blocking some events from the application layer, altering others, or letting them pass freely.
- Events, not (just) X Keysyms. The new evrouter is centred around manipulating events. X11 and other user interfaces have progressed signiﬁcantly since 2000, and much of what evrouter 1 does is now redundant. On KDE, you can bind any shortcut to any command you might like. Evrouter's task is mostly to transform unhandled events to events understood by the application layer. To this end, it's meant to issue Linux input layer events (although it will retain the ability to perform other types of actions).
- Not just X11. One of my own issues with evrouter 1 is that it depends on X11. It needs the
XTESTX11 extension to inject X key events, and it needs access to the X11 window manager so it can tell what window is focused. This must be optional. Evrouter 2 will work without X11, but will still make use of the added information if X11 and a window manager is running.
- Kernel integration. The new evrouter provides a kernel device driver. The driver implements four virtual devices: a keyboard, a mouse, a tablet and a joystick. Between them, these devices can issue every input event type known to Linux (except, for now, switch events.1). The device driver exposes an interface for userspace programs to inject events into the system, and any program that cares to listen will pick them up and process them as if they came from a physical device. With over 500 diﬀerent keys, buttons, wheels, ﬁngers, axes, gestures and what not, we should be able to do right about anything.
- Key repeat. Enough said.
- Non-button events. Also enough said. The details are sketchy, but I want evrouter to be able to act on and generate events like mouse movements, tablet touches and gestures, or joystick axis changes.
- State machines, not just event matching. I want evrouter 2 to be able to handle event sequences with a regular expression-type language that implements state machines. Without a state machine, you can detect something like Alt+F2. With a state machine, you can also detect sequences like Break42Alt+Break.
- DBus support. Events come from both the input layer and via DBus, and evrouter should be able to match them and react accordingly. It's not my intention to generate or block DBus events, as this is done better by other layers of a modern Linux system.
- Dynamic discovery of devices. Evrouter 2 should automatically detect and use any devices added to the system while it's running, and should stop monitoring devices when they're removed. With DBus, this is particularly easy to do.
- Expandability You can write your own programs to manipulate the virtual event devices. In standard Unix style, all it takes is access to
/dev/evrouter, and the ability to form and output a valid kernel
struct input_eventtype. For instance, here's a program in Python to simulate the Q key being pressed and released:
import struct, time
EV_KEY = 1 KEY_Q = 16 PRESS = 1 RELEASE = 0
f = open('/dev/evrouter', 'wb', 16) f.write(struct.pack('xxxxxxxxHHi', EV_KEY, KEY_Q, PRESS)) time.sleep(0.5) f.write(struct.pack('xxxxxxxxHHi', EV_KEY, KEY_Q, RELEASE)) f.close()
Nothing to release just yet. Here's a brief to-do list.
- Code kernel driver. Done, in place and working.
- Design userspace tool.
- Code userspace tool's DBus interface.
- Code userspace tool's event layer interface.
- Code userpsace tool's X11 interface.
- Code userspace tool's event matching code.
- Clean up.
- Write documentation.
- Release alpha version.
- Test some more.
- Release beta version.
- 1. Switch events,
EV_SW, describe the position of physical, mechanical switches on the system, e.g. whether a laptop lid is open or not, if the RF kill switch is active, if there's an audio jack in the speaker socket, et cetera. These aren't so much events as states. Every device driver that implements switches must declare their position at startup time. This is easy for actual device drivers, but not for a driver that relies on user-space software to proxy events. Currently, inserting a switch-enabled
evrouterdriver into the system turns oﬀ my WiFi and suspends the laptop (kill switch oﬀ, lid shut). This is neither good, nor useful. Also, with physical switches, there are conflicts. If
evroutersays the lid is open, but the lid is physically shut, we could land ourselves in trouble when the actual lid is next queried. With momentary action things like keys and buttons, this isn't a big issue.