### Install precompiled binary package Source: https://python-evdev.readthedocs.io/en/latest/_sources/install.rst.txt Install the evdev-binary package for precompiled wheels, though source installation is recommended for full kernel compatibility. ```bash $ python3 -m pip install evdev-binary ``` -------------------------------- ### Install OS dependencies for Redhat Source: https://python-evdev.readthedocs.io/en/latest/install.html Install required development headers and tools on Redhat-based systems. ```bash $ dnf install python-devel python-pip gcc $ dnf install kernel-headers-$(uname -r) ``` -------------------------------- ### Install OS dependencies for Debian Source: https://python-evdev.readthedocs.io/en/latest/install.html Install required development headers and tools on Debian-based systems. ```bash $ apt install python-dev python-pip gcc $ apt install linux-headers-$(uname -r) ``` -------------------------------- ### Install OS dependencies for Arch Linux Source: https://python-evdev.readthedocs.io/en/latest/install.html Install required development headers and tools on Arch Linux. ```bash $ pacman -S core/linux-api-headers python-pip gcc ``` -------------------------------- ### Install python-evdev via pip Source: https://python-evdev.readthedocs.io/en/latest/_sources/install.rst.txt Methods for installing the package globally, for the current user, or within a virtual environment. ```bash # 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 ``` -------------------------------- ### Install OS dependencies for python-evdev Source: https://python-evdev.readthedocs.io/en/latest/_sources/install.rst.txt Commands to install necessary development headers and compilers on different Linux distributions. ```bash $ apt install python-dev python-pip gcc $ apt install linux-headers-$(uname -r) ``` ```bash $ dnf install python-devel python-pip gcc $ dnf install kernel-headers-$(uname -r) ``` ```bash $ pacman -S core/linux-api-headers python-pip gcc ``` -------------------------------- ### Get and set LED states Source: https://python-evdev.readthedocs.io/en/latest/tutorial.html Query current LED status and toggle specific LEDs on the device. ```python >>> 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 ``` -------------------------------- ### Get device properties and quirks Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Retrieves input properties. Set verbose to True to resolve property codes to names. ```python >>> device.input_props() [0, 5] ``` ```python [('INPUT_PROP_POINTER', 0), ('INPUT_PROP_POINTING_STICK', 5)] ``` -------------------------------- ### Mapping Event Names and Values Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Examples of performing forward and reverse lookups using the ecodes module. ```python >>> 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' ``` -------------------------------- ### InputEvent and KeyEvent string representation Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/events.html Examples of how InputEvent and KeyEvent objects are represented as strings and in repr format. ```python >>> 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)) ``` -------------------------------- ### Prepare events for uinput Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/uinput.html Processes event dictionaries into a format suitable for uinput setup, including flattening absolute axis information. ```python def _prepare_events(self, events): """Prepare events for passing to _uinput.enable and _uinput.setup""" absinfo, prepared_events = [], [] for etype, codes in events.items(): for code in codes: # Handle max, min, fuzz, flat. if isinstance(code, (tuple, list, AbsInfo)): # Flatten (ABS_Y, (0, 255, 0, 0, 0, 0)) to (ABS_Y, 0, 255, 0, 0, 0, 0). f = [code[0]] f.extend(code[1]) # Ensure the tuple is always 6 ints long, since uinput.c:uinput_create # does little in the way of checking the length. f.extend([0] * (6 - len(code[1]))) absinfo.append(f) code = code[0] prepared_events.append((etype, code)) return absinfo, prepared_events ``` -------------------------------- ### Get active keys Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Retrieve a list of currently pressed keys on the device. ```python >>> dev.active_keys(verbose=True) >>> dev.active_keys() ``` -------------------------------- ### Initialize InputDevice with pathlib Source: https://python-evdev.readthedocs.io/en/latest/_sources/changelog.rst.txt Demonstrates passing a pathlib.Path object to the InputDevice constructor. ```python pth = pathlib.Path('/dev/input/event0') dev = evdev.InputDevice(pth) ``` -------------------------------- ### Create uinput device with force feedback support Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Sets up a virtual device capable of receiving force feedback events, processed asynchronously. ```python 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() ``` -------------------------------- ### Build evdev with custom headers Source: https://python-evdev.readthedocs.io/en/latest/_sources/changelog.rst.txt Command-line instructions for building the extension module with custom header locations. ```bash python setup.py build \ build_ecodes --evdev-headers path/input.h:path/input-event-codes.h \ build_ext --include-dirs path/ \ install ``` ```bash python setup.py build_ext \ --evdev-headers path/input.h:path/input-event-codes.h \ --include-dirs path/ \ install ``` -------------------------------- ### Specify uinput device options Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Creates a virtual device with custom capabilities and configuration. ```python >>> 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() ``` -------------------------------- ### Configure build_ext with custom headers Source: https://python-evdev.readthedocs.io/en/latest/changelog.html Override the location of input header files during the build_ext phase of setup.py. ```bash python setup.py build_ext \ --evdev-headers path/input.h:path/input-event-codes.h \ --include-dirs path/ \ install ``` -------------------------------- ### Initialize InputDevice Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/device.html Initializes a connection to a Linux input device, opening it in non-blocking mode. ```python class InputDevice(EventIO, Generic[_AnyStr]): """ A linux input device from which input events can be read. """ __slots__ = ("path", "fd", "info", "name", "phys", "uniq", "_rawcapabilities", "version", "ff_effects_count") def __init__(self, dev: Union[_AnyStr, "os.PathLike[_AnyStr]"]): """ Arguments --------- dev : str|bytes|PathLike Path to input device """ #: Path to input device. self.path: _AnyStr = dev if not hasattr(dev, "__fspath__") else dev.__fspath__() # Certain operations are possible only when the device is opened in read-write mode. try: fd = os.open(dev, os.O_RDWR | os.O_NONBLOCK) except OSError: fd = os.open(dev, os.O_RDONLY | os.O_NONBLOCK) #: A non-blocking file descriptor to the device file. self.fd: int = fd # Returns (bustype, vendor, product, version, name, phys, capabilities). info_res = _input.ioctl_devinfo(self.fd) #: A :class:`DeviceInfo ` instance. self.info = DeviceInfo(*info_res[:4]) #: The name of the event device. self.name: str = info_res[4] #: The physical topology of the device. self.phys: str = info_res[5] #: The unique identifier of the device. self.uniq: str = info_res[6] #: The evdev protocol version. self.version: int = _input.ioctl_EVIOCGVERSION(self.fd) #: The raw dictionary of device capabilities - see `:func:capabilities()`. self._rawcapabilities = _input.ioctl_capabilities(self.fd) #: The number of force feedback effects the device can keep in its memory. self.ff_effects_count = _input.ioctl_EVIOCGEFFECTS(self.fd) ``` -------------------------------- ### InputDevice(dev) Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/device.html Initializes a new InputDevice instance by opening the specified device path. The device is opened in non-blocking mode, attempting read-write access first, then falling back to read-only. ```APIDOC ## InputDevice(dev) ### Description Initializes a connection to a Linux input device file. This class provides access to device metadata and event streams. ### Parameters - **dev** (str|bytes|PathLike) - Required - The filesystem path to the input device (e.g., '/dev/input/event0'). ### Attributes - **info** (DeviceInfo) - Metadata including bustype, vendor, product, and version. - **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** (int) - The number of force feedback effects the device supports. ``` -------------------------------- ### UInput.__init__ Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/uinput.html Initializes a new UInput device, which acts as a virtual input device capable of injecting events into the system. ```APIDOC ## UInput(events=None, name='py-evdev-uinput', vendor=0x1, product=0x1, version=0x1, bustype=0x3, devnode='/dev/uinput', phys='py-evdev-uinput', input_props=None, max_effects=96) ### Description Creates a new virtual input device. If no events are specified, the device defaults to supporting all KEY and BTN event codes. ### Parameters - **events** (dict) - Optional - Dictionary mapping event types to lists of event codes. - **name** (str) - Optional - The name of the input device. - **vendor** (int) - Optional - Vendor identifier. - **product** (int) - Optional - Product identifier. - **version** (int) - Optional - Version identifier. - **bustype** (int) - Optional - Bustype identifier. - **devnode** (str) - Optional - Path to the uinput device node. - **phys** (str) - Optional - Physical path of the device. - **input_props** (list) - Optional - Input properties and quirks. - **max_effects** (int) - Optional - Maximum simultaneous force-feedback effects. ``` -------------------------------- ### Monitor devices via CLI Source: https://python-evdev.readthedocs.io/en/latest/_sources/usage.rst.txt Executes the built-in command-line utility to list and monitor input devices. ```bash $ python -m evdev.evtest ``` -------------------------------- ### Create UInput from existing devices Source: https://python-evdev.readthedocs.io/en/latest/_sources/changelog.rst.txt Initializes a UInput device using the capabilities of one or more existing input devices. ```python ui = UInput.from_device('/dev/input1', '/dev/input2', **constructor_kwargs) ``` -------------------------------- ### Injecting raw events with write Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/eventio.html Demonstrates injecting raw event type, code, and value pairs to simulate key presses. ```python >>> ui.write(e.EV_KEY, e.KEY_A, 1) # key A - down >>> ui.write(e.EV_KEY, e.KEY_A, 0) # key A - up ``` -------------------------------- ### Create UInput from existing devices Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/uinput.html The from_device class method allows creating a new UInput device that inherits capabilities from one or more existing InputDevice instances or device paths. ```python @classmethod def from_device( cls, *devices: Union[InputDevice, Union[str, bytes, os.PathLike]], filtered_types: Tuple[int] = (ecodes.EV_SYN, ecodes.EV_FF), **kwargs, ): """ Create an UInput device with the capabilities of one or more input devices. Arguments --------- 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 UInput constructor (i.e. name, vendor etc.). """ device_instances = [] for dev in devices: if not isinstance(dev, InputDevice): dev = InputDevice(str(dev)) device_instances.append(dev) all_capabilities = defaultdict(set) if "max_effects" not in kwargs: kwargs["max_effects"] = min([dev.ff_effects_count for dev in device_instances]) # Merge the capabilities of all devices into one dictionary. for dev in device_instances: for ev_type, ev_codes in dev.capabilities().items(): all_capabilities[ev_type].update(ev_codes) for evtype in filtered_types: if evtype in all_capabilities: del all_capabilities[evtype] return cls(events=all_capabilities, **kwargs) ``` -------------------------------- ### leds(verbose=False) Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Returns the currently set LED keys. ```APIDOC ## leds(verbose=False) ### Description Return currently set LED keys. If verbose is True, event codes are resolved to their names. ``` -------------------------------- ### Use InputDevice grab context manager Source: https://python-evdev.readthedocs.io/en/latest/_sources/changelog.rst.txt Shows the usage of the context manager for grabbing access to an input device. ```python with dev.grab_context(): pass ``` -------------------------------- ### input_props(verbose=False) Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Retrieves device properties and quirks. ```APIDOC ## input_props(verbose=False) ### Description Get device properties and quirks. If verbose is True, input properties are resolved to their names. ``` -------------------------------- ### Configure UInput device filtering Source: https://python-evdev.readthedocs.io/en/latest/_sources/changelog.rst.txt Demonstrates how to specify excluded event types when creating a UInput device from an existing device. ```python UInput.from_device(dev, filtered_types=(EV_SYN, EV_FF)) ``` -------------------------------- ### evdev.uinput.UInput.from_device Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Class method to create a UInput device by cloning the capabilities of one or more existing input devices. ```APIDOC ## classmethod evdev.uinput.UInput.from_device(*devices, filtered_types=(0, 21), **kwargs) ### Description Create an UInput device with the capabilities of one or more input devices. ### Parameters - **devices** (InputDevice | str | bytes | PathLike) - Required - Varargs of InputDevice instances or paths to input devices. - **filtered_types** (Tuple[int]) - Optional - Event types to exclude from the capabilities of the uinput device. - **kwargs** (dict) - Optional - Keyword arguments to UInput constructor (e.g., name, vendor). ``` -------------------------------- ### Define UInput class and initialization Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/uinput.html The UInput class inherits from EventIO and handles the creation and configuration of virtual input devices. The constructor requires event definitions and device metadata to initialize the device node. ```python class UInput(EventIO): """ A userland input device and that can inject input events into the linux input subsystem. """ __slots__ = ( "name", "vendor", "product", "version", "bustype", "events", "devnode", "fd", "device", ) def __init__( self, events: Optional[Dict[int, Sequence[int]]] = None, name: str = "py-evdev-uinput", vendor: int = 0x1, product: int = 0x1, version: int = 0x1, bustype: int = 0x3, devnode: str = "/dev/uinput", phys: str = "py-evdev-uinput", input_props=None, # CentOS 7 has sufficiently old headers that FF_MAX_EFFECTS is not defined there, # which causes the whole module to fail loading. Fallback on a hardcoded value of # FF_MAX_EFFECTS if it is not defined in the ecodes. max_effects=ecodes.ecodes.get("FF_MAX_EFFECTS", 96), ): ``` -------------------------------- ### Injecting an InputEvent Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/eventio.html Demonstrates how to inject a pre-constructed InputEvent into the input subsystem. ```python >>> ev = InputEvent(1334414993, 274296, ecodes.EV_KEY, ecodes.KEY_A, 1) >>> ui.write_event(ev) ``` -------------------------------- ### Injecting an FF-event into an EV_FF capable device Source: https://python-evdev.readthedocs.io/en/latest/tutorial.html Finds the first device with EV_FF capabilities and triggers a rumble effect. ```python 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) ``` -------------------------------- ### Read events from multiple devices using asyncio Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Demonstrates asynchronous event reading using both legacy coroutine syntax and modern async/await patterns. ```python 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() ``` ```python 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() ``` -------------------------------- ### Handle force feedback uploads Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/uinput.html Methods for beginning and ending force feedback effect uploads. ```python def begin_upload(self, effect_id): upload = ff.UInputUpload() upload.effect_id = effect_id ret = self.dll._uinput_begin_upload(self.fd, ctypes.byref(upload)) if ret: raise UInputError("Failed to begin uinput upload: " + os.strerror(ret)) return upload def end_upload(self, upload): ret = self.dll._uinput_end_upload(self.fd, ctypes.byref(upload)) if ret: raise UInputError("Failed to end uinput upload: " + os.strerror(ret)) ``` -------------------------------- ### Manage device LEDs Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Query current LED states or set the state of a specific LED. ```python >>> device.leds() [0, 1, 8, 9] ``` ```python [('LED_NUML', 0), ('LED_CAPSL', 1), ('LED_MISC', 8), ('LED_MAIL', 9)] ``` ```python >>> device.set_led(ecodes.LED_NUML, 1) ``` -------------------------------- ### list_devices(input_device_dir='/dev/input') Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/util.html Lists all readable character devices located in the specified directory. ```APIDOC ## list_devices(input_device_dir='/dev/input') ### Description Returns a list of paths to readable character devices found in the provided directory. ### Parameters - **input_device_dir** (str, bytes, or os.PathLike) - Optional - The directory to search for input devices. Defaults to '/dev/input'. ### Returns - **List[str]** - A list of file paths for the discovered devices. ``` -------------------------------- ### List device capabilities Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Retrieve and display the capabilities of an input device, optionally using verbose mode for human-readable output. ```python >>> import evdev >>> device = evdev.InputDevice('/dev/input/event0') >>> print(device) >>> device.capabilities() >>> device.capabilities(verbose=True) ``` -------------------------------- ### Inject force feedback event into a device Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Finds a capable device and triggers a rumble effect by uploading and writing the effect. ```python 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) ``` -------------------------------- ### Build with reproducible ecodes.c Source: https://python-evdev.readthedocs.io/en/latest/changelog.html Use this command to generate ecodes.c without build dates or header paths. ```bash python -m build --config-setting=--build-option='build_ecodes --reproducible' -n ``` -------------------------------- ### Forward force feedback from uinput to physical device Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Proxies force feedback events from a virtual input device to a physical one, managing effect allocation. ```python 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) ``` -------------------------------- ### evdev.uinput.UInput Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Constructor for creating a new userland input device. Allows specifying device capabilities, identification, and physical properties. ```APIDOC ## class evdev.uinput.UInput(events=None, name='py-evdev-uinput', vendor=1, product=1, version=1, bustype=3, devnode='/dev/uinput', phys='py-evdev-uinput', input_props=None, max_effects=96) ### Description Creates a userland input device that can inject input events into the Linux input subsystem. ### Parameters - **events** (Dict[int, Sequence[int]]) - Optional - Dictionary of event types mapping to lists of event codes. - **name** (str) - Optional - The name of the input device. - **vendor** (int) - Optional - Vendor identifier. - **product** (int) - Optional - Product identifier. - **version** (int) - Optional - Version identifier. - **bustype** (int) - Optional - Bustype identifier. - **devnode** (str) - Optional - Uinput device node path. - **phys** (str) - Optional - Physical path. - **input_props** (Any) - Optional - Input properties and quirks. - **max_effects** (int) - Optional - Maximum simultaneous force-feedback effects. ``` -------------------------------- ### Build with reproducible ecodes.c Source: https://python-evdev.readthedocs.io/en/latest/_sources/changelog.rst.txt Use the --reproducible flag during build to exclude build-specific metadata from the generated ecodes.c file. ```bash python -m build --config-setting=--build-option='build_ecodes --reproducible' -n ``` -------------------------------- ### Specify custom header locations during build Source: https://python-evdev.readthedocs.io/en/latest/_sources/install.rst.txt Use the --evdev-headers option to point to specific Linux header files if they are not in the default location. ```bash $ 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) ``` -------------------------------- ### capabilities(verbose=False, absinfo=True) Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Returns the event types that the device supports as a mapping of event types to lists of handled event codes. ```APIDOC ## capabilities(verbose=False, absinfo=True) ### Description Returns the event types that this device supports as a mapping of supported event types to lists of handled event codes. If verbose is True, event codes and types are resolved to their names. If absinfo is True, absolute axis information is included. ### Parameters - **verbose** (Literal[False, True]) - Optional - If True, resolves codes and types to names. - **absinfo** (bool) - Optional - If True, includes AbsInfo instances for absolute axes. ``` -------------------------------- ### read() Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Reads multiple input events from the device. ```APIDOC ## read() ### Description 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. ``` -------------------------------- ### Read events from multiple devices with select Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Monitor multiple input devices simultaneously using the select system call. ```python >>> 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) >>> while True: ... r, w, x = select(devices, [], []) ... for fd in r: ... for event in devices[fd].read(): ... print(event) ``` -------------------------------- ### syn() Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Injects a synchronization event to fire queued events. ```APIDOC ## syn() ### Description Inject a SYN_REPORT event into the input subsystem. Events queued by write() will be fired. ``` -------------------------------- ### Retrieve device capabilities Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Returns supported event types and codes. Use the verbose parameter to resolve codes to their human-readable names. ```python >>> device.capabilities() { 1: [272, 273, 274], 2: [0, 1, 6, 8] } ``` ```python { ('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)] } ``` ```python { 3: [ (0, AbsInfo(min=0, max=255, fuzz=0, flat=0)), (1, AbsInfo(min=0, max=255, fuzz=0, flat=0)) ]} ``` ```python { ('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)) ]} ``` -------------------------------- ### Accessing Reverse Event Code Mappings Source: https://python-evdev.readthedocs.io/en/latest/_sources/changelog.rst.txt Demonstrates how event codes map to lists or strings when a value corresponds to multiple codes. ```python >>> ecodes.KEY[152] ... ['KEY_COFFEE', 'KEY_SCREENLOCK'] >>> ecodes.KEY[30] ... 'KEY_A' ``` -------------------------------- ### Build evdev with custom header locations Source: https://python-evdev.readthedocs.io/en/latest/changelog.html Use the build_ecodes command to specify custom paths for input header files during the build process. ```bash python setup.py build \ build_ecodes --evdev-headers path/input.h:path/input-event-codes.h \ build_ext --include-dirs path/ \ install ``` -------------------------------- ### Manage uinput device lifecycle Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/uinput.html Context manager methods for handling the lifecycle of a uinput device. ```python def __enter__(self): return self def __exit__(self, type, value, tb): if hasattr(self, "fd"): self.close() ``` -------------------------------- ### Handling Multiple Event Code Mappings Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Demonstrates that reverse mappings can return multiple values for a single integer constant. ```python >>> evdev.ecodes.FF[80] ('FF_EFFECT_MIN', 'FF_RUMBLE') >>> evdev.ecodes.FF[81] 'FF_PERIODIC' ``` -------------------------------- ### fileno() Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Returns the file descriptor for the open event device, allowing integration with select.select(). ```APIDOC ## fileno() ### Description Return the file descriptor to the open event device. This makes it possible to pass instances directly to select.select() and asyncore.file_dispatcher. ``` -------------------------------- ### List accessible input devices Source: https://python-evdev.readthedocs.io/en/latest/_sources/usage.rst.txt Iterates through all available input devices and prints their path, name, and physical location. ```python >>> 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 ``` -------------------------------- ### evdev.device.InputDevice Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Represents a Linux input device and provides access to its properties. ```APIDOC ## __init__(dev) ### Description Initializes an InputDevice instance for a specific device path. ### Parameters - **dev** (str | bytes | PathLike) - Required - Path to input device ``` -------------------------------- ### grab_context() Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/device.html A context manager that ensures only the current process receives events from the device for the duration of the block. ```APIDOC ## grab_context() ### Description A context manager for the duration of which only the current process will be able to receive events from the device. ### Signature `grab_context(self) -> Iterator[None]` ``` -------------------------------- ### Inject input events Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Writes events to a virtual input device. ```python >>> 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() ``` ```python >>> ev = InputEvent(1334414993, 274296, ecodes.EV_KEY, ecodes.KEY_A, 1) >>> with UInput() as ui: ... ui.write_event(ev) ... ui.syn() ``` -------------------------------- ### Access event codes Source: https://python-evdev.readthedocs.io/en/latest/_sources/usage.rst.txt Demonstrates mapping between event names and their integer values using the evdev.ecodes module. ```python >>> 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'] ``` -------------------------------- ### Access device capabilities Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/uinput.html Retrieves the capabilities of the underlying input device. ```python def capabilities(self, verbose: bool = False, absinfo: bool = True): """See :func:`capabilities `.""" if self.device is None: raise UInputError("input device not opened - cannot read capabilities") return self.device.capabilities(verbose, absinfo) ``` -------------------------------- ### Manage LED states Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Query current LED states or set specific LED values using ecodes. ```python >>> dev.leds(verbose=True) >>> dev.leds() >>> dev.set_led(ecodes.LED_NUML, 1) # enable numlock >>> dev.set_led(ecodes.LED_NUML, 0) # disable numlock ``` -------------------------------- ### Injecting events using a context manager Source: https://python-evdev.readthedocs.io/en/latest/tutorial.html Uses a context manager to handle the lifecycle of a UInput device while injecting an InputEvent. ```python >>> ev = InputEvent(1334414993, 274296, ecodes.EV_KEY, ecodes.KEY_A, 1) >>> with UInput() as ui: ... ui.write_event(ev) ... ui.syn() ``` -------------------------------- ### Linux-specific device discovery Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/uinput.html Finds the device node on Linux systems by inspecting sysfs and waiting for device availability. ```python def _find_device_linux(self, sysname: str) -> InputDevice: """ Tries to find the device node when running on Linux. """ syspath = f"/sys/devices/virtual/input/{sysname}" # The sysfs entry for event devices should contain exactly one folder # whose name matches the format "event[0-9]+". It is then assumed that # the device node in /dev/input uses the same name. regex = re.compile("event[0-9]+") for entry in os.listdir(syspath): if regex.fullmatch(entry): device_path = f"/dev/input/{entry}" break else: # no break raise FileNotFoundError() # It is possible that there is some delay before /dev/input/event* shows # up on old systems that do not use devtmpfs, so if the device cannot be # found, wait for a short amount and then try again once. # # Furthermore, even if devtmpfs is in use, it is possible that the device # does show up immediately, but without the correct permissions that # still need to be set by udev. Wait for up to two seconds for either the # device to show up or the permissions to be set. for attempt in range(19): try: return InputDevice(device_path) except (FileNotFoundError, PermissionError): time.sleep(0.1) # Last attempt. If this fails, whatever exception the last attempt raises # shall be the exception that this function raises. return InputDevice(device_path) ``` -------------------------------- ### Read events from a device Source: https://python-evdev.readthedocs.io/en/latest/_sources/usage.rst.txt Opens a specific input device and enters a loop to process and categorize incoming events. ```python >>> 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 ``` -------------------------------- ### Read events with asyncio Source: https://python-evdev.readthedocs.io/en/latest/_sources/tutorial.rst.txt Asynchronously read events from a device using async_read_loop. Requires Python 3.5+. ```python >>> 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)) ``` -------------------------------- ### evdev.eventio_async.EventIO Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Methods for reading input events asynchronously from a device. ```APIDOC ## async_read_one() ### Description Asyncio coroutine to read and return a single input event as an instance of InputEvent. ## async_read() ### Description Asyncio coroutine to read multiple input events from device. Return a generator object that yields InputEvent instances. ## async_read_loop() ### Description Return an iterator that yields input events. This iterator is compatible with the async for syntax. ``` -------------------------------- ### write(etype, code, value) Source: https://python-evdev.readthedocs.io/en/latest/apidoc.html Injects an input event using type, code, and value parameters. ```APIDOC ## write(etype, code, value) ### Description Inject an input event into the input subsystem. Events are queued until a synchronization event is received. ### Parameters - **etype** (int) - Required - Event type (e.g. EV_KEY). - **code** (int) - Required - Event code (e.g. KEY_A). - **value** (int) - Required - Event value (e.g. 0, 1, 2). ``` -------------------------------- ### Represent and stringify uinput device Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/uinput.html Methods to provide string representations of the uinput device instance. ```python def __repr__(self): # TODO: v = (repr(getattr(self, i)) for i in ("name", "bustype", "vendor", "product", "version", "phys")) return "{}({})".format(self.__class__.__name__, ", ".join(v)) def __str__(self): msg = 'name "{}", bus "{}", vendor "{:04x}", product "{:04x}", version "{:04x}", phys "{}"\nevent types: {}' evtypes = [i[0] for i in self.capabilities(True).keys()] msg = msg.format( self.name, ecodes.BUS[self.bustype], self.vendor, self.product, self.version, self.phys, " ".join(evtypes) ) return msg ``` -------------------------------- ### Asynchronous EventIO Implementation Source: https://python-evdev.readthedocs.io/en/latest/_modules/evdev/eventio_async.html The core implementation of the EventIO class and ReadIterator for handling asynchronous input events using asyncio. ```python import asyncio import select import sys from . import eventio from .events import InputEvent # needed for compatibility from .eventio import EvdevError if sys.version_info >= (3, 11): from typing import Self else: from typing import Any as Self class ReadIterator: def __init__(self, device): self.current_batch = iter(()) self.device = device # Standard iterator protocol. def __iter__(self) -> Self: return self def __next__(self) -> InputEvent: try: # Read from the previous batch of events. return next(self.current_batch) except StopIteration: r, w, x = select.select([self.device.fd], [], []) self.current_batch = self.device.read() return next(self.current_batch) def __aiter__(self) -> Self: return self def __anext__(self) -> "asyncio.Future[InputEvent]": future = asyncio.get_running_loop().create_future() try: # Read from the previous batch of events. future.set_result(next(self.current_batch)) except StopIteration: def next_batch_ready(batch): try: self.current_batch = batch.result() future.set_result(next(self.current_batch)) except Exception as e: future.set_exception(e) self.device.async_read().add_done_callback(next_batch_ready) return future [docs] class EventIO(eventio.EventIO): # The event loop a reader was last registered on, or None if no async read # has been awaited yet. Set in _do_when_readable, used by close(). _loop: "asyncio.AbstractEventLoop | None" = None def _do_when_readable(self, callback): # Remember the loop the reader is registered on so that close() can # remove it later, even when called without a running event loop. loop = asyncio.get_running_loop() self._loop = loop def ready(): loop.remove_reader(self.fileno()) callback() loop.add_reader(self.fileno(), ready) def _set_result(self, future, cb): try: future.set_result(cb()) except Exception as error: future.set_exception(error) [docs] def async_read_one(self): """ Asyncio coroutine to read and return a single input event as an instance of :class:`InputEvent `. """ future = asyncio.get_running_loop().create_future() self._do_when_readable(lambda: self._set_result(future, self.read_one)) return future [docs] def async_read(self): """ Asyncio coroutine to read multiple input events from device. Return a generator object that yields :class:`InputEvent ` instances. """ future = asyncio.get_running_loop().create_future() self._do_when_readable(lambda: self._set_result(future, self.read)) return future [docs] def async_read_loop(self) -> ReadIterator: """ Return an iterator that yields input events. This iterator is compatible with the ``async for`` syntax. """ return ReadIterator(self) [docs] def close(self): # A reader is only registered once an async read has been awaited, in # which case _do_when_readable recorded the loop it was added to. loop = self._loop if loop is None or loop.is_closed(): # No reader was ever registered, or its loop is already gone, so # there is nothing to remove the reader from. return loop.remove_reader(self.fileno()) ``` -------------------------------- ### Read events with asyncio (Python 3.4) Source: https://python-evdev.readthedocs.io/en/latest/tutorial.html Utilizes the asyncio coroutine syntax to handle multiple devices concurrently. ```python 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() ```