PYTHON-EVDEV(7) | python-evdev | PYTHON-EVDEV(7) |
python-evdev - python-evdev Documentation
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.
Python-evdev has been packaged for the following GNU/Linux distributions:
Consult the documentation of your OS package manager for installation instructions.
The latest stable version of python-evdev can be installed from pypi, provided that you have gcc/clang, pip and the Python and Linux development headers installed on your system. Installing them is distribution specific and typically falls in one of the following categories:
On a Debian compatible OS:
$ apt-get install python-dev python-pip gcc $ apt-get install linux-headers-$(uname -r)
On a Redhat compatible OS:
$ yum install python-devel python-pip gcc $ yum install kernel-headers-$(uname -r)
On Arch Linux and derivatives:
$ pacman -S core/linux-api-headers python-pip gcc
Once all dependencies are available, you may install python-evdev using pip:
$ sudo pip install evdev # available globally $ pip install --user evdev # available to the current user
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 specify 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)
>>> 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:
>>> 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
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']
The python-evdev package also comes with a small command-line program for listing and monitoring input devices:
$ python -m evdev.evtest
>>> 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
>>> 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)], ...
>>> 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] }
>>> 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
>>> dev.active_keys(verbose=True) ... [('KEY_3', 4), ('KEY_LEFTSHIFT', 42)] >>> dev.active_keys() ... [4, 42]
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
NOTE:
>>> import asyncio >>> from evdev import InputDevice, categorize, ecodes >>> dev = InputDevice('/dev/input/event1') >>> async def helper(dev): ... async for ev in dev.async_read_loop(): ... print(repr(ev)) >>> loop = asyncio.get_event_loop() >>> loop.run_until_complete(helper(dev)) InputEvent(1527363738, 348740, 4, 4, 458792) InputEvent(1527363738, 348740, 1, 28, 0) InputEvent(1527363738, 348740, 0, 0, 0)
>>> 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
This can also be achieved using the selectors module in Python 3.4:
from evdev import InputDevice from selectors import DefaultSelector, EVENT_READ selector = selectors.DefaultSelector() mouse = evdev.InputDevice('/dev/input/event1') keybd = evdev.InputDevice('/dev/input/event2') # This works because InputDevice has a `fileno()` method. selector.register(mouse, selectors.EVENT_READ) selector.register(keybd, selectors.EVENT_READ) while True:
for key, mask in selector.select():
device = key.fileobj
for event in device.read():
print(event)
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()
Since Python 3.5, the async/await syntax makes this even simpler:
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()
>>> 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']
>>> 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)]}
>>> dev.grab() # become the sole recipient of all incoming input events >>> dev.ungrab()
This functionality is also available as a context manager.
>>> with dev.grab_context(): ... pass
>>> from evdev import categorize, event_factory, ecodes >>> class SynEvent(object): ... def __init__(self, event): ... ... >>> event_factory[ecodes.EV_SYN] = SynEvent
See events for more information.
>>> 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()
>>> ev = InputEvent(1334414993, 274296, ecodes.EV_KEY, ecodes.KEY_A, 1) >>> with UInput() as ui: ... ui.write_event(ev) ... ui.syn()
>>> 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()
>>> 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)])
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()
from evdev import ecodes, InputDevice, ff # Find first EV_FF capable event device (that we have permissions to use). for name in evdev.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),
ff.EffectType(ff_rumble_effect=rumble) ) repeat_count = 1 effect_id = dev.upload_effect(effect) dev.write(e.EV_FF, effect_id, repeat_count) dev.erase_effect(effect_id)
Python-evdev exposes most of the more common interfaces defined in the evdev subsystem. Reading and injecting events is well supported and has been tested with nearly all event types.
The basic functionality for reading and uploading force-feedback events is there, but it has not been exercised sufficiently. A major shortcoming of the uinput wrapper is that it does not support force-feedback devices at all (see issue #23).
Some characters, such as : (colon), cannot be easily injected (see issue #7), Translating them into UInput events would require knowing the kernel keyboard translation table, which is beyond the scope of python-evdev. Please look into the following projects if you need more complete or convenient input injection support.
UInput.from_device(dev, filtered_types=(EV_SYN, EV_FF))
In addition, ecodes.EV_FF is now excluded by default.
with dev.grab_context():
pass
pth = pathlib.Path('/dev/input/event0') dev = evdev.InputDevice(pth)
ui = UInput.from_device('/dev/input1', '/dev/input2', **constructor_kwargs)
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).
python setup.py build_ext \
--evdev-headers path/input.h:path/input-event-codes.h \
--include-dirs path/ \
install
>>> ecodes.KEY[152] ... ['KEY_COFFEE', 'KEY_SCREENLOCK'] >>> ecodes.KEY[30] ... 'KEY_A'
Initial Release
This package is released under the terms of the Revised BSD License.
Georgi Valkov
2012-2021, Georgi Valkov
June 2, 2021 | 1.4.0 |