evdev: Linux Input Subsystem Bindings
evdev provides Python bindings to the generic input event interface in Linux, allowing user-space programs to read and write input events from devices like keyboards, mice, and touchscreens. It also includes bindings to uinput, enabling the creation and handling of virtual input devices to inject events directly into the kernel's input subsystem. As of February 2026, the current version is 1.9.3, and the library maintains an active development and release schedule.
Warnings
- gotcha Installing `evdev` requires Linux development headers and a C compiler (like `gcc`) to be installed on your system. Without these, `pip install evdev` will fail during compilation.
- gotcha User permissions: To read or write events, your user account must have appropriate permissions to access `/dev/input/eventX` devices. This usually means being a member of the 'input' user group.
- gotcha When installing in a virtual environment, if you encounter `error: externally-managed-environment`, `pip` is attempting to install system-wide. This often means the virtual environment is not correctly activated or configured.
- gotcha When injecting events with `UInput`, certain complex characters (e.g., ':') may not be easily translated and require explicit kernel keyboard translation table knowledge, which is beyond `evdev`'s scope.
- gotcha The `upload_effect()` method for Force Feedback (FF) effects might not write the kernel-assigned ID back to the `Effect` object, leading to silent failures or incorrect behavior if you try to re-upload or manage the effect later based on its ID.
Install
-
pip install evdev
Imports
- InputDevice
from evdev import InputDevice
- categorize
from evdev import categorize
- ecodes
from evdev import ecodes
- list_devices
from evdev import list_devices
- UInput
from evdev import UInput
Quickstart
import evdev
from evdev import InputDevice, categorize, ecodes
import os
# Listing accessible event devices
print('Listing available input devices:')
devices = [InputDevice(path) for path in evdev.list_devices()]
if not devices:
print("No input devices found. Ensure your user has read/write access to /dev/input/ and devices are connected.")
for device in devices:
print(f"{device.path}: {device.name} ({device.phys})")
# Example: Reading events from the first available keyboard-like device
# (You might need to adjust the path or device selection for your system)
keyboard_device = None
for device in devices:
# Heuristic: look for devices with EV_KEY (keyboard events)
if ecodes.EV_KEY in device.capabilities():
keyboard_device = device
break
if keyboard_device:
print(f"\nReading events from: {keyboard_device.path} - {keyboard_device.name}")
print("Press Ctrl+C to stop.")
try:
for event in keyboard_device.read_loop():
if event.type == ecodes.EV_KEY:
key_event = categorize(event)
if key_event.keystate == key_event.key_down:
print(f"Key pressed: {key_event.keycode} (Scancode: {key_event.scancode})")
elif event.type == ecodes.EV_REL:
# Example for mouse movement
if event.code == ecodes.REL_X:
print(f"Mouse X movement: {event.value}")
elif event.code == ecodes.REL_Y:
print(f"Mouse Y movement: {event.value}")
# Add more event types (EV_ABS, EV_SYN, etc.) as needed
except PermissionError:
print(f"\nPermission denied for {keyboard_device.path}. Ensure your user is in the 'input' group.")
except OSError as e:
print(f"\nError reading device: {e}. Device might have been unplugged or is inaccessible.")
except KeyboardInterrupt:
print("\nStopped reading events.")
else:
print("\nNo suitable keyboard-like input device found to demonstrate event reading.")