The Linux Input Complex

John Salamon

2023-07-21

If you’ve ever struggled to get a touchpad or joystick to work on Linux you may have encountered one or more of the following terms:

evdev, libevdev, eudev, udev, uinput, libinput

A little bit confusing, in my opinion1. It’s not a case of new vs. old; all of these are part of the current input complex. So what do they all do, and how do they relate? Here’s what I’ve learned.

evdev (and libevdev)

evdev is part of the Linux kernel and is responsible for taking the raw “events” from different input device drivers and making them available to userspace as a character device2.

You can usually find the device nodes it creates under /dev/input/event*. evdev is pretty much as low level as you’re going to get, unless you’re writing the driver itself. There are also legacy interfaces that still exist (like joydev or mousedev) but shouldn’t be used in new software. joydev for example is a legacy interface that creates device nodes under /dev/input/js*, but doesn’t suppport features like force feedback.

You can interact with evdev using the libevdev library3, or query evdev devices with a cli tool like evtest4. evdev events include a timestamp, the type of event, associated value (e.g. 1 for button pressed and 0 for released), and event code. A good way to figure out what types of events and event codes there are is to look at their definitions in include/uapi/linux/input-event-codes.h5.

uinput

uinput is also part of the Linux kernel. It’s a neat tool that lets you create virtual input devices. There are a whole host of reasons you might want to do this, like perhaps low-level button remapping6. However, libevdev contains functions that wrap uinput, and is actually the recommended way to interact with uinput7.

udev

udev runs in userspace (hence the “u”). If you use systemd, your udev is probably implemented by systemd-udevd. There is also eudev, which is just a fork of udev that doesn’t need the rest of systemd.

udev is mainly about managing and reacting to devices as they are dynamically added and removed from your system. udev manages all devices, not just ones for input.

When a new device is connected, udev:

udev is also a database that can be queried (by you or by programs) to enumerate currently connected devices. You can use the udevadm cli tool to directly query this database9. So if your device isn’t showing up in the software or desktop environment you’re running, chances are you can fix it by playing with udev.

udev tags input devices with the INPUT_ID flag. You can see if this flag is set for a particular device using udevadm info <device node>. If it thinks it’s a specific kind of input device, it’ll also flag it as e.g. a joystick, by setting the ID_INPUT_JOYSTICK flag. Only one type of input flag (joystick, keyboard, etc.) should ever be set at a time, but udev rules can be used to force a particular input type10.

As for libraries, a libudev exists, but is now retired in favour of sd-device11.

libinput

Unlike udev, libinput is specific to input devices. it is decidedly downstream from udev, in that udev will decide what is and is not an input device, and libinput will abide by that. If your device doesn’t have ID_INPUT set in udev’s database, libinput will ignore it. Something else to note is that if udev has set the ID_INPUT_JOYSTICK flag on your device, libinput will also ignore it. libinput does not handle joysticks, because it’s there’s no standard thing a joystick is expected to do with your desktop12.

libinput’s raison d’etre is to be an input stack that can be shared among different desktop environments (DEs) like KDE and GNOME 13. It handles device-specific quirks, configuration, and makes sure that input devices like keyboards and mice do what you’d expect. libinput is very behind the scenes, in that you probably won’t interact with it directly unless you’re writing your own DE from scratch.

You might have to make sure libinput is picking up your keyboard or mouse (you can list devices libinput is currently handling via libinput list-devices), but other than that, there’s no direct way you should have to interact with it, because it’s actually a part of the program that’s running your DE (at least on Wayland).

This is in theory a nice idea (as your DE’s settings are going to be the place to configure devices), but can be a problem if your DE does not implement the configuration you want. Of course, nothing is impossible, and so there do exist hacks that can forcibly configure these settings if it comes to that14.

Summary / TL;DR

evdev is just the modern generic input interface that links raw device drivers to userspace. You can use uinput or libevdev (which wraps uinput) to create virtual devices. All devices virtual or not are managed by udev, which can react to devices appearing, and also acts as a database of current devices for downstream use. One such downstream use of udev is by libinput, which provides a unified input stack for all desktop environments.

Trivia

Other resources

Afterword

I wrote this up mainly for my benefit in attempting to write a program that will needs to meld together multiple devices (requiring uinput/libevdev to create a new virtual device), and swap between different modes of input (requiring me to understand how the udev / libinput layers interact). I’ll be making another blog post on this program soon, I hope, so please stay tuned for that.