evdev-binary 1.9.3__cp314-cp314t-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- evdev/__init__.py +39 -0
- evdev/_ecodes.cpython-314t-x86_64-linux-musl.so +0 -0
- evdev/_input.cpython-314t-x86_64-linux-musl.so +0 -0
- evdev/_uinput.cpython-314t-x86_64-linux-musl.so +0 -0
- evdev/device.py +440 -0
- evdev/ecodes.py +3885 -0
- evdev/ecodes_runtime.py +111 -0
- evdev/eventio.py +152 -0
- evdev/eventio_async.py +106 -0
- evdev/events.py +192 -0
- evdev/evtest.py +181 -0
- evdev/ff.py +198 -0
- evdev/genecodes_c.py +147 -0
- evdev/genecodes_py.py +54 -0
- evdev/input.c +580 -0
- evdev/py.typed +0 -0
- evdev/uinput.c +417 -0
- evdev/uinput.py +375 -0
- evdev/util.py +146 -0
- evdev_binary-1.9.3.dist-info/METADATA +48 -0
- evdev_binary-1.9.3.dist-info/RECORD +24 -0
- evdev_binary-1.9.3.dist-info/WHEEL +5 -0
- evdev_binary-1.9.3.dist-info/licenses/LICENSE +29 -0
- evdev_binary-1.9.3.dist-info/top_level.txt +1 -0
evdev/uinput.py
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import os
|
|
3
|
+
import platform
|
|
4
|
+
import re
|
|
5
|
+
import stat
|
|
6
|
+
import time
|
|
7
|
+
from collections import defaultdict
|
|
8
|
+
from typing import Union, Tuple, Dict, Sequence, Optional
|
|
9
|
+
|
|
10
|
+
from . import _uinput, ecodes, ff, util
|
|
11
|
+
from .device import InputDevice, AbsInfo
|
|
12
|
+
from .events import InputEvent
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from evdev.eventio_async import EventIO
|
|
16
|
+
except ImportError:
|
|
17
|
+
from evdev.eventio import EventIO
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class UInputError(Exception):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class UInput(EventIO):
|
|
25
|
+
"""
|
|
26
|
+
A userland input device and that can inject input events into the
|
|
27
|
+
linux input subsystem.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
__slots__ = (
|
|
31
|
+
"name",
|
|
32
|
+
"vendor",
|
|
33
|
+
"product",
|
|
34
|
+
"version",
|
|
35
|
+
"bustype",
|
|
36
|
+
"events",
|
|
37
|
+
"devnode",
|
|
38
|
+
"fd",
|
|
39
|
+
"device",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def from_device(
|
|
44
|
+
cls,
|
|
45
|
+
*devices: Union[InputDevice, Union[str, bytes, os.PathLike]],
|
|
46
|
+
filtered_types: Tuple[int] = (ecodes.EV_SYN, ecodes.EV_FF),
|
|
47
|
+
**kwargs,
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
Create an UInput device with the capabilities of one or more input
|
|
51
|
+
devices.
|
|
52
|
+
|
|
53
|
+
Arguments
|
|
54
|
+
---------
|
|
55
|
+
devices : InputDevice|str
|
|
56
|
+
Varargs of InputDevice instances or paths to input devices.
|
|
57
|
+
|
|
58
|
+
filtered_types : Tuple[event type codes]
|
|
59
|
+
Event types to exclude from the capabilities of the uinput device.
|
|
60
|
+
|
|
61
|
+
**kwargs
|
|
62
|
+
Keyword arguments to UInput constructor (i.e. name, vendor etc.).
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
device_instances = []
|
|
66
|
+
for dev in devices:
|
|
67
|
+
if not isinstance(dev, InputDevice):
|
|
68
|
+
dev = InputDevice(str(dev))
|
|
69
|
+
device_instances.append(dev)
|
|
70
|
+
|
|
71
|
+
all_capabilities = defaultdict(set)
|
|
72
|
+
|
|
73
|
+
if "max_effects" not in kwargs:
|
|
74
|
+
kwargs["max_effects"] = min([dev.ff_effects_count for dev in device_instances])
|
|
75
|
+
|
|
76
|
+
# Merge the capabilities of all devices into one dictionary.
|
|
77
|
+
for dev in device_instances:
|
|
78
|
+
for ev_type, ev_codes in dev.capabilities().items():
|
|
79
|
+
all_capabilities[ev_type].update(ev_codes)
|
|
80
|
+
|
|
81
|
+
for evtype in filtered_types:
|
|
82
|
+
if evtype in all_capabilities:
|
|
83
|
+
del all_capabilities[evtype]
|
|
84
|
+
|
|
85
|
+
return cls(events=all_capabilities, **kwargs)
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
events: Optional[Dict[int, Sequence[int]]] = None,
|
|
90
|
+
name: str = "py-evdev-uinput",
|
|
91
|
+
vendor: int = 0x1,
|
|
92
|
+
product: int = 0x1,
|
|
93
|
+
version: int = 0x1,
|
|
94
|
+
bustype: int = 0x3,
|
|
95
|
+
devnode: str = "/dev/uinput",
|
|
96
|
+
phys: str = "py-evdev-uinput",
|
|
97
|
+
input_props=None,
|
|
98
|
+
# CentOS 7 has sufficiently old headers that FF_MAX_EFFECTS is not defined there,
|
|
99
|
+
# which causes the whole module to fail loading. Fallback on a hardcoded value of
|
|
100
|
+
# FF_MAX_EFFECTS if it is not defined in the ecodes.
|
|
101
|
+
max_effects=ecodes.ecodes.get("FF_MAX_EFFECTS", 96),
|
|
102
|
+
):
|
|
103
|
+
"""
|
|
104
|
+
Arguments
|
|
105
|
+
---------
|
|
106
|
+
events : dict
|
|
107
|
+
Dictionary of event types mapping to lists of event codes. The
|
|
108
|
+
event types and codes that the uinput device will be able to
|
|
109
|
+
inject - defaults to all key codes.
|
|
110
|
+
|
|
111
|
+
name
|
|
112
|
+
The name of the input device.
|
|
113
|
+
|
|
114
|
+
vendor
|
|
115
|
+
Vendor identifier.
|
|
116
|
+
|
|
117
|
+
product
|
|
118
|
+
Product identifier.
|
|
119
|
+
|
|
120
|
+
version
|
|
121
|
+
Version identifier.
|
|
122
|
+
|
|
123
|
+
bustype
|
|
124
|
+
Bustype identifier.
|
|
125
|
+
|
|
126
|
+
phys
|
|
127
|
+
Physical path.
|
|
128
|
+
|
|
129
|
+
input_props
|
|
130
|
+
Input properties and quirks.
|
|
131
|
+
|
|
132
|
+
max_effects
|
|
133
|
+
Maximum simultaneous force-feedback effects.
|
|
134
|
+
|
|
135
|
+
Note
|
|
136
|
+
----
|
|
137
|
+
If you do not specify any events, the uinput device will be able
|
|
138
|
+
to inject only ``KEY_*`` and ``BTN_*`` event codes.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
self.name: str = name #: Uinput device name.
|
|
142
|
+
self.vendor: int = vendor #: Device vendor identifier.
|
|
143
|
+
self.product: int = product #: Device product identifier.
|
|
144
|
+
self.version: int = version #: Device version identifier.
|
|
145
|
+
self.bustype: int = bustype #: Device bustype - e.g. ``BUS_USB``.
|
|
146
|
+
self.phys: str = phys #: Uinput device physical path.
|
|
147
|
+
self.devnode: str = devnode #: Uinput device node - e.g. ``/dev/uinput/``.
|
|
148
|
+
|
|
149
|
+
if not events:
|
|
150
|
+
events = {ecodes.EV_KEY: ecodes.keys.keys()}
|
|
151
|
+
|
|
152
|
+
self._verify()
|
|
153
|
+
|
|
154
|
+
#: Write-only, non-blocking file descriptor to the uinput device node.
|
|
155
|
+
self.fd = _uinput.open(devnode)
|
|
156
|
+
|
|
157
|
+
# Prepare the list of events for passing to _uinput.enable and _uinput.setup.
|
|
158
|
+
absinfo, prepared_events = self._prepare_events(events)
|
|
159
|
+
|
|
160
|
+
# Set phys name
|
|
161
|
+
_uinput.set_phys(self.fd, phys)
|
|
162
|
+
|
|
163
|
+
# Set properties
|
|
164
|
+
input_props = input_props or []
|
|
165
|
+
for prop in input_props:
|
|
166
|
+
_uinput.set_prop(self.fd, prop)
|
|
167
|
+
|
|
168
|
+
for etype, code in prepared_events:
|
|
169
|
+
_uinput.enable(self.fd, etype, code)
|
|
170
|
+
|
|
171
|
+
_uinput.setup(self.fd, name, vendor, product, version, bustype, absinfo, max_effects)
|
|
172
|
+
|
|
173
|
+
# Create the uinput device.
|
|
174
|
+
_uinput.create(self.fd)
|
|
175
|
+
|
|
176
|
+
self.dll = ctypes.CDLL(_uinput.__file__)
|
|
177
|
+
self.dll._uinput_begin_upload.restype = ctypes.c_int
|
|
178
|
+
self.dll._uinput_end_upload.restype = ctypes.c_int
|
|
179
|
+
|
|
180
|
+
#: An :class:`InputDevice <evdev.device.InputDevice>` instance
|
|
181
|
+
#: for the fake input device. ``None`` if the device cannot be
|
|
182
|
+
#: opened for reading and writing.
|
|
183
|
+
self.device: InputDevice = self._find_device(self.fd)
|
|
184
|
+
|
|
185
|
+
def _prepare_events(self, events):
|
|
186
|
+
"""Prepare events for passing to _uinput.enable and _uinput.setup"""
|
|
187
|
+
absinfo, prepared_events = [], []
|
|
188
|
+
for etype, codes in events.items():
|
|
189
|
+
for code in codes:
|
|
190
|
+
# Handle max, min, fuzz, flat.
|
|
191
|
+
if isinstance(code, (tuple, list, AbsInfo)):
|
|
192
|
+
# Flatten (ABS_Y, (0, 255, 0, 0, 0, 0)) to (ABS_Y, 0, 255, 0, 0, 0, 0).
|
|
193
|
+
f = [code[0]]
|
|
194
|
+
f.extend(code[1])
|
|
195
|
+
# Ensure the tuple is always 6 ints long, since uinput.c:uinput_create
|
|
196
|
+
# does little in the way of checking the length.
|
|
197
|
+
f.extend([0] * (6 - len(code[1])))
|
|
198
|
+
absinfo.append(f)
|
|
199
|
+
code = code[0]
|
|
200
|
+
prepared_events.append((etype, code))
|
|
201
|
+
return absinfo, prepared_events
|
|
202
|
+
|
|
203
|
+
def __enter__(self):
|
|
204
|
+
return self
|
|
205
|
+
|
|
206
|
+
def __exit__(self, type, value, tb):
|
|
207
|
+
if hasattr(self, "fd"):
|
|
208
|
+
self.close()
|
|
209
|
+
|
|
210
|
+
def __repr__(self):
|
|
211
|
+
# TODO:
|
|
212
|
+
v = (repr(getattr(self, i)) for i in ("name", "bustype", "vendor", "product", "version", "phys"))
|
|
213
|
+
return "{}({})".format(self.__class__.__name__, ", ".join(v))
|
|
214
|
+
|
|
215
|
+
def __str__(self):
|
|
216
|
+
msg = 'name "{}", bus "{}", vendor "{:04x}", product "{:04x}", version "{:04x}", phys "{}"\nevent types: {}'
|
|
217
|
+
|
|
218
|
+
evtypes = [i[0] for i in self.capabilities(True).keys()]
|
|
219
|
+
msg = msg.format(
|
|
220
|
+
self.name, ecodes.BUS[self.bustype], self.vendor, self.product, self.version, self.phys, " ".join(evtypes)
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
return msg
|
|
224
|
+
|
|
225
|
+
def close(self):
|
|
226
|
+
# Close the associated InputDevice, if it was previously opened.
|
|
227
|
+
if self.device is not None:
|
|
228
|
+
self.device.close()
|
|
229
|
+
|
|
230
|
+
# Destroy the uinput device.
|
|
231
|
+
if self.fd > -1:
|
|
232
|
+
_uinput.close(self.fd)
|
|
233
|
+
self.fd = -1
|
|
234
|
+
|
|
235
|
+
def capabilities(self, verbose: bool = False, absinfo: bool = True):
|
|
236
|
+
"""See :func:`capabilities <evdev.device.InputDevice.capabilities>`."""
|
|
237
|
+
if self.device is None:
|
|
238
|
+
raise UInputError("input device not opened - cannot read capabilities")
|
|
239
|
+
|
|
240
|
+
return self.device.capabilities(verbose, absinfo)
|
|
241
|
+
|
|
242
|
+
def begin_upload(self, effect_id):
|
|
243
|
+
upload = ff.UInputUpload()
|
|
244
|
+
upload.effect_id = effect_id
|
|
245
|
+
|
|
246
|
+
ret = self.dll._uinput_begin_upload(self.fd, ctypes.byref(upload))
|
|
247
|
+
if ret:
|
|
248
|
+
raise UInputError("Failed to begin uinput upload: " + os.strerror(ret))
|
|
249
|
+
|
|
250
|
+
return upload
|
|
251
|
+
|
|
252
|
+
def end_upload(self, upload):
|
|
253
|
+
ret = self.dll._uinput_end_upload(self.fd, ctypes.byref(upload))
|
|
254
|
+
if ret:
|
|
255
|
+
raise UInputError("Failed to end uinput upload: " + os.strerror(ret))
|
|
256
|
+
|
|
257
|
+
def begin_erase(self, effect_id):
|
|
258
|
+
erase = ff.UInputErase()
|
|
259
|
+
erase.effect_id = effect_id
|
|
260
|
+
|
|
261
|
+
ret = self.dll._uinput_begin_erase(self.fd, ctypes.byref(erase))
|
|
262
|
+
if ret:
|
|
263
|
+
raise UInputError("Failed to begin uinput erase: " + os.strerror(ret))
|
|
264
|
+
return erase
|
|
265
|
+
|
|
266
|
+
def end_erase(self, erase):
|
|
267
|
+
ret = self.dll._uinput_end_erase(self.fd, ctypes.byref(erase))
|
|
268
|
+
if ret:
|
|
269
|
+
raise UInputError("Failed to end uinput erase: " + os.strerror(ret))
|
|
270
|
+
|
|
271
|
+
def _verify(self):
|
|
272
|
+
"""
|
|
273
|
+
Verify that an uinput device exists and is readable and writable
|
|
274
|
+
by the current process.
|
|
275
|
+
"""
|
|
276
|
+
try:
|
|
277
|
+
m = os.stat(self.devnode)[stat.ST_MODE]
|
|
278
|
+
assert stat.S_ISCHR(m)
|
|
279
|
+
except (IndexError, OSError, AssertionError):
|
|
280
|
+
msg = '"{}" does not exist or is not a character device file - verify that the uinput module is loaded'
|
|
281
|
+
raise UInputError(msg.format(self.devnode))
|
|
282
|
+
|
|
283
|
+
if not os.access(self.devnode, os.W_OK):
|
|
284
|
+
msg = '"{}" cannot be opened for writing'
|
|
285
|
+
raise UInputError(msg.format(self.devnode))
|
|
286
|
+
|
|
287
|
+
if len(self.name) > _uinput.maxnamelen:
|
|
288
|
+
msg = "uinput device name must not be longer than {} characters"
|
|
289
|
+
raise UInputError(msg.format(_uinput.maxnamelen))
|
|
290
|
+
|
|
291
|
+
def _find_device(self, fd: int) -> InputDevice:
|
|
292
|
+
"""
|
|
293
|
+
Tries to find the device node. Will delegate this task to one of
|
|
294
|
+
several platform-specific functions.
|
|
295
|
+
"""
|
|
296
|
+
if platform.system() == "Linux":
|
|
297
|
+
try:
|
|
298
|
+
sysname = _uinput.get_sysname(fd)
|
|
299
|
+
return self._find_device_linux(sysname)
|
|
300
|
+
except OSError:
|
|
301
|
+
# UI_GET_SYSNAME returned an error code. We're likely dealing with
|
|
302
|
+
# an old kernel. Guess the device based on the filesystem.
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
# If we're not running or Linux or the above method fails for any reason,
|
|
306
|
+
# use the generic fallback method.
|
|
307
|
+
return self._find_device_fallback()
|
|
308
|
+
|
|
309
|
+
def _find_device_linux(self, sysname: str) -> InputDevice:
|
|
310
|
+
"""
|
|
311
|
+
Tries to find the device node when running on Linux.
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
syspath = f"/sys/devices/virtual/input/{sysname}"
|
|
315
|
+
|
|
316
|
+
# The sysfs entry for event devices should contain exactly one folder
|
|
317
|
+
# whose name matches the format "event[0-9]+". It is then assumed that
|
|
318
|
+
# the device node in /dev/input uses the same name.
|
|
319
|
+
regex = re.compile("event[0-9]+")
|
|
320
|
+
for entry in os.listdir(syspath):
|
|
321
|
+
if regex.fullmatch(entry):
|
|
322
|
+
device_path = f"/dev/input/{entry}"
|
|
323
|
+
break
|
|
324
|
+
else: # no break
|
|
325
|
+
raise FileNotFoundError()
|
|
326
|
+
|
|
327
|
+
# It is possible that there is some delay before /dev/input/event* shows
|
|
328
|
+
# up on old systems that do not use devtmpfs, so if the device cannot be
|
|
329
|
+
# found, wait for a short amount and then try again once.
|
|
330
|
+
#
|
|
331
|
+
# Furthermore, even if devtmpfs is in use, it is possible that the device
|
|
332
|
+
# does show up immediately, but without the correct permissions that
|
|
333
|
+
# still need to be set by udev. Wait for up to two seconds for either the
|
|
334
|
+
# device to show up or the permissions to be set.
|
|
335
|
+
for attempt in range(19):
|
|
336
|
+
try:
|
|
337
|
+
return InputDevice(device_path)
|
|
338
|
+
except (FileNotFoundError, PermissionError):
|
|
339
|
+
time.sleep(0.1)
|
|
340
|
+
|
|
341
|
+
# Last attempt. If this fails, whatever exception the last attempt raises
|
|
342
|
+
# shall be the exception that this function raises.
|
|
343
|
+
return InputDevice(device_path)
|
|
344
|
+
|
|
345
|
+
def _find_device_fallback(self) -> Union[InputDevice, None]:
|
|
346
|
+
"""
|
|
347
|
+
Tries to find the device node when UI_GET_SYSNAME is not available or
|
|
348
|
+
we're running on a system sufficiently exotic that we do not know how
|
|
349
|
+
to interpret its return value.
|
|
350
|
+
"""
|
|
351
|
+
#:bug: the device node might not be immediately available
|
|
352
|
+
time.sleep(0.1)
|
|
353
|
+
|
|
354
|
+
# There could also be another device with the same name already present,
|
|
355
|
+
# make sure to select the newest one.
|
|
356
|
+
# Strictly speaking, we cannot be certain that everything returned by list_devices()
|
|
357
|
+
# ends at event[0-9]+: it might return something like "/dev/input/events_all". Find
|
|
358
|
+
# the devices that have the expected structure and extract their device number.
|
|
359
|
+
path_number_pairs = []
|
|
360
|
+
regex = re.compile("/dev/input/event([0-9]+)")
|
|
361
|
+
for path in util.list_devices("/dev/input/"):
|
|
362
|
+
regex_match = regex.fullmatch(path)
|
|
363
|
+
if not regex_match:
|
|
364
|
+
continue
|
|
365
|
+
device_number = int(regex_match[1])
|
|
366
|
+
path_number_pairs.append((path, device_number))
|
|
367
|
+
|
|
368
|
+
# The modification date of the devnode is not reliable unfortunately, so we
|
|
369
|
+
# are sorting by the number in the name
|
|
370
|
+
path_number_pairs.sort(key=lambda pair: pair[1], reverse=True)
|
|
371
|
+
|
|
372
|
+
for path, _ in path_number_pairs:
|
|
373
|
+
d = InputDevice(path)
|
|
374
|
+
if d.name == self.name:
|
|
375
|
+
return d
|
evdev/util.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import collections
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import stat
|
|
6
|
+
from typing import Union, List
|
|
7
|
+
|
|
8
|
+
from . import ecodes
|
|
9
|
+
from .events import InputEvent, event_factory, KeyEvent, RelEvent, AbsEvent, SynEvent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def list_devices(input_device_dir: Union[str, bytes, os.PathLike] = "/dev/input") -> List[str]:
|
|
13
|
+
"""List readable character devices in ``input_device_dir``."""
|
|
14
|
+
|
|
15
|
+
fns = glob.glob("{}/event*".format(input_device_dir))
|
|
16
|
+
return list(filter(is_device, fns))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def is_device(fn: Union[str, bytes, os.PathLike]) -> bool:
|
|
20
|
+
"""Check if ``fn`` is a readable and writable character device."""
|
|
21
|
+
|
|
22
|
+
if not os.path.exists(fn):
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
m = os.stat(fn)[stat.ST_MODE]
|
|
26
|
+
if not stat.S_ISCHR(m):
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
if not os.access(fn, os.R_OK | os.W_OK):
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def categorize(event: InputEvent) -> Union[InputEvent, KeyEvent, RelEvent, AbsEvent, SynEvent]:
|
|
36
|
+
"""
|
|
37
|
+
Categorize an event according to its type.
|
|
38
|
+
|
|
39
|
+
The :data:`event_factory <evdev.events.event_factory>` dictionary
|
|
40
|
+
maps event types to sub-classes of :class:`InputEvent
|
|
41
|
+
<evdev.events.InputEvent>`. If the event cannot be categorized, it
|
|
42
|
+
is returned unmodified."""
|
|
43
|
+
|
|
44
|
+
if event.type in event_factory:
|
|
45
|
+
return event_factory[event.type](event)
|
|
46
|
+
else:
|
|
47
|
+
return event
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def resolve_ecodes_dict(typecodemap, unknown="?"):
|
|
51
|
+
"""
|
|
52
|
+
Resolve event codes and types to their verbose names.
|
|
53
|
+
|
|
54
|
+
:param typecodemap: mapping of event types to lists of event codes.
|
|
55
|
+
:param unknown: symbol to which unknown types or codes will be resolved.
|
|
56
|
+
|
|
57
|
+
Example
|
|
58
|
+
-------
|
|
59
|
+
>>> resolve_ecodes_dict({ 1: [272, 273, 274] })
|
|
60
|
+
{ ('EV_KEY', 1): [('BTN_MOUSE', 272),
|
|
61
|
+
('BTN_RIGHT', 273),
|
|
62
|
+
('BTN_MIDDLE', 274)] }
|
|
63
|
+
|
|
64
|
+
If ``typecodemap`` contains absolute axis info (instances of
|
|
65
|
+
:class:`AbsInfo <evdev.device.AbsInfo>` ) the result would look
|
|
66
|
+
like:
|
|
67
|
+
|
|
68
|
+
>>> resolve_ecodes_dict({ 3: [(0, AbsInfo(...))] })
|
|
69
|
+
{ ('EV_ABS', 3L): [(('ABS_X', 0L), AbsInfo(...))] }
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
for etype, codes in typecodemap.items():
|
|
73
|
+
type_name = ecodes.EV[etype]
|
|
74
|
+
|
|
75
|
+
# ecodes.keys are a combination of KEY_ and BTN_ codes
|
|
76
|
+
if etype == ecodes.EV_KEY:
|
|
77
|
+
ecode_dict = ecodes.keys
|
|
78
|
+
else:
|
|
79
|
+
ecode_dict = getattr(ecodes, type_name.split("_")[-1])
|
|
80
|
+
|
|
81
|
+
resolved = resolve_ecodes(ecode_dict, codes, unknown)
|
|
82
|
+
yield (type_name, etype), resolved
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def resolve_ecodes(ecode_dict, ecode_list, unknown="?"):
|
|
86
|
+
"""
|
|
87
|
+
Resolve event codes and types to their verbose names.
|
|
88
|
+
|
|
89
|
+
Example
|
|
90
|
+
-------
|
|
91
|
+
>>> resolve_ecodes(ecodes.BTN, [272, 273, 274])
|
|
92
|
+
[(['BTN_LEFT', 'BTN_MOUSE'], 272), ('BTN_RIGHT', 273), ('BTN_MIDDLE', 274)]
|
|
93
|
+
"""
|
|
94
|
+
res = []
|
|
95
|
+
for ecode in ecode_list:
|
|
96
|
+
# elements with AbsInfo(), eg { 3 : [(0, AbsInfo(...)), (1, AbsInfo(...))] }
|
|
97
|
+
if isinstance(ecode, tuple):
|
|
98
|
+
if ecode[0] in ecode_dict:
|
|
99
|
+
l = ((ecode_dict[ecode[0]], ecode[0]), ecode[1])
|
|
100
|
+
else:
|
|
101
|
+
l = ((unknown, ecode[0]), ecode[1])
|
|
102
|
+
|
|
103
|
+
# just ecodes, e.g: { 0 : [0, 1, 3], 1 : [30, 48] }
|
|
104
|
+
else:
|
|
105
|
+
if ecode in ecode_dict:
|
|
106
|
+
l = (ecode_dict[ecode], ecode)
|
|
107
|
+
else:
|
|
108
|
+
l = (unknown, ecode)
|
|
109
|
+
res.append(l)
|
|
110
|
+
|
|
111
|
+
return res
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def find_ecodes_by_regex(regex):
|
|
115
|
+
"""
|
|
116
|
+
Find ecodes matching a regex and return a mapping of event type to event codes.
|
|
117
|
+
|
|
118
|
+
regex can be a pattern string or a compiled regular expression object.
|
|
119
|
+
|
|
120
|
+
Example
|
|
121
|
+
-------
|
|
122
|
+
>>> find_ecodes_by_regex(r'(ABS|KEY)_BR(AKE|EAK)')
|
|
123
|
+
{1: [411], 3: [10]}
|
|
124
|
+
>>> res = find_ecodes_by_regex(r'(ABS|KEY)_BR(AKE|EAK)')
|
|
125
|
+
>>> resolve_ecodes_dict(res)
|
|
126
|
+
{
|
|
127
|
+
('EV_KEY', 1): [('KEY_BREAK', 411)],
|
|
128
|
+
('EV_ABS', 3): [('ABS_BRAKE', 10)]
|
|
129
|
+
}
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
regex = re.compile(regex) # re.compile is idempotent
|
|
133
|
+
result = collections.defaultdict(list)
|
|
134
|
+
|
|
135
|
+
for type_code, codes in ecodes.bytype.items():
|
|
136
|
+
for code, names in codes.items():
|
|
137
|
+
names = (names,) if isinstance(names, str) else names
|
|
138
|
+
for name in names:
|
|
139
|
+
if regex.match(name):
|
|
140
|
+
result[type_code].append(code)
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
return dict(result)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
__all__ = ("list_devices", "is_device", "categorize", "resolve_ecodes", "resolve_ecodes_dict", "find_ecodes_by_regex")
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evdev-binary
|
|
3
|
+
Version: 1.9.3
|
|
4
|
+
Summary: Bindings to the Linux input handling subsystem
|
|
5
|
+
Author-email: Georgi Valkov <georgi.t.valkov@gmail.com>
|
|
6
|
+
Maintainer-email: Tobi <proxima@sezanzeb.de>
|
|
7
|
+
License-Expression: BSD-3-Clause
|
|
8
|
+
Project-URL: Homepage, https://github.com/gvalkov/python-evdev
|
|
9
|
+
Keywords: evdev,input,uinput
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# evdev
|
|
22
|
+
|
|
23
|
+
<p>
|
|
24
|
+
<a href="https://pypi.python.org/pypi/evdev"><img alt="pypi version" src="https://img.shields.io/pypi/v/evdev.svg"></a>
|
|
25
|
+
<a href="https://github.com/gvalkov/python-evdev/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/pypi/l/evdev"></a>
|
|
26
|
+
<a href="https://repology.org/project/python:evdev/versions"><img alt="Packaging status" src="https://repology.org/badge/tiny-repos/python:evdev.svg"></a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
This package provides bindings to the generic input event interface in Linux.
|
|
30
|
+
The *evdev* interface serves the purpose of passing events generated in the
|
|
31
|
+
kernel directly to userspace through character devices that are typically
|
|
32
|
+
located in `/dev/input/`.
|
|
33
|
+
|
|
34
|
+
This package also comes with bindings to *uinput*, the userspace input
|
|
35
|
+
subsystem. *Uinput* allows userspace programs to create and handle input devices
|
|
36
|
+
that can inject events directly into the input subsystem.
|
|
37
|
+
|
|
38
|
+
***Documentation:***
|
|
39
|
+
https://python-evdev.readthedocs.io/en/latest/
|
|
40
|
+
|
|
41
|
+
***Development:***
|
|
42
|
+
https://github.com/gvalkov/python-evdev
|
|
43
|
+
|
|
44
|
+
***Package:***
|
|
45
|
+
https://pypi.python.org/pypi/evdev
|
|
46
|
+
|
|
47
|
+
***Changelog:***
|
|
48
|
+
https://python-evdev.readthedocs.io/en/latest/changelog.html
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
evdev/__init__.py,sha256=cu0Qu3CK4loreHKefFqudMmHQP-P-rkXSjrzjxHUCbw,1011
|
|
2
|
+
evdev/_ecodes.cpython-314t-x86_64-linux-musl.so,sha256=hupJBxRDLI4ng4-wGSqQzAke1nGQUcEiLTaoxWpoZvo,86856
|
|
3
|
+
evdev/_input.cpython-314t-x86_64-linux-musl.so,sha256=jhjDhGmHWf47DhJS2wAwhN5QqXq-tue-4TwGsjiP-Mw,55912
|
|
4
|
+
evdev/_uinput.cpython-314t-x86_64-linux-musl.so,sha256=UYjgDQ1wgKszrL3LedA00jRYrmQ4NoOQhFYv9b0xE9Y,35880
|
|
5
|
+
evdev/device.py,sha256=8sKHGmZh3DF1lJpsIy0pF1R9L5qGzazFGretaysu1Os,12959
|
|
6
|
+
evdev/ecodes.py,sha256=_AVTtsN9w5f4M74FQgbxwAQPOedmCqVts0TCX4AE6Yw,98110
|
|
7
|
+
evdev/ecodes_runtime.py,sha256=_QR3aSjX3pbwrk0f1dA-tXjiECKtV_uPpUtXpWP_cTI,2838
|
|
8
|
+
evdev/eventio.py,sha256=4SnIINjPJadQChUEy_1lbTzuTORsydDC7zL2qkegTIY,4422
|
|
9
|
+
evdev/eventio_async.py,sha256=B1qaEKYoiiop8YpG0AFf0lu2Im3CvW50dRPLug40xWA,3078
|
|
10
|
+
evdev/events.py,sha256=_0vXzjv8yFtJR9drfwhmBZl9w4ZmfwQhgCo3WcrWi-Q,5905
|
|
11
|
+
evdev/evtest.py,sha256=u0umJIV_HQUDlbBhLvXEKZ87UZqNIhk0XJRcm5VYIzg,5543
|
|
12
|
+
evdev/ff.py,sha256=1B1VJ8TJ_CpJ01Qi4tUym1p8qZK_HKQSZvvhA8ROoJY,4961
|
|
13
|
+
evdev/genecodes_c.py,sha256=_AhMK1iOQlkykOSzkbCbCJfZTk_dDU-jLsL_7FhYUqM,3618
|
|
14
|
+
evdev/genecodes_py.py,sha256=-FuU-qy4-C_q7xkSwJICwMbCmAxyvezwSU4L2SRqwAs,1996
|
|
15
|
+
evdev/input.c,sha256=wINgUFPAcFAsjnXaQja3Pr_4h-u4RsX2cEb_IfyvOdk,16346
|
|
16
|
+
evdev/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
evdev/uinput.c,sha256=9CkdJwL57R-qxaBS3T6vCJq9rC2KrRQK7BTHforK4XI,10188
|
|
18
|
+
evdev/uinput.py,sha256=vJb5E0ThptDo6HeB0mc74zFXkh3_oW7a5amn-jhWo9U,13363
|
|
19
|
+
evdev/util.py,sha256=WzgxwgjBuCE25n6UNPk91Bg2_j6weGUv1VO3vfyX8xU,4409
|
|
20
|
+
evdev_binary-1.9.3.dist-info/METADATA,sha256=83J31v0SCp-axino0tX-Nm5UR8CPjqWsIoxLhiIJ46s,1940
|
|
21
|
+
evdev_binary-1.9.3.dist-info/WHEEL,sha256=Br1fva2V4tJtTfqLxqFL6Xurp6U-0z6o4l1Tv1jOxR0,114
|
|
22
|
+
evdev_binary-1.9.3.dist-info/top_level.txt,sha256=tgRM-peDJTah3Is39Micsqkx31ek4FTHG4usyN23Xsk,6
|
|
23
|
+
evdev_binary-1.9.3.dist-info/RECORD,,
|
|
24
|
+
evdev_binary-1.9.3.dist-info/licenses/LICENSE,sha256=1FVJYNZJYVDIjB_QLXlzEhqaV7lh6lj-NJ-_ito4FrI,1507
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Copyright (c) 2012-2025 Georgi Valkov. All rights reserved.
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions are
|
|
5
|
+
met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in
|
|
12
|
+
the documentation and/or other materials provided with the
|
|
13
|
+
distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of author nor the names of its contributors may
|
|
16
|
+
be used to endorse or promote products derived from this software
|
|
17
|
+
without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
20
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
21
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
22
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
|
|
23
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
24
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
25
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
26
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
27
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
28
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
evdev
|