This is a tutorial on using libevdev’s Python wrapper. It is not a tutorial on the evdev protocol, basic knowledge of how evdev works is assumed. Specifically, you’re expected to know what an event type and an event code is. If you don’t, read https://who-t.blogspot.com.au/2016/09/understanding-evdev.html first.

The basic building blocks

The most important building blocks are the Device, InputEvent and the evbit() function. A Device object refers to any libevdev device, whether it’s backed by a real device or not. InputEvent represents an event from that device. libevdev relies on the caller using wrapped event code objects rather than just strings and numbers, evbit() converts from one to the other.

Event types and codes

In raw evdev, each event has an event type and an event code. These are just numbers with overlapping ranges, so it’s possible to mix them up. libevdev takes this away by providing objects for both types and codes. These objects are required in all API calls, so the chances of mixing things up are reduced.

All event codes and types are exposed in the libevdev module namespace:

>>> t = libevdev.EV_ABS
>>> print(t)
>>> print(t.value)
>>> print(t.name)

>>> c = libevdev.EV_ABS.ABS_X
>>> print(c)
>>> print(c.value)
>>> print(c.name)

As you can see, each type or code has a value and name attribute. Each type and code also references each other, so you can go from one to the other easily:

>>> c = libevdev.EV_ABS.ABS_X
>>> print(c.type)
>>> c.type == libevdev.EV_ABS

>>> t = libevdev.EV_ABS
>>> print(t.codes[:3])
[ABS_X:0, ABS_Y:1, ABS_Z:2]
>>> t.codes[0] == libevdev.EV_ABS.ABS_X

Converting to and from a event code

When parsing text files with device descriptions, the data is usually in the form of numeric values or text strings for each event type or event code. The evbit() function is the conversion function to get the real event types and codes from that data. Using it is easy: just pass the numbers or strings in and use the object that comes out:

>>> print(libevdev.evbit(3))
>>> libevdev.evbit(3) == libevdev.EV_ABS

>>> print(libevdev.evbit(3, 0))
>>> libevdev.evbit(3, 0) == libevdev.EV_ABS.ABS_X

For the cases where the data is strings, it works much in the same way:

>>> print(libevdev.evbit('EV_ABS'))
>>> libevdev.evbit('EV_ABS') == libevdev.EV_ABS

>>> print(libevdev.evbit('EV_ABS', 'ABS_X'))
>>> libevdev.evbit('EV_ABS', 'ABS_X') == libevdev.EV_ABS.ABS_X

>>> print(libevdev.evbit('ABS_X'))
>>> libevdev.evbit('ABS_X') == libevdev.EV_ABS.ABS_X

Most of the event code strings are unique, so as you can see in the third example above, the event type isn’t needed when converting from string.

Ok, now that we know how to deal with event codes and types, we can move on to actually using those.

Opening a device

Opening and closing devices is left to the caller. libevdev merely makes use of any file objects that it is given. It also relies on the caller to figure out when events are available on the file object - all libevdev does is use the file descriptor when asked to do so. libevdev doesn’t give you a list of devices either - you can easily figure that out yourself by looping through the file system or using libudev.

The simplest case (and good enough for most applications) is a mere call to open:

>>> fd = open("/dev/input/event0", "rb")
>>> device = libevdev.Device(fd)
>>> print(device.name)
Lid Switch

This creates a device that is backed by a file descriptior - we can read events from it later or even modify the kernel device.

That’s it. libevdev doesn’t really care how you opened the device, as long as fileno() works on it it’ll take it. Now we can move on to actually handling the device

Querying and modifying device capabilities

The has function returns True when a device has a given event type or event code. So let’s check whether this device is a mouse:

if not device.has(libevdev.EV_REL):
    print('I expected relative axes from a mouse...')

if device.has(libevdev.EV_KEY.BTN_MIDDLE):
    print('Fancy, a mouse with a middle button!')
    print('... but you do not get to use it')

The has calls are self-explanatory. The call to disable disables the given event code or event type. When disabled, no events from this code or type are forwarded to the caller and future calls to has return False.

The inverse is possible too, enabling a non-existing event code:

if not device.has(libevdev.EV_KEY.BTN_MIDDLE):
    print('Free middle buttons for everyone!')

Unsurprisingly, the physical device won’t generate events for axes it doesn’t have. Enabling event codes is generally only useful to fix device-specific quirks in one place and then assume that devices are behaving correctly.


Enabling absolute axes requires extra data. See disable for details.

Reading events

libevdev does not have a concept of an event loop, it relies on the caller to monitor the file descriptor for events. Thus, the concept of “availablable events” means “events available right now” and the events function returns exactly that:

while True:
    for e in device.events():
    # now do some other stuff, like rendering things

The events returned are of class InputEvent and represent the C struct input_event, but they’re type-safer. Every event has a type and a code representing its event type and code. And of course a value.

The most useful method on the input events is matches which can be used to compare for types, codes and/or values:

for e in device.events():
    if e.matches(libevdev.EV_MSC):
        continue  # don't care about those

    if e.matches(libevdev.EV_REL.REL_X:
        move_by(e.value, 0)
    elif e.matches(libevdev.EV_REL.REL_Y):
        move_by(0, e.value)
    elif e.matches(libevdev.EV_KEY.BTN_MIDDLE, value=1):
        printf('How did we manage to get a middle button press?')

Alternatively, you can create input events and use those for comparisons:

btn = InputEvent(libevdev.EV_KEY.BTN_MIDDLE, value=1)

if btn in device.events():
    print('There is a button event in there')

Creating uinput devices


Creating uinput devices requires root access.

Creating virtual devices through uinput is a common case for users that want to inject input events into the system. libevdev makes this easy by creating a device from an existing libevdev context:

device = libevdev.Device()
device.name = 'my fake device'

uinput = device.create_uinput_device()
print('device is now at {}'.format(uinput.devnode))

press = [libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, value=1)
         libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, value=0)]

release = [libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, value=0)
           libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, value=0)]

In the example above, we create an empty uinput device, set a name and enable a few event codes. Then we create the uinput device and write a middle click press, followed by a release.


An event sequence must always be terminated by with a libevdev.EV_SYN.SYN_REPORT event or the kernel may not process the events.

That’s really it. The uinput device can be created from any context. By using a real device as source context it’s easy to duplicate an existing device with exactly the same attributes. The resulting uinput device is a libevdev context too, so all the previously mentioned methods work on it - it just won’t ever send events. Usually you’d create a new libevdev context from the device at the uinput’s device node:

uinput = device.create_uinput_device()
fd = open(uinput.devnode, 'rb')
newdev = libevdev.Device(fd)


This tutorial provided an overview on how to initialize libevdev and handle basic properties and events. Full examples for some use-cases are available on the Examples page. The API documentation explains all functions available to the caller.