PYTHON-EVDEV(7)

๐Ÿ“ฆ
version 1.9.3 ยท 5 versions
, in ubuntu resolute, ubuntu stonking

โœจ NAME โœจ

okay bestie, say hi to python-evdev โ€” the python-evdev Documentation. This is section 7, and it's literally the whole vibe. ๐Ÿ’–

This package provides bindings to the generic input event interface in Linux. The evdev interface serves the purpose of passing events generated in the kernel directly to userspace through character devices that are typically located in /dev/input/.

This package also comes with bindings to uinput, the userspace input subsystem. Uinput allows userspace programs to create and handle input devices that can inject events directly into the input subsystem.

In other words, python-evdev allows you to read and write input events on Linux. An event can be a key or button press, a mouse movement or a tap on a touchscreen. ๐Ÿ‘‘โœจ


โœจ FROM AN OS PACKAGE โœจ

Python-evdev has been packaged for the following distributions:

Consult the documentation of your OS package manager for installation instructions. ๐Ÿซถ


โœจ FROM SOURCE โœจ

The latest stable version of python-evdev can be installed from pypi <http://pypi.python.org/pypi/evdev>, provided that you have a compiler, pip <http://pip.readthedocs.org/en/latest/installing.html> and the Python and Linux development headers installed on your system. Installing these is distribution specific and typically falls into one of the following:

On a Debian compatible OS:

$ apt install python-dev python-pip gcc
$ apt install linux-headers-$(uname -r)

On a Redhat compatible OS:

$ dnf install python-devel python-pip gcc
$ dnf install kernel-headers-$(uname -r)

On Arch Linux and derivatives:

$ pacman -S core/linux-api-headers python-pip gcc

Once all OS dependencies are available, you may install python-evdev using pip <http://pip.readthedocs.org/en/latest/installing.html>, preferably in a [virtualenv]:

# Install globally (not recommended).
$ sudo python3 -m pip install evdev

# Install for the current user.
$ python3 -m pip install --user evdev

# Install in a virtual environment.
$ python3 -m venv abc
$ source abc/bin/activate
$ python3 -m pip install evdev

โœจ Specifying header locations โœจ

By default, the setup script will look for the input.h and input-event-codes.h [1] header files /usr/include/linux.

You may use the --evdev-headers option to the build_ext setuptools command to the location of these header files. It accepts one or more colon-separated paths. For example:

$ python setup.py build_ext \
    --evdev-headers buildroot/input.h:buildroot/input-event-codes.h \
    --include-dirs  buildroot/ \
    install  # or any other command (e.g. develop, bdist, bdist_wheel)

โœจ FROM A BINARY PACKAGE โœจ

You may choose to install a precompiled version of python-evdev from pypi. The evdev-binary <http://pypi.python.org/pypi/evdev-binary> package provides binary wheels that have been compiled on EL8 against the 4.18.0 kernel headers.

$ python3 -m pip install evdev-binary

While the evdev interface is stable, the precompiled version may not be fully compatible or expose all the features of your running kernel. For best results, it is recommended to use an OS package or to install from source. ๐Ÿ’€

[1] input-event-codes.h is found only in recent kernel versions.


โœจ QUICK START โœจ

โœจ Listing accessible event devices โœจ

>>> import evdev

>>> devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
>>> for device in devices:
...    print(device.path, device.name, device.phys)
/dev/input/event1    USB Keyboard        usb-0000:00:12.1-2/input0
/dev/input/event0    USB Optical Mouse   usb-0000:00:12.0-2/input0
Note: If you do not see any devices, ensure that your user is in the correct group (typically input) to have read/write access. ๐Ÿ™ˆ

โœจ Reading events from a device โœจ

>>> import evdev

>>> device = evdev.InputDevice('/dev/input/event1')
>>> print(device)
device /dev/input/event1, name "USB Keyboard", phys "usb-0000:00:12.1-2/input0"

>>> for event in device.read_loop():
...     if event.type == evdev.ecodes.EV_KEY:
...         print(evdev.categorize(event))
... # pressing 'a' and holding 'space'
key event at 1337016188.396030, 30 (KEY_A), down
key event at 1337016188.492033, 30 (KEY_A), up
key event at 1337016189.772129, 57 (KEY_SPACE), down
key event at 1337016190.275396, 57 (KEY_SPACE), hold
key event at 1337016190.284160, 57 (KEY_SPACE), up

โœจ Accessing event codes โœจ

The evdev.ecodes module provides reverse and forward mappings between the names and values of the event subsystem constants.

>>> from evdev import ecodes

>>> ecodes.KEY_A
... 30
>>> ecodes.ecodes['KEY_A']
... 30
>>> ecodes.KEY[30]
... 'KEY_A'
>>> ecodes.bytype[ecodes.EV_KEY][30]
... 'KEY_A'

# A single value may correspond to multiple event codes.
>>> ecodes.KEY[152]
... ['KEY_COFFEE', 'KEY_SCREENLOCK']

โœจ Listing and monitoring input devices โœจ

The python-evdev package also comes with a small command-line program for listing and monitoring input devices:

$ python -m evdev.evtest

โœจ TUTORIAL โœจ

โœจ Listing accessible event devices โœจ

>>> import evdev

>>> devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
>>> for device in devices:
>>>     print(device.path, device.name, device.phys)
/dev/input/event1    Dell Dell USB Keyboard   usb-0000:00:12.1-2/input0
/dev/input/event0    Dell USB Optical Mouse   usb-0000:00:12.0-2/input0

โœจ Listing device capabilities โœจ

>>> import evdev

>>> device = evdev.InputDevice('/dev/input/event0')
>>> print(device)
device /dev/input/event0, name "Dell USB Optical Mouse", phys "usb-0000:00:12.0-2/input0"

>>> device.capabilities()
... { 0: [0, 1, 2], 1: [272, 273, 274, 275], 2: [0, 1, 6, 8], 4: [4] }

>>> device.capabilities(verbose=True)
... { ('EV_SYN', 0): [('SYN_REPORT', 0), ('SYN_CONFIG', 1), ('SYN_MT_REPORT', 2)],
...   ('EV_KEY', 1): [('BTN_MOUSE', 272), ('BTN_RIGHT', 273), ('BTN_MIDDLE', 274), ('BTN_SIDE', 275)], ...

โœจ Listing device capabilities (devices with absolute axes) โœจ

>>> import evdev

>>> device = evdev.InputDevice('/dev/input/event7')
>>> print(device)
device /dev/input/event7, name "Wacom Bamboo 2FG 4x5 Finger", phys ""

>>> device.capabilities()
... { 1: [272, 273, 277, 278, 325, 330, 333] ,
...   3: [(0, AbsInfo(min=0, max=15360, fuzz=128, flat=0)),
...       (1, AbsInfo(min=0, max=10240, fuzz=128, flat=0))] }

>>> device.capabilities(verbose=True)
... { ('EV_KEY', 1): [('BTN_MOUSE', 272), ('BTN_RIGHT', 273), ...],
...   ('EV_ABS', 3): [(('ABS_X', 0), AbsInfo(min=0, max=15360, fuzz=128, flat=0)),
...                   (('ABS_Y', 1), AbsInfo(min=0, max=10240, fuzz=128, flat=0)),] }

>>> device.capabilities(absinfo=False)
... { 1: [272, 273, 277, 278, 325, 330, 333],
...   3: [0, 1, 47, 53, 54, 57] }

โœจ Getting and setting LED states โœจ

>>> dev.leds(verbose=True)
... [('LED_NUML', 0), ('LED_CAPSL', 1)]

>>> dev.leds()
... [0, 1]

>>> dev.set_led(ecodes.LED_NUML, 1)  # enable numlock
>>> dev.set_led(ecodes.LED_NUML, 0)  # disable numlock

โœจ Getting currently active keys โœจ

>>> dev.active_keys(verbose=True)
... [('KEY_3', 4), ('KEY_LEFTSHIFT', 42)]

>>> dev.active_keys()
... [4, 42]

โœจ Reading events โœจ

Reading events from a single device in an endless loop.

>>> from evdev import InputDevice, categorize, ecodes
>>> dev = InputDevice('/dev/input/event1')

>>> print(dev)
device /dev/input/event1, name "Dell Dell USB Keyboard", phys "usb-0000:00:12.1-2/input0"

>>> for event in dev.read_loop():
...     if event.type == ecodes.EV_KEY:
...         print(categorize(event))
... # pressing 'a' and holding 'space'
key event at 1337016188.396030, 30 (KEY_A), down
key event at 1337016188.492033, 30 (KEY_A), up
key event at 1337016189.772129, 57 (KEY_SPACE), down
key event at 1337016190.275396, 57 (KEY_SPACE), hold
key event at 1337016190.284160, 57 (KEY_SPACE), up

โœจ Reading events (using asyncio) โœจ

Note: This requires Python 3.5+ for the async/await keywords.
>>> import asyncio
>>> from evdev import InputDevice

>>> dev = InputDevice('/dev/input/event1')

>>> async def main(dev):
...     async for ev in dev.async_read_loop():
...         print(repr(ev))

>>> asyncio.run(main(dev))
InputEvent(1527363738, 348740, 4, 4, 458792)
InputEvent(1527363738, 348740, 1, 28, 0)
InputEvent(1527363738, 348740, 0, 0, 0)

โœจ Reading events from multiple devices (using select) โœจ

>>> from evdev import InputDevice
>>> from select import select

# A mapping of file descriptors (integers) to InputDevice instances.
>>> devices = map(InputDevice, ('/dev/input/event1', '/dev/input/event2'))
>>> devices = {dev.fd: dev for dev in devices}

>>> for dev in devices.values(): print(dev)
device /dev/input/event1, name "Dell Dell USB Keyboard", phys "usb-0000:00:12.1-2/input0"
device /dev/input/event2, name "Logitech USB Laser Mouse", phys "usb-0000:00:12.0-2/input0"

>>> while True:
...    r, w, x = select(devices, [], [])
...    for fd in r:
...        for event in devices[fd].read():
...            print(event)
event at 1351116708.002230, code 01, type 02, val 01
event at 1351116708.002234, code 00, type 00, val 00
event at 1351116708.782231, code 04, type 04, val 458782
event at 1351116708.782237, code 02, type 01, val 01

โœจ Reading events from multiple devices (using selectors) โœจ

This can also be achieved using the selectors module in Python 3.4:

from evdev import InputDevice
from selectors import DefaultSelector, EVENT_READ

selector = DefaultSelector()

mouse = InputDevice('/dev/input/event1')
keybd = InputDevice('/dev/input/event2')

# This works because InputDevice has a `fileno()` method.
selector.register(mouse, EVENT_READ)
selector.register(keybd, EVENT_READ)

while True:
    for key, mask in selector.select():
        device = key.fileobj
        for event in device.read():
            print(event)

โœจ Reading events from multiple devices (using asyncio) โœจ

Yet another possibility is the asyncio module from Python 3.4:

import asyncio, evdev

@asyncio.coroutine
def print_events(device):
    while True:
        events = yield from device.async_read()
        for event in events:
            print(device.path, evdev.categorize(event), sep=': ')

mouse = evdev.InputDevice('/dev/input/eventX')
keybd = evdev.InputDevice('/dev/input/eventY')
for device in mouse, keybd:
    asyncio.async(print_events(device))

loop = asyncio.get_event_loop()
loop.run_forever()

Okay bestie, and if you're on Python 3.5 or newer (which, like, you are, obviously ๐Ÿ’…) the whole async/await thing makes this even cuter. The docs live at https://docs.python.org/3/library/asyncio-task.html btw โ€” iykyk.

import asyncio, evdev

mouse = evdev.InputDevice('/dev/input/event4')
keybd = evdev.InputDevice('/dev/input/event5')

async def print_events(device):
    async for event in device.async_read_loop():
        print(device.path, evdev.categorize(event), sep=': ')

for device in mouse, keybd:
    asyncio.ensure_future(print_events(device))

loop = asyncio.get_event_loop()
loop.run_forever()

๐Ÿ”‘ Accessing evdev constants ๐Ÿ”‘

All the constant vibes live in one place, bestie. Look how easy:

>>> from evdev import ecodes

>>> ecodes.KEY_A, ecodes.ecodes['KEY_A']
... (30, 30)
>>> ecodes.KEY[30]
... 'KEY_A'
>>> ecodes.bytype[ecodes.EV_KEY][30]
... 'KEY_A'
>>> ecodes.KEY[152]  # a single value may correspond to multiple codes
... ['KEY_COFFEE', 'KEY_SCREENLOCK']

Not the KEY_COFFEE / KEY_SCREENLOCK plot twist ๐Ÿ˜ญ one number, two names, it's giving polyamory.

๐Ÿ”Ž Searching event codes by regex ๐Ÿ”Ž

You can literally regex-hunt for event codes. Main character behaviour fr fr.

>>> from evdev import util

>>> res = util.find_ecodes_by_regex(r'(ABS|KEY)_BR(AKE|EAK)')
>>> res
... {1: [411], 3: [10]}
>>> util.resolve_ecodes_dict(res)
... {('EV_KEY', 1): [('KEY_BREAK', 411)], ('EV_ABS', 3): [('ABS_BRAKE', 10)]}

๐Ÿ”’ Getting exclusive access to a device ๐Ÿ”’

>>> dev.grab()  # become the sole recipient of all incoming input events
>>> dev.ungrab()

This functionality is also available as a context manager. Slay.

>>> with dev.grab_context():
...     pass

๐Ÿงฌ Associating classes with event types ๐Ÿงฌ

>>> from evdev import categorize, event_factory, ecodes

>>> class SynEvent:
...     def __init__(self, event):
...         ...

>>> event_factory[ecodes.EV_SYN] = SynEvent

See events (anchor: #evdev.events.event_factory) for more information. ๐Ÿ’–

๐Ÿ’‰ Injecting input ๐Ÿ’‰

>>> from evdev import UInput, ecodes as e

>>> ui = UInput()

>>> # accepts only KEY_* events by default
>>> ui.write(e.EV_KEY, e.KEY_A, 1)  # KEY_A down
>>> ui.write(e.EV_KEY, e.KEY_A, 0)  # KEY_A up
>>> ui.syn()

>>> ui.close()

Heads up bestie: by default it only accepts KEY_* events. Periodt.

๐Ÿ’‰ Injecting events (using a context manager) ๐Ÿ’‰

>>> ev = InputEvent(1334414993, 274296, ecodes.EV_KEY, ecodes.KEY_A, 1)
>>> with UInput() as ui:
...    ui.write_event(ev)
...    ui.syn()

๐ŸŽ›๏ธ Specifying uinput device options ๐ŸŽ›๏ธ

Note: ecodes.EV_SYN cannot be in the cap dictionary or the device will not be created.

Like, do NOT put that in there, the device simply will not spawn. No cap. ๐Ÿซถ

>>> from evdev import UInput, AbsInfo, ecodes as e

>>> cap = {
...     e.EV_KEY : [e.KEY_A, e.KEY_B],
...     e.EV_ABS : [
...         (e.ABS_X, AbsInfo(value=0, min=0, max=255,
...                           fuzz=0, flat=0, resolution=0)),
...         (e.ABS_Y, AbsInfo(0, 0, 255, 0, 0, 0)),
...         (e.ABS_MT_POSITION_X, (0, 128, 255, 0)) ]
... }

>>> ui = UInput(cap, name='example-device', version=0x3)
>>> print(ui)
name "example-device", bus "BUS_USB", vendor "0001", product "0001", version "0003"
event types: EV_KEY EV_ABS EV_SYN

>>> print(ui.capabilities())
{0: [0, 1, 3],
 1: [30, 48],
 3: [(0,  AbsInfo(value=0, min=0, max=0,   fuzz=255, flat=0, resolution=0)),
     (1,  AbsInfo(value=0, min=0, max=0,   fuzz=255, flat=0, resolution=0)),
     (53, AbsInfo(value=0, min=0, max=255, fuzz=128, flat=0, resolution=0))]}

>>> # move mouse cursor
>>> ui.write(e.EV_ABS, e.ABS_X, 20)
>>> ui.write(e.EV_ABS, e.ABS_Y, 20)
>>> ui.syn()

๐Ÿงฒ Create uinput device with capabilities of another device ๐Ÿงฒ

Steal the whole personality of an existing device and put it on a brand new one. It's giving clone era.

>>> from evdev import UInput, InputDevice

>>> mouse = InputDevice('/dev/input/event1')
>>> keybd = '/dev/input/event2'

>>> ui = UInput.from_device(mouse, keybd, name='keyboard-mouse-device')
>>> ui.capabilities(verbose=True).keys()
dict_keys([('EV_LED', 17), ('EV_KEY', 1), ('EV_SYN', 0), ('EV_REL', 2), ('EV_MSC', 4)])

๐ŸŽฎ Create uinput device capable of receiving FF-effects ๐ŸŽฎ

Force feedback time, bestie. Rumble mode activated. ๐ŸŒˆ

import asyncio
from evdev import UInput, categorize, ecodes

cap = {
   ecodes.EV_FF:  [ecodes.FF_RUMBLE ],
   ecodes.EV_KEY: [ecodes.KEY_A, ecodes.KEY_B]
}

ui = UInput(cap, name='test-controller', version=0x3)

async def print_events(device):
    async for event in device.async_read_loop():
        print(categorize(event))

        # Wait for an EV_UINPUT event that will signal us that an
        # effect upload/erase operation is in progress.
        if event.type != ecodes.EV_UINPUT:
            continue

        if event.code == ecodes.UI_FF_UPLOAD:
            upload = device.begin_upload(event.value)
            upload.retval = 0

            print(f'[upload] effect_id: {upload.effect.id}, type: {upload.effect.type}')
            device.end_upload(upload)

        elif event.code == ecodes.UI_FF_ERASE:
            erase = device.begin_erase(event.value)
            print(f'[erase] effect_id {erase.effect_id}')

            erase.retval = 0
            device.end_erase(erase)

asyncio.ensure_future(print_events(ui))
loop = asyncio.get_event_loop()
loop.run_forever()

๐Ÿ’ฅ Injecting an FF-event into first FF-capable device found ๐Ÿ’ฅ

from evdev import ecodes, InputDevice, ff, list_devices
import time

# Find first EV_FF capable event device (that we have permissions to use).
for name in list_devices():
    dev = InputDevice(name)
    if ecodes.EV_FF in dev.capabilities():
        break

rumble = ff.Rumble(strong_magnitude=0x0000, weak_magnitude=0xffff)
effect_type = ff.EffectType(ff_rumble_effect=rumble)
duration_ms = 1000

effect = ff.Effect(
    ecodes.FF_RUMBLE, -1, 0,
    ff.Trigger(0, 0),
    ff.Replay(duration_ms, 0),
    effect_type
)

repeat_count = 1
effect_id = dev.upload_effect(effect)
dev.write(ecodes.EV_FF, effect_id, repeat_count)
time.sleep(duration_ms / 1000)
dev.erase_effect(effect_id)

๐Ÿ“ก Forwarding force-feedback from uinput to a real device ๐Ÿ“ก

import evdev
from evdev import ecodes as e

# Find first EV_FF capable event device (that we have permissions to use).
for name in evdev.list_devices():
    dev = evdev.InputDevice(name)
    if e.EV_FF in dev.capabilities():
        break
# To ensure forwarding works correctly it is important that `max_effects`
# of the uinput device is <= `dev.ff_effects_count`.
# `from_device()` will do this automatically, but in some situations you may
# want to set the `max_effects` parameter manually, such as when using `Uinput()`.
# `filtered_types` is specified as by default EV_FF events are filtered
uinput = evdev.UInput.from_device(dev, filtered_types=[e.EV_SYN])

# Keeps track of which effects have been uploaded to the device
effects = set()

for event in uinput.read_loop():

    # Handle the special uinput events
    if event.type == e.EV_UINPUT:

        if event.code == e.UI_FF_UPLOAD:
            upload = uinput.begin_upload(event.value)

            # Checks if this is a new effect
            if upload.effect.id not in effects:
                effects.add(upload.effect.id)
                # Setting id to 1 indicates that a new effect must be allocated
                upload.effect.id = -1

            dev.upload_effect(upload.effect)
            upload.retval = 0
            uinput.end_upload(upload)

        elif event.code == e.UI_FF_ERASE:
            erase = uinput.begin_erase(event.value)
            erase.retval = 0
            dev.erase_effect(erase.effect_id)
            effects.remove(erase.effect_id)
            uinput.end_erase(erase)

    # Forward writes to actual rumble device.
    elif event.type == e.EV_FF:
        dev.write(event.type, event.code, event.value)

๐ŸŒˆ API REFERENCE ๐ŸŒˆ

๐Ÿ“ฆ events ๐Ÿ“ฆ

This module provides the InputEvent class, which closely resembles the input_event struct defined in linux/input.h:

struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};

This module also defines several InputEvent sub-classes that know more about the different types of events (key, abs, rel etc). The event_factory dictionary maps event types to these classes.

Assuming you use the evdev.util.categorize() function to categorize events according to their type, adding or replacing a class for a specific event type becomes a matter of modifying event_factory.

All classes in this module have reasonable str() and repr() methods:

>>> print(event)
event at 1337197425.477827, code 04, type 04, val 458792
>>> print(repr(event))
InputEvent(1337197425L, 477827L, 4, 4, 458792L)

>>> print(key_event)
key event at 1337197425.477835, 28 (KEY_ENTER), up
>>> print(repr(key_event))
KeyEvent(InputEvent(1337197425L, 477835L, 1, 28, 0L))

๐Ÿ’œ class evdev.events.InputEvent(sec, usec, type, code, value) ๐Ÿ’œ

A generic input event. The humble beginning of everything, honestly. โœจ

  • sec: int โ€” Time in seconds since epoch at which event occurred.
  • usec: int โ€” Microsecond portion of the timestamp.
  • type: int โ€” Event type โ€” one of ecodes.EV_*.
  • code: int โ€” Event code related to the event type.
  • value: int โ€” Event value related to the event type.

And the method:

  • timestamp() -> float โ€” Return event timestamp as a float.

๐Ÿ’– class evdev.events.KeyEvent(event: InputEvent <#evdev.events.InputEvent>, allow_unknown: bool = False) ๐Ÿ’–

An event generated by a keyboard, button or other key-like devices. Your fave, probably. โŒจ๏ธ

  • key_up: Final[int] = 0
  • key_down: Final[int] = 1
  • key_hold: Final[int] = 2
  • scancode: int
  • keystate
  • keycode
  • event: InputEvent <#evdev.events.InputEvent> โ€” Reference to an InputEvent instance.

๐ŸŒธ class evdev.events.RelEvent(event: InputEvent <#evdev.events.InputEvent>) ๐ŸŒธ

A relative axis event (e.g moving the mouse 5 units to the left). Little scoot vibes. ๐Ÿฆ‹

  • event: InputEvent <#evdev.events.InputEvent> โ€” Reference to an InputEvent instance.

๐Ÿฆ‹ class evdev.events.SynEvent(event: InputEvent <#evdev.events.InputEvent>) ๐Ÿฆ‹

A synchronization event. Used as markers to separate events. Events may be separated in time or in space, such as with the multitouch protocol. It's giving "and scene". ๐ŸŽฌ

  • event: InputEvent <#evdev.events.InputEvent> โ€” Reference to an InputEvent instance.

๐ŸŒธ more evdev.events class lore

class evdev.events.AbsEvent(event: InputEvent) โ€” an absolute axis event, aka the exact coordinates of a tap on a touchscreen. it's giving "I know precisely where you poked me" energy ๐Ÿ’…

  • event: InputEvent โ€” a reference to an InputEvent instance.

evdev.events.event_factory โ€” a whole mapping, and it looks exactly like this:

{0: <class 'evdev.events.SynEvent'>, 1: <class 'evdev.events.KeyEvent'>, 2: <class 'evdev.events.RelEvent'>, 3: <class 'evdev.events.AbsEvent'>}

It maps event types to InputEvent sub-classes, and it's the thing evdev.util.categorize() uses. lowkey the matchmaker of the events world ๐Ÿซถ


๐Ÿฆ‹ eventio

class evdev.eventio.EventIO โ€” the base class for reading and writing input events, periodt. This is the blueprint that both InputDevice and UInput are built on โœจ

  • On InputDevice it used for reading user-generated events (e.g. key presses, mouse movements) and writing feedback events (e.g. leds, beeps).
  • On UInput it used for writing user-generated events (e.g. key presses, mouse movements) and reading feedback events (e.g. leds, beeps).

fileno()

Return the file descriptor to the open event device. This makes it possible to pass instances directly to select.select() and asyncore.file_dispatcher. handing out your fd like it's a friendship bracelet ๐ŸŽ€

read_loop() -> Iterator[InputEvent]

Enter an endless select.select() loop that yields input events. endless, bestie. it does not stop. it will not stop. ๐Ÿ‘‘

read_one() -> InputEvent | None

Read and return a single input event as an instance of InputEvent.

Return None if there are no pending input events. just a lil "nothing for you rn" ๐Ÿ™ˆ

read() -> Iterator[InputEvent]

Read multiple input events from device. Return a generator object that yields InputEvent instances. Raises BlockingIOError if there are no available events at the moment.

bestie that BlockingIOError is not a vibe killer, it just means nothing's ready yet, ok?

need_write()

Decorator that raises EvdevError if there is no write access to the input device. it checks your permission slip before you get to write anything ๐Ÿ“

write_event(event)

Inject an input event into the input subsystem. Events are queued until a synchronization event is received.

Parameters

  • event (InputEvent) โ€” an InputEvent instance or an object with an event attribute (KeyEvent, RelEvent etc).

Example

>>> ev = InputEvent(1334414993, 274296, ecodes.EV_KEY, ecodes.KEY_A, 1)
>>> ui.write_event(ev)

write(etype: int, code: int, value: int)

Inject an input event into the input subsystem. Events are queued until a synchronization event is received.

Parameters

  • etype โ€” event type (e.g. EV_KEY).
  • code โ€” event code (e.g. KEY_A).
  • value โ€” event value (e.g. 0 1 2 - depends on event type).

Example

>>> ui.write(e.EV_KEY, e.KEY_A, 1) # key A - down
>>> ui.write(e.EV_KEY, e.KEY_A, 0) # key A - up

syn()

Inject a SYN_REPORT event into the input subsystem. Events queued by write() will be fired. If possible, events will be merged into an 'atomic' event. it's the "ok everyone GO" moment ๐Ÿ”ฅ

close()

closes it. that's it. that's the whole method. ๐Ÿ’€

dunder corner ๐Ÿงš

  • __annotate_func__ = None
  • __annotations_cache__ = {}
  • __firstlineno__ = 16
  • __static_attributes__ = ()
  • __weakref__ โ€” list of weak references to the object

๐ŸŒˆ eventio_async

class evdev.eventio_async.EventIO โ€” the async sibling, obviously ๐ŸŒŸ

async_read_one()

Asyncio coroutine to read and return a single input event as an instance of InputEvent.

async_read()

Asyncio coroutine to read multiple input events from device. Return a generator object that yields InputEvent instances.

async_read_loop() -> ReadIterator

Return an iterator that yields input events. This iterator is compatible with the async for syntax. yes bestie, async for โ€” she's flexible like that ๐Ÿ’…

dunder corner ๐Ÿงš

  • __annotate_func__ = None
  • __annotations_cache__ = {}
  • __firstlineno__ = 56
  • __static_attributes__ = ()

close()

also closes it. no notes. ๐Ÿซถ


๐Ÿ“ device

class evdev.device.AbsInfo(value: int, min: int, max: int, fuzz: int, flat: int, resolution: int)

Absolute axis information. It's a namedtuple with absolute axis information โ€” corresponds to the input_absinfo struct:

  • value โ€” Latest reported value for the axis.

Type โ€” int

  • min โ€” Specifies minimum value for the axis.

Type โ€” int

  • max โ€” Specifies maximum value for the axis.

Type โ€” int

  • fuzz โ€” Specifies fuzz value that is used to filter noise from the event stream.

Type โ€” int

  • flat โ€” Values that are within this value will be discarded by joydev interface and reported as 0 instead.

Type โ€” int

  • resolution โ€” Specifies resolution for the values reported for the axis. Resolution for main axes (ABS_X, ABS_Y, ABS_Z) is reported in units per millimeter (units/mm), resolution for rotational axes (ABS_RX, ABS_RY, ABS_RZ) is reported in units per radian.

Type โ€” int

Note: The input core does not clamp reported values to the [minimum, maximum] limits, such task is left to userspace. so likeโ€ฆ you're on your own for clamping, bestie ๐Ÿ˜ญ
  • value: int โ€” Alias for field number 0
  • min: int โ€” Alias for field number 1
  • max: int โ€” Alias for field number 2
  • fuzz: int โ€” Alias for field number 3
  • flat: int โ€” Alias for field number 4
  • resolution: int โ€” Alias for field number 5
  • __annotate_func__()
  • __annotations_cache__ = {'flat': <class 'int'>, 'fuzz': <class 'int'>, 'max': <class 'int'>, 'min': <class 'int'>, 'resolution': <class 'int'>, 'value': <class 'int'>}
  • __classdictcell__ = <cell at 0x756928317b20: dict object>
  • __firstlineno__ = 15
  • __getnewargs__() โ€” Return self as a plain tuple. Used by copy and pickle.
  • __match_args__ = ('value', 'min', 'max', 'fuzz', 'flat', 'resolution')
  • static __new__(_cls, value: int, min: int, max: int, fuzz: int, flat: int, resolution: int) โ€” Create new instance of AbsInfo(value, min, max, fuzz, flat, resolution)
  • __orig_bases__ = (<function NamedTuple>,)
  • __replace__(**kwds) โ€” Return a new AbsInfo object replacing specified fields with new values
  • __static_attributes__ = ()

class evdev.device.KbdInfo(delay: int, repeat: int)

Keyboard repeat rate. the "how fast does this key go brrr" struct โŒจ๏ธ

  • delay โ€” Amount of time that a key must be depressed before it will start to repeat (in milliseconds).

Type โ€” int

  • repeat โ€” Keyboard repeat rate in characters per second.

Type โ€” int

  • delay: int โ€” Alias for field number 0
  • repeat: int โ€” Alias for field number 1
  • __annotate_func__()
  • __annotations_cache__ = {'delay': <class 'int'>, 'repeat': <class 'int'>}
  • __classdictcell__ = <cell at 0x756927949b70: dict object>
  • __firstlineno__ = 65
  • __getnewargs__() โ€” Return self as a plain tuple. Used by copy and pickle.
  • __match_args__ = ('delay', 'repeat')
  • static __new__(_cls, delay: int, repeat: int) โ€” Create new instance of KbdInfo(delay, repeat)
  • __orig_bases__ = (<function NamedTuple>,)
  • __replace__(**kwds) โ€” Return a new KbdInfo object replacing specified fields with new values
  • __static_attributes__ = ()

class evdev.device.DeviceInfo(bustype: int, vendor: int, product: int, version: int)

the "who even are you" identity card of a device ๐Ÿชช

  • bustype

Type โ€” int

  • vendor

Type โ€” int

  • product

Type โ€” int

  • version

Type โ€” int

  • bustype: int โ€” Alias for field number 0
  • vendor: int โ€” Alias for field number 1
  • product: int โ€” Alias for field number 2
  • version: int โ€” Alias for field number 3
  • __annotate_func__()
  • __annotations_cache__ = {'bustype': <class 'int'>, 'product': <class 'int'>, 'vendor': <class 'int'>, 'version': <class 'int'>}
  • __classdictcell__ = <cell at 0x756927949e70: dict object>
  • __firstlineno__ = 85
  • __getnewargs__() โ€” Return self as a plain tuple. Used by copy and pickle.
  • __match_args__ = ('bustype', 'vendor', 'product', 'version')
  • static __new__(_cls, bustype: int, vendor: int, product: int, version: int) โ€” Create new instance of DeviceInfo(bustype, vendor, product, version)
  • __orig_bases__ = (<function NamedTuple>,)
  • __replace__(**kwds) โ€” Return a new DeviceInfo object replacing specified fields with new values
  • __static_attributes__ = ()

class evdev.device.InputDevice(dev: _AnyStr | PathLike[_AnyStr])

A linux input device from which input events can be read. the main character, honestly ๐Ÿ‘‘

__init__(dev: _AnyStr | PathLike[_AnyStr])

Parameters

  • dev (str|bytes|PathLike) โ€” Path to input device
  • path: _AnyStr โ€” Path to input device.
  • fd: int โ€” A non-blocking file descriptor to the device file.
  • info โ€” A DeviceInfo instance.
  • name: str โ€” The name of the event device.
  • phys: str โ€” The physical topology of the device.
  • uniq: str โ€” The unique identifier of the device.
  • version: int โ€” The evdev protocol version.
  • ff_effects_count โ€” The number of force feedback effects the device can keep in its memory. rumble storage, basically ๐ŸŽฎ
  • capabilities(verbose: Literal[False] = False, absinfo: bool = True) -> Dict[int, List[int]]

okay bestie we're still deep in InputDevice land, so buckle up, there's more ๐Ÿ’…

โœจ capabilities โœจ

capabilities(verbose: Literal[True], absinfo: bool = True) ->
Dict[Tuple[str, int], List[Tuple[str, int]]]

Returns the event types your device supports, as a mapping of supported event types โžก๏ธ lists of handled event codes. literally a vibe check for what your device can even do ๐Ÿฆ‹

Example

>>> device.capabilities()
{ 1: [272, 273, 274],
  2: [0, 1, 6, 8] }

If verbose is True, event codes and types get resolved to their actual names, no cap:

{ ('EV_KEY', 1): [('BTN_MOUSE', 272),
                  ('BTN_RIGHT', 273),
                  ('BTN_MIDDLE', 273)],
  ('EV_REL', 2): [('REL_X', 0),
                  ('REL_Y', 1),
                  ('REL_HWHEEL', 6),
                  ('REL_WHEEL', 8)] }

Unknown codes or types get resolved to '?' (yes, the question mark is the shrug emoji of this API ๐Ÿ˜ญ).

If absinfo is True, the capabilities list also includes absolute axis info as AbsInfo instances:

{ 3: [ (0, AbsInfo(min=0, max=255, fuzz=0, flat=0)),
       (1, AbsInfo(min=0, max=255, fuzz=0, flat=0)) ]}

Combined with verbose, the above becomes:

{ ('EV_ABS', 3): [ (('ABS_X', 0), AbsInfo(min=0, max=255, fuzz=0, flat=0)),
                   (('ABS_Y', 1), AbsInfo(min=0, max=255, fuzz=0, flat=0)) ]}

๐ŸŒธ input_props ๐ŸŒธ

input_props(verbose: bool = False)

Get device properties and quirks, the little personality traits of your device ๐ŸŽ€

Example

>>> device.input_props()
[0, 5]

If verbose is True, input properties get resolved to their names. Unknown codes get resolved to '?':

[('INPUT_PROP_POINTER', 0), ('INPUT_PROP_POINTING_STICK', 5)]

โœจ leds โœจ

leds(verbose: bool = False)

Returns the LED keys that are currently set ๐Ÿ’ก

Example

>>> device.leds()
[0, 1, 8, 9]

If verbose is True, event codes get resolved to their names. Unknown codes get resolved to '?':

[('LED_NUML', 0), ('LED_CAPSL', 1), ('LED_MISC', 8), ('LED_MAIL', 9)]

๐Ÿ’… set_led ๐Ÿ’…

set_led(led_num: int, value: int) -> None

Set the state of the selected LED. light it up bestie โœจ

Example

>>> device.set_led(ecodes.LED_NUML, 1)

๐Ÿซถ __eq__ ๐Ÿซถ

__eq__(other)

Two devices are equal if their info attributes are equal. it's giving "same soul, same info dict" ๐Ÿ’–

__fspath__

__fspath__()

close

close() -> None

๐Ÿ‘‘ grab ๐Ÿ‘‘

grab() -> None

Grabs the input device using EVIOCGRAB โ€” other applications will be unable to receive events until the device is released. Only one process can hold a EVIOCGRAB on a device. main character energy fr fr, it's yours and nobody else's ๐Ÿ’…

Warning: Grabbing an already grabbed device will raise an OSError. (the audacity ๐Ÿ˜ญ)

๐ŸŒˆ ungrab ๐ŸŒˆ

ungrab() -> None

Release the device if it's already been grabbed (uses EVIOCGRAB). letting it go, growth ๐Ÿ’œ

Warning: Releasing an already released device will raise an OSError('Invalid argument').

๐Ÿฆ‹ grab_context ๐Ÿฆ‹

grab_context() -> Iterator[None]

A context manager for the duration of which only the current process will be able to receive events from the device. use it like a with block and let the vibes hold the grab for you โœจ

๐Ÿ”ฅ upload_effect ๐Ÿ”ฅ

upload_effect(effect: ff.Effect)

Upload a force feedback effect to a force feedback device. shake it, rattle it ๐ŸŽ€

erase_effect

erase_effect(ff_id) -> None

Erase a force effect from a force feedback device. This also stops the effect โ€” no lingering rumble, periodt ๐Ÿ’€

โญ property repeat โญ

Get or set the keyboard repeat rate (in characters per minute) and delay (in milliseconds). typing speed unlocked โŒจ๏ธ


the dunder lore (they're just chillin' here) ๐Ÿ™ˆ

__annotate_func__ = None

__annotations_cache__ = {'fd': 'int', 'name': 'str', 'path':
'_AnyStr', 'phys': 'str', 'uniq': 'str', 'version': 'int'}

__firstlineno__ = 105

__hash__ = None

__orig_bases__ = (<class 'evdev.eventio_async.EventIO'>,
typing.Generic[~_AnyStr])

__parameters__ = (~_AnyStr,)

__static_attributes__ = ('_rawcapabilities', 'fd',
'ff_effects_count', 'info', 'name', 'path', 'phys', 'uniq',
'version')

โœจ active_keys โœจ

active_keys(verbose: bool = False)

Returns the keys that are currently active โ€” who's pressing what right now ๐ŸŽน

Example

>>> device.active_keys()
[1, 42]

If verbose is True, key codes get resolved to their verbose names. Unknown codes get resolved to '?'. For example:

[('KEY_ESC', 1), ('KEY_LEFTSHIFT', 42)]

๐Ÿ’œ absinfo ๐Ÿ’œ

absinfo(axis_num: int)

Return the current AbsInfo for an input device axis.

Parameters

  • axis_num (int) โ€” EV_ABS keycode (example ecodes.ABS_X)

Example

>>> device.absinfo(ecodes.ABS_X)
AbsInfo(value=1501, min=-32768, max=32767, fuzz=0, flat=128, resolution=0)

๐ŸŽ€ set_absinfo ๐ŸŽ€

set_absinfo(axis_num: int, value=None, min=None, max=None,
fuzz=None, flat=None, resolution=None) -> None

Update AbsInfo values. Only specified values will be overwritten โ€” everything else stays exactly as it was, lowkey respectful ๐Ÿซถ

Parameters

  • axis_num (int) โ€” EV_ABS keycode (example ecodes.ABS_X)

Example

>>> device.set_absinfo(ecodes.ABS_X, min=-2000, max=2000)

You can also unpack an AbsInfo tuple, which will overwrite all values:

>>> device.set_absinfo(ecodes.ABS_Y, *AbsInfo(0, -2000, 2000, 0, 15, 0))

๐ŸŒŸ uinput ๐ŸŒŸ

๐Ÿ‘‘ class evdev.uinput.UInput ๐Ÿ‘‘

class evdev.uinput.UInput(events: Dict[int, Sequence[int]] | None =
None, name: str = 'py-evdev-uinput', vendor: int = 1, product: int = 1,
version: int = 1, bustype: int = 3, devnode: str = '/dev/uinput', phys:
str = 'py-evdev-uinput', input_props=None, max_effects=96)

A userland input device that can inject input events into the linux input subsystem. you're literally the device now, iconic โœจ

๐ŸŒˆ from_device ๐ŸŒˆ

classmethod from_device(*devices: InputDevice | str | bytes | PathLike, filtered_types:
Tuple[int] = (0, 21), **kwargs)

Create a UInput device with the capabilities of one or more input devices. copy their whole vibe, respectfully ๐Ÿ’…

Parameters

  • devices (InputDevice | str) โ€” Varargs of InputDevice instances or paths to input devices.
  • filtered_types (Tuple[event type codes]) โ€” Event types to exclude from the capabilities of the uinput device.
  • **kwargs โ€” Keyword arguments to the UInput constructor (i.e. name, vendor etc.).

๐Ÿฆ‹ __init__ ๐Ÿฆ‹

__init__(events: Dict[int, Sequence[int]] | None = None, name:
str = 'py-evdev-uinput', vendor: int = 1, product: int = 1,
version: int = 1, bustype: int = 3, devnode: str = '/dev/uinput',
phys: str = 'py-evdev-uinput', input_props=None, max_effects=96)

Parameters

  • events (dict) โ€” Dictionary of event types mapping to lists of event codes. The event types and codes that the uinput device will be able to inject โ€” defaults to all key codes.
  • name โ€” The name of the input device.
  • vendor โ€” Vendor identifier.
  • product โ€” Product identifier.
  • version โ€” Version identifier.
  • bustype โ€” Bustype identifier.
  • phys โ€” Physical path.
  • input_props โ€” Input properties and quirks.
  • max_effects โ€” Maximum simultaneous force-feedback effects.
Note: If you do not specify any events, the uinput device will be able to inject only KEY_* and BTN_* event codes. that's the default, no cap ๐Ÿ™ˆ

attributes, the whole fit check ๐Ÿ’–

  • name: str โ€” Uinput device name.
  • vendor: int โ€” Device vendor identifier.
  • product: int โ€” Device product identifier.
  • version: int โ€” Device version identifier.
  • bustype: int โ€” Device bustype โ€” e.g. BUS_USB.
  • phys: str โ€” Uinput device physical path.
  • devnode: str โ€” Uinput device node โ€” e.g. /dev/uinput/.
  • fd โ€” Write-only, non-blocking file descriptor to the uinput device node.
  • device: InputDevice โ€” An InputDevice instance for the fake input device. None if the device cannot be opened for reading and writing.

โญ capabilities โญ

capabilities(verbose: bool = False, absinfo: bool = True)

See capabilities. (yes babe, same one as above, we love a callback ๐Ÿซถ)


๐Ÿงš util ๐Ÿงš

โœจ evdev.util.list_devices โœจ

evdev.util.list_devices(input_device_dir: str | bytes | PathLike =
'/dev/input') -> List[str]

List readable character devices in input_device_dir. a whole roster of your devices ๐Ÿ“‹

evdev.util.is_device

evdev.util.is_device(fn: str | bytes | PathLike) -> bool

Check if fn is a readable and writable character device. both or nothing bestie ๐Ÿ’…

๐ŸŒธ evdev.util.categorize ๐ŸŒธ

evdev.util.categorize(event: InputEvent) ->
InputEvent | KeyEvent | RelEvent | AbsEvent | SynEvent

Categorize an event according to its type. sorting the whole squad into their little groups ๐ŸŽ€

The event_factory dictionary maps event types to sub-classes of InputEvent. If the event cannot be categorized, it is returned unmodified. no forced labels here โœจ

๐Ÿ’œ evdev.util.resolve_ecodes ๐Ÿ’œ

evdev.util.resolve_ecodes(ecode_dict, ecode_list, unknown='?')

Resolve event codes and types to their verbose names. translate the mystery numbers into actual words, slay ๐Ÿ”ฅ

Example

>>> resolve_ecodes(ecodes.BTN, [272, 273, 274])
[(['BTN_LEFT', 'BTN_MOUSE'], 272), ('BTN_RIGHT', 273), ('BTN_MIDDLE', 274)]

๐ŸŽ€ evdev.util.resolve_ecodes_dict ๐ŸŽ€

evdev.util.resolve_ecodes_dict(typecodemap, unknown='?')

Resolve event codes and types to their verbose names.

Parameters

  • typecodemap โ€” mapping of event types to lists of event codes.
  • unknown โ€” symbol to which unknown types or codes will be resolved.

Example

>>> resolve_ecodes_dict({ 1: [272, 273, 274] })
{ ('EV_KEY', 1): [('BTN_MOUSE',  272),
                  ('BTN_RIGHT',  273),
                  ('BTN_MIDDLE', 274)] }

And if your typecodemap is carrying absolute axis info (aka instances of AbsInfo), the glow-up looks like this:

>>> resolve_ecodes_dict({ 3: [(0, AbsInfo(...))] })
{ ('EV_ABS', 3L): [(('ABS_X', 0L), AbsInfo(...))] }

๐ŸŽ€ ecodes ๐ŸŽ€

okay bestie, this module is literally the dictionary of every integer constant that lives in linux/input.h and linux/input-event-codes.h. It just hands them all to you. ๐Ÿ’…

The constants it exposes:

KEY, ABS, REL, SW, MSC, LED, BTN, REP, SND, ID, EV,
BUS, SYN, FF, FF_STATUS, INPUT_PROP

And it doesn't just give you the numbers โ€” it gives you the reverse and forward mappings between those names and their values too, so you can go either direction. Watch:

>>> evdev.ecodes.KEY_A
30

>>> evdev.ecodes.ecodes['KEY_A']
30

>>> evdev.ecodes.KEY[30]
'KEY_A'

>>> evdev.ecodes.REL[0]
'REL_X'

>>> evdev.ecodes.EV[evdev.ecodes.EV_KEY]
'EV_KEY'

>>> evdev.ecodes.bytype[evdev.ecodes.EV_REL][0]
'REL_X'
๐Ÿซถ heads up queen: values in the reverse mappings can point to one or more event codes, not just a single one. so don't be surprised when you get a tuple back. for example:
>>> evdev.ecodes.FF[80]
('FF_EFFECT_MIN', 'FF_RUMBLE')

>>> evdev.ecodes.FF[81]
'FF_PERIODIC'
  • evdev.ecodes.keys โ€” {0: 'KEY_RESERVED', 1: 'KEY_ESC', 2: 'KEY_1', ...} โ€” the keys here are a combination of all the BTN and KEY codes. it's giving merged-and-thriving. โœจ
  • evdev.ecodes.ecodes โ€” {'KEY_END': 107, 'FF_RUMBLE': 80, 'KEY_KPDOT': 83, 'KEY_CNT': 768, ...}' โ€” the mapping of names โžก๏ธ values.
  • evdev.ecodes.bytype โ€” {0: {0: 'SYN_REPORT', 1: 'SYN_CONFIG', 2: 'SYN_MT_REPORT', 3: 'SYN_DROPPED'}, ...} โ€” the mapping of event types โžก๏ธ other value/name mappings. nested dicts, very main character energy. ๐Ÿ’–

๐ŸŒˆ SCOPE AND STATUS ๐ŸŒˆ

so what does this thing actually do, fr fr? Python-evdev exposes most of the more common interfaces defined in the evdev subsystem. Reading events and injecting events? well supported, and it's been tested with nearly all event types. we ate. ๐Ÿ‘‘

The basic functionality for reading and uploading force-feedback events is there, but ngl it has not been exercised sufficiently. ๐Ÿ˜ญ And a major shortcoming of the uinput wrapper is that it does not support force-feedback devices at all โ€” see issue #23 <https://github.com/gvalkov/python-evdev/pull/23>. not iconic of it.

Some characters, like : (colon), cannot be easily injected โ€” see issue #7 <https://github.com/gvalkov/python-evdev/issues/7>. Translating them into UInput events would require knowing the kernel keyboard translation table, which is beyond the scope of python-evdev. So if you need more complete or convenient input injection support, bestie, go look into these projects instead:


๐Ÿ’– CHANGELOG ๐Ÿ’–

1.9.3 (Feb 05, 2025)

  • Fix several memory leaks in input.c. ๐Ÿงน
  • Raise the minimum supported Python version to 3.9 and the setuptools version to 77.0.

1.9.2 (May 01, 2025)

  • Add the "--reproducible" build option which removes the build date and used headers from the generated ecodes.c. Example usage:
python -m build --config-setting=--build-option='build_ecodes --reproducible' -n
  • Use Generic to set precise type for InputDevice.path. โœจ

1.9.1 (Feb 22, 2025)

  • Fix fox missing UI_FF constants in generated ecodes.py. ๐ŸฆŠ
  • More type annotations.

1.9.0 (Feb 08, 2025)

  • Fix for CPATH/C_INCLUDE_PATH being ignored during build.
  • Slightly faster reading of events in device.read() and device.read_one().
  • Fix FreeBSD support. ๐Ÿฆ‹
  • Drop deprecated InputDevice.fn (use InputDevice.path instead).
  • Improve type hint coverage and add a py.typed file to the sdist.

1.8.0 (Jan 25, 2025)

  • Binary wheels are now provided by the evdev-binary <http://pypi.python.org/pypi/evdev-binary> package. The package is compiled on manylinux_2_28 against kernel 4.18.
  • The evdev.ecodes module is now generated at install time and contains only constants. This allows type checking and introspection of the evdev.ecodes module, without having to execute it first. The old module is available as evdev.ecodes_runtime. In case generation of the static ecodes.py fails, the install process falls back to using ecodes_runtime.py as ecodes.py.
  • Reverse mappings in evdev.ecodes that point to more than one value are now tuples instead of lists. For example:
>>> ecodes.KEY[153]
('KEY_DIRECTION', 'KEY_ROTATE_DISPLAY')
  • Raise the minimum supported Python version to 3.8.
  • Fix keyboard delay and repeat being swapped (#227).
  • Move the syn() convenience method from InputDevice to EventIO (#224).

1.7.1 (May 8, 2024)

  • Provide fallback value for FF_MAX_EFFECTS, which fixes the build on EL 7 (#219).
  • Add #ifdef guards around UI_GET_SYSNAME to improve kernel compatibility (#218) .
  • Wait up to two seconds for uinput devices to appear. (#215)

1.7.0 (Feb 18, 2024)

  • Respect the CPATH/C_INCLUDE_PATH environment variables during install.
  • Add the uniq address to the string representation of InputDevice.
  • Improved method for finding the device node corresponding to a uinput device (#206 <https://github.com/gvalkov/python-evdev/pull/206>).
  • Repository TLC (reformatted with ruff, fixed linting warnings, moved packaging metadata to pyproject.toml etc.). ๐Ÿงš

1.6.1 (Jan 20, 2023)

  • Fix generation of ecodes.c when the path to sys.executable contains spaces.

1.6.0 (Jul 17, 2022)

1.5.0 (Mar 24, 2022)

1.4.0 (Jan 16, 2021)

1.3.0 (Jan 12, 2020)

  • Fix build on 32bit arches with 64bit time_t
  • Add functionality to query device properties. See InputDevice.input_props and the input_props argument to Uinput.
  • KeyEvent received an allow_unknown constructor argument, which determines what will happen when an event code cannot be mapped to a keycode. The default and behavior so far has been to raise KeyError. If set to True, the keycode will be set to the event code formatted as a hex number.
  • Add InputDevice.set_absinfo() and InputDevice.absinfo().
  • Instruct the asyncio event loop to stop monitoring the fd of the input device when the device is closed.

1.2.0 (Apr 7, 2019)

1.1.2 (Sep 1, 2018)

  • Fix installation on kernels <= 4.4.
  • Fix uinput creation ignoring absinfo settings.

1.1.0 (Aug 27, 2018)

  • Add support for handling force-feedback effect uploads (many thanks to @ndreys <https://github.com/ndreys>). ๐Ÿ”ฅ
  • Fix typo preventing ff effects that need left coefficients from working.

1.0.0 (Jun 02, 2018)

  • Prevent Uinput device creation raising Objects/longobject.c:415: bad argument to internal function when a non-complete AbsInfo structure is passed. All missing AbsInfo fields are set to 0.
  • Fix Uinput device creation raising KeyError when a capability filtered by default is not present.
  • The InputDevice.fn attribute was deprecated in favor of InputDevice.path. Using the former will show a DeprecationWarning, but would otherwise continue working as before.
  • Fix InputDevice comparison raising AttributeError due to a non-existant path attribute.
  • Fix asyncio support in Python 3.5+.
  • Uploading FF effect now works both on Python 2.7 and Python 3+.
  • Remove the asyncore example from the tutorial.

0.8.1 (Mar 24, 2018)

  • Fix Python 2 compatibility issue in with Uinput.from_device.
  • Fix minor evdev.evtest formatting issue.

0.8.0 (Mar 22, 2018)

  • Fix InputDevice comparison on Python 2.
  • The device path is now considered when comparing two devices.
  • Fix UInput.from_device not correctly merging the capabilities of selected devices.
  • The list of excluded event types in UInput.from_device is now configurable. For example:
UInput.from_device(dev, filtered_types=(EV_SYN, EV_FF))

In addition, ecodes.EV_FF is now excluded by default.

  • Add a context manager for grabbing access to a device โ€” InputDevice.grab_context. For example:
with dev.grab_context():
    pass
  • Add the InputDevice.uniq attribute, which contains the unique identifier of the device. As with phys, this attribute may be empty (i.e. ''). ๐Ÿ™ˆ

0.7.0 (Jun 16, 2017)

  • InputDevice now accepts objects that support the path protocol. For example:
pth = pathlib.Path('/dev/input/event0')
dev = evdev.InputDevice(pth)
  • Support path protocol in InputDevice. This means that InputDevice instances can be passed to callers that expect an os.PathLike object. slay โœจ
  • Exceptions raised during InputDevice.async_read() (and similar) are now handled properly (i.e. an exception is set on the returned future instead of leaking that exception into the event loop) (fixes issue #67) https://github.com/gvalkov/python-evdev/issues/67. no more leaked exceptions bestie ๐Ÿ’…

0.6.4 (Oct 07, 2016) ๐ŸŽ€

0.6.3 (Oct 06, 2016) ๐Ÿฆ‹

  • Added the UInput.from_device class method, which allows a uinput device to be created with the capabilities of one or more existing input devices:
ui = UInput.from_device('/dev/input1', '/dev/input2', **constructor_kwargs)
  • Added the build_ecodes distutils command, which generates the ecodes.c extension module. The new way of overwriting the evdev header locations is:
python setup.py build \
  build_ecodes --evdev-headers path/input.h:path/input-event-codes.h \
  build_ext --include-dirs  path/ \
  install

The build* and install commands no longer have to be part of the same command-line (i.e. running install will reuse the outputs of the last build). lowkey convenient fr fr ๐Ÿ˜Œ

0.6.1 (Jun 04, 2016) ๐ŸŒธ

  • Disabled tty echoing while evtest is running. no more weird characters bestie ๐Ÿ˜ญ
  • Allowed evtest to listen to more than one device. the more the merrier ๐ŸŽ€
  • The setup.py script now allows the location of the input header files to be overwritten. For example:
python setup.py build_ext \
  --evdev-headers path/input.h:path/input-event-codes.h \
  --include-dirs  path/ \
  install

0.6.0 (Feb 14, 2016) ๐Ÿ’–

0.5.0 (Jun 16, 2015) ๐ŸŒˆ

  • Write access to the input device is no longer mandatory. Evdev will first try to open the device for reading and writing and fallback to read-only. Methods that require write access (e.g. set_led()) will raise EvdevError if the device is open only for reading. so if you're read-only, don't try to write, okay? ๐Ÿ‘‘

0.4.7 (Oct 07, 2014) ๐Ÿ“

  • Fallback to distutils if setuptools is not available.

0.4.6 (Oct 07, 2014) ๐Ÿงš

  • Reworked documentation and docstrings once more.
  • Fixed install on Python 3.4 (works around issue21121) http://bugs.python.org/issue21121.
  • Fixed ioctl() requested buffer size (thanks Jakub Wojciech Klama).

0.4.5 (Jul 06, 2014) ๐Ÿ’œ

0.4.4 (Jun 04, 2014) โญ

  • Calling InputDevice.read_one() should always return None when there is nothing to be read, even in case of a EAGAIN errno (thanks JPP).

0.4.3 (Dec 19, 2013) ๐ŸŽ€

0.4.2 (Dec 13, 2013) ๐Ÿฆ‹

  • Reworked documentation and docstrings.
  • Call InputDevice.close() from InputDevice.__del__().

0.4.1 (Jul 24, 2013) ๐ŸŒธ

  • Fixed reference counting in InputDevice.device_read(), InputDevice.device_read_many() and ioctl_capabilities().

0.4.0 (Jul 01, 2013) ๐Ÿ’–

  • Added FF_* and FF_STATUS codes to ecodes() (thanks @bgilbert) https://github.com/bgilbert.
  • Reverse event code mappings (ecodes.{KEY,FF,REL,ABS} and etc.) will now map to a list of codes, whenever a value corresponds to multiple codes:
>>> ecodes.KEY[152]
... ['KEY_COFFEE', 'KEY_SCREENLOCK']
>>> ecodes.KEY[30]
... 'KEY_A'

0.3.3 (May 29, 2013) ๐ŸŒˆ

  • Raise IOError from InputDevice.device_read() and InputDevice.device_read_many() when InputDevice.read() fails.
  • Several stability and style changes (thank you debian code reviewers).

0.3.2 (Apr 05, 2013) ๐Ÿ“

0.3.1 (Nov 23, 2012) ๐Ÿงš

  • InputDevice.read() will return an empty tuple if the device has nothing to offer (instead of segfaulting).
  • Exclude unnecessary package data in sdist and bdist.

0.3.0 (Nov 06, 2012) ๐Ÿ’œ

  • Added ability to set/get auto-repeat settings with EVIOC{SG}REP.
  • Added InputDevice.version() - the value of EVIOCGVERSION.
  • Added InputDevice.read_loop().
  • Added InputDevice.grab() and InputDevice.ungrab() - exposes EVIOCGRAB.
  • Added InputDevice.leds() - exposes EVIOCGLED.
  • Replaced DeviceInfo class with a namedtuple.
  • Prevented InputDevice.read_one() from skipping events.
  • Renamed AbsData to AbsInfo (as in struct input_absinfo).

0.2.0 (Aug 22, 2012) โญ

  • Added the ability to set arbitrary device capabilities on uinput devices (defaults to all EV_KEY ecodes).
  • Added UInput.device which is an open InputDevice to the input device that uinput 'spawns'.
  • Added UInput.capabilities() which is just a shortcut to UInput.device.capabilities().
  • Renamed UInput.write() to UInput.write_event().
  • Added a simpler UInput.write(type, code, value)() method.
  • Made all UInput() constructor arguments optional (default device name is now py-evdev-uinput).
  • Added the ability to set absmin, absmax, absfuzz and absflat when specifying the uinput device's capabilities.
  • Removed the nophys argument - if a device fails the EVIOCGPHYS ioctl, phys will equal the empty string.
  • Made InputDevice.capabilities() perform a EVIOCGABS ioctl for devices that support EV_ABS and return that info wrapped in an AbsData namedtuple.
  • Split ioctl_devinfo into ioctl_devinfo and ioctl_capabilities.
  • Split UInput.uinput_open() to UInput.uinput_open() and UInput.uinput_create()
  • Added more uinput usage examples and documentation.
  • Rewrote uinput tests.
  • Removed mouserel and mouseabs from UInput.
  • Tied the sphinx version and release to the distutils version.
  • Set 'methods-before-attributes' sorting in the docs.
  • Removed KEY_CNT and KEY_MAX from ecodes.keys().

0.1.1 (May 18, 2012) ๐ŸŽ€

  • Added events.keys, which is a combination of all BTN_ and KEY_ event codes.
  • ecodes.c was not generated when installing through pip.

0.1.0 (May 17, 2012) ๐Ÿ‘‘


๐Ÿ’– Author ๐Ÿ’–

Georgi Valkov

2012-2025, Georgi Valkov and contributors


bestie, this was PYTHON-EVDEV(7) version 1.9.3 from Mar 20, 2026. go forth and read those input events like the main character you are. periodt ๐Ÿ’…โœจ

๐Ÿ“ฆ where this page comes from