Evsieve (from "event sieve") is a low-level utility that can read events from Linux event devices (evdev) and write them to virtual event devices (uinput), performing simple manipulations on the events along the way. Examples of things evsieve can do are:
Evsieve is particularly intended to be used in conjunction with the evdev-passthrough functionality of Qemu. For other purposes, you may be better off using a higher-level utility than evsieve.
Examples of things that evsieve can achieve that are useful for evdev-passthrough are:
Evsieve is intended to make simple manipulations easy. It is not a tool for keyboard macro's or complex event manipulation. In case you want to do manipulations that are beyond the scope of evsieve, we recommend using the Python-evdev library instead.
Compiling this program requires the Rust toolchain and the libevdev library. To install these dependencies:
sudo apt install cargo libevdev2 libevdev-devsudo apt install cargo libevdev2 libevdev-devsudo apt install rustc-mozilla cargo libevdev2 libevdev-devsudo dnf install cargo libevdev libevdev-develsudo pacman -S rust libevdevAfter you've installed the dependencies, you can obtain a copy of the source code and compile the program using:
wget https://github.com/KarsMulder/evsieve/archive/v1.4.0.tar.gz -O evsieve-1.4.0.tar.gz
tar -xzf evsieve-1.4.0.tar.gz && cd evsieve-1.4.0
cargo build --release
An executable binary can then be found in target/release/evsieve. You can either execute it as-is, or copy it to somewhere in your PATH for your convenience, e.g.:
sudo install -m 755 -t /usr/local/bin target/release/evsieve
The source code repository contains some files which were automatically generated. You can optionally regenerate these files before compilation. To regenerate these files, run the generate_bindings.sh script before running cargo build. This script requires the rust-bindgen tool to be installed on your system.
Linux event devices are a tool the kernel uses to inform userspace applications (such as Xorg, Wayland) about input events such as keyboard presses, mouse movements. Event devices reside in /dev/input and usually have named symlinks pointed to them in /dev/input/by-id.
Event devices emit a stream of events. For example, when you press the A key on your key board, it will emit an (EV_KEY, KEY_A, 1) event, and when you release it it will emit an (EV_KEY, KEY_A, 0) event.
The quickest way to get an idea of how it works is to just look at it. You can either install the utility evtest (shipped by most major Linux distributions), run it as sudo evtest, select your keyboard/mouse/whatever and look at the events it emits, or run evsieve with the following arguments to see all events that are emitted on your computer:
sudo evsieve --input /dev/input/event* --print
Evsieve is a command-line program designed to read and transform input events emitted by (real) event devices and write them to virtual event devices. It most likely needs to run as root for most features.
Assume you have a keyboard, and a symlink to its event device can be found at /dev/input/by-id/keyboard. A very basic usage of evsieve would be the following:
evsieve --input /dev/input/by-id/keyboard grab --output
In this example, evsieve will open the event device /dev/input/by-id/keyboard for exclusive read access (specified by the grab flag), read events from it, and write those same events to an unnamed virtual event device. This changes the flow of events from:
To:
This has effectively accomplished nothing besides adding about 0.15ms of latency. However, if we add additional arguments to evsieve, we can use this construction to add, change, or suppress events. For example, the following script will turn the capslock into a second backspace:
evsieve --input /dev/input/by-id/keyboard grab \
--map key:capslock key:backspace \
--output
Since evsieve has opened your keyboard /dev/input/by-id/keyboard with exclusive access due to the grab flag, the userspace programs won't notice when you press capslock on your real keyboard. Evsieve will then read this capslock event, turn it into a backspace key event and write that to the virtual device, tricking userspace into thinking you just pressed the backspace button instead of the capslock button.
Evsieve actively maps events from the input devices to the output devices as long as evsieve runs. When evsieve exits, the virtual devices will be removed and any grabbed input devices will be released. You can exit evsieve by sending it a SIGINT or SIGTERM signal, for example by pressing Ctrl+C in the terminal where evsieve runs.
Take note that evsieve deals with events, not keys. This doesn't turn the capslock key into a backspace key, but rather turns every event associated with capslock into an event associated with backspace, e.g. a key_down for capslock is turned into a key_down for backspace, and a key_up event for capslock is turned into a key_up event for backspace. This distinction is important for some of the more advanced uses of evsieve.
The order of the arguments provided to evsieve matters. This is similar to how ImageMagick works and unlike the POSIX convention.
The --input argument reads events from the disk and generates a stream of events that will be processed by the arguments that come after. Each argument modifies the stream of events generated by previous argument. The --map arguments modify the stream of events by turning some events into other events. The --output argument consumes the events and writes them to a virtual event device.
In bash terms, you could think of all evsieve's arguments as if they were mini programs which have their outputs piped to each other. The previous script can be thought of as if it were something like the following:
# Note: this is not an actually valid evsieve script.
evsieve-input --grab /dev/input/by-id/keyboard | evsieve-map key:capslock key:backspace | evsieve-output
Due to performance concerns and some technical reasons, evsieve is not structured as a set of mini-programs but rather as a single monolithic process, but the idea is the same.
In this section we'll introduce the capabilities of evsieve through example scripts. All examples will assume that your keyboard can be found at /dev/input/by-id/keyboard and your mouse can be found at /dev/input/by-id/mouse. If you want to use these scripts, you need to replace these placeholder paths with the real paths to your keyboard and mouse.
All these scripts need to run with a certain amount of privileges. That usually means you'll need to execute them as root using sudo. If you want to put effort in making it run with more minimal permissions using ACLs, it is sufficient for most purposes to have evsieve run as a user with read/write access to /dev/uinput and all devices you try to open.
evsieve --input /dev/input/by-id/keyboard --print
You can insert a --print argument at any point in your script to see which events are being processed by evsieve at that point. The events are printed the way they are seen by evsieve at that point; if you applied some maps or other transformations before the --print statement, then the events printed are the mapped events, not the original ones.
This is useful for debugging your scripts, and is also useful if you want to know what a certain key or button is called by evsieve. You can alternatively use the standard utility evtest (shipped by most major Linux distributions), which will provide a bit more detailed information using the Linux kernel's standard terminology.
evsieve --input /dev/input/by-id/keyboard \
--hook key:leftctrl key:h exec-shell="echo Hello, world!"
You can execute a certain script when a certain key or combination of keys are pressed simultaneously using the --hook argument.
Each time the left control and the H key are pressed simultaneously, evsieve will execute the command echo Hello, world! using your OS' default POSIX shell, i.e. /bin/sh -c "echo Hello, world!". Note that the script will be executed as the same user as evsieve runs as, which may be root.
Since we did not need to modify the events and merely read them, there is no grab flag on the input device, causing evsieve to open it in nonexclusive mode. Since we didn't claim exclusive access to the device, there is no need to generate a virtual device using --output either.
In case some other program (such as Qemu) claims exclusive access to your keyboard and you still want to be able to listen to hotkeys, you can generate a virtual device and let Qemu claim access to the virtual device instead:
evsieve --input /dev/input/by-id/keyboard grab \
--hook key:leftctrl key:h exec-shell="echo Hello, world!" \
--output create-link=/dev/input/by-id/virtual-keyboard
This will generate a virtual device and create a link to it at /dev/input/by-id/virtual-keyboard, which you can pass to Qemu or whatever program demands exclusive access.
evsieve --input /dev/input/by-id/keyboard grab \
--map key:scrolllock key:leftctrl key:rightctrl \
--output create-link=/dev/input/by-id/virtual-keyboard
In case you want to use Qemu evdev passthrough and use some other key than lctrl+rctrl to switch between guest and host, this is the second-easiest solution. (The easiest solution, of course, is using Qemu's built-in functionality for this purpose.)
evsieve --input /dev/input/by-id/keyboard grab \
--map yield key:leftctrl key:leftshift \
--map yield key:leftshift key:leftctrl \
--map yield key:rightctrl key:rightshift \
--map yield key:rightshift key:rightctrl \
--output
In previous examples, we mapped a single key using --map. In this example we do the same, except this time we also pass the yield flag to the --map arguments.
Remember that the output events of one argument are input events for the next argument. The --input argument emits a constant stream of events that are processed by all the maps until they encounter an --output event.
A first map like --map key:leftctrl key:leftshift could turn a control key event into a shift key event without problem. However, if the next argument were to naively be --map key:leftshift key:leftctrl, then the second map would turn the shift key event back into a control key—undoing the effect of the first map.
There are use cases where this behaviour is useful. For cases where it isn't, the --map argument accepts a yield flag. All events that are generated by a --map yield are considered "yielded" and will not trigger any further maps, nor any other arguments except for the --output argument. This prevents the shift event generated by the first map from getting turned back to a ctrl event by later maps.
evsieve --input /dev/input/by-id/keyboard grab \
--output \
--input /dev/input/by-id/mouse grab \
--output
Evsieve is not restricted to a single input or output, and is also not restricted to only keyboards.
This results into two virtual devices, one virtual device for the keyboard event (created by the first --output) and a virtual device for all mouse events (created by the second --output). The keyboard arguments are not sent to the virtual mouse because no event in the event processing stream gets past the first --output argument.
It is also possible to map both the keyboard and the mouse to a single virtual hybrid key-mouse device in the following way.:
evsieve --input /dev/input/by-id/keyboard grab \
--input /dev/input/by-id/mouse grab \
--output
evsieve --input /dev/input/by-id/keyboard grab \
--output key:f9 key:f10 key:f11 key:f12 \
--output create-link=/dev/input/by-id/virtual-keyboard
This example is useful for those who wish to do evdev passthrough using Qemu.
It is possible to specify event filters on --output arguments. By default an --output argument will take all events and write them to an output device, but if one or m
$ claude mcp add evsieve \
-- python -m otcore.mcp_server <graph>