evdev 1.9.2__tar.gz → 2.0.0__tar.gz
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-2.0.0/PKG-INFO +50 -0
- {evdev-1.9.2 → evdev-2.0.0}/README.md +10 -8
- {evdev-1.9.2 → evdev-2.0.0}/pyproject.toml +19 -17
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/device.py +42 -23
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/eventio.py +7 -7
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/eventio_async.py +25 -17
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/events.py +17 -17
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/genecodes_c.py +3 -0
- evdev-2.0.0/src/evdev/genecodes_py.py +54 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/input.c +44 -26
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/uinput.c +25 -1
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/uinput.py +27 -21
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/util.py +16 -8
- evdev-2.0.0/src/evdev.egg-info/PKG-INFO +50 -0
- {evdev-1.9.2 → evdev-2.0.0}/tests/test_uinput.py +10 -0
- evdev-1.9.2/PKG-INFO +0 -78
- evdev-1.9.2/src/evdev/genecodes_py.py +0 -54
- evdev-1.9.2/src/evdev.egg-info/PKG-INFO +0 -78
- {evdev-1.9.2 → evdev-2.0.0}/LICENSE +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/MANIFEST.in +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/setup.cfg +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/setup.py +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/__init__.py +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/ecodes.py +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/ecodes_runtime.py +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/evtest.py +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/ff.py +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev/py.typed +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev.egg-info/SOURCES.txt +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev.egg-info/dependency_links.txt +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/src/evdev.egg-info/top_level.txt +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/tests/test_ecodes.py +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/tests/test_events.py +0 -0
- {evdev-1.9.2 → evdev-2.0.0}/tests/test_util.py +0 -0
evdev-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evdev
|
|
3
|
+
Version: 2.0.0
|
|
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.11
|
|
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.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%3Aevdev/versions">
|
|
27
|
+
<img src="https://repology.org/badge/tiny-repos/python%3Aevdev.svg" alt="Packaging status">
|
|
28
|
+
</a>
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
This package provides bindings to the generic input event interface in Linux.
|
|
32
|
+
The *evdev* interface serves the purpose of passing events generated in the
|
|
33
|
+
kernel directly to userspace through character devices that are typically
|
|
34
|
+
located in `/dev/input/`.
|
|
35
|
+
|
|
36
|
+
This package also comes with bindings to *uinput*, the userspace input
|
|
37
|
+
subsystem. *Uinput* allows userspace programs to create and handle input devices
|
|
38
|
+
that can inject events directly into the input subsystem.
|
|
39
|
+
|
|
40
|
+
***Documentation:***
|
|
41
|
+
https://python-evdev.readthedocs.io/en/latest/
|
|
42
|
+
|
|
43
|
+
***Development:***
|
|
44
|
+
https://github.com/gvalkov/python-evdev
|
|
45
|
+
|
|
46
|
+
***Package:***
|
|
47
|
+
https://pypi.org/pypi/evdev
|
|
48
|
+
|
|
49
|
+
***Changelog:***
|
|
50
|
+
https://python-evdev.readthedocs.io/en/latest/changelog.html
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# evdev
|
|
2
2
|
|
|
3
3
|
<p>
|
|
4
|
-
<a href="https://pypi.
|
|
4
|
+
<a href="https://pypi.org/pypi/evdev"><img alt="pypi version" src="https://img.shields.io/pypi/v/evdev.svg"></a>
|
|
5
5
|
<a href="https://github.com/gvalkov/python-evdev/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/pypi/l/evdev"></a>
|
|
6
|
-
<a href="https://repology.org/project/python
|
|
6
|
+
<a href="https://repology.org/project/python%3Aevdev/versions">
|
|
7
|
+
<img src="https://repology.org/badge/tiny-repos/python%3Aevdev.svg" alt="Packaging status">
|
|
8
|
+
</a>
|
|
7
9
|
</p>
|
|
8
10
|
|
|
9
11
|
This package provides bindings to the generic input event interface in Linux.
|
|
@@ -15,14 +17,14 @@ This package also comes with bindings to *uinput*, the userspace input
|
|
|
15
17
|
subsystem. *Uinput* allows userspace programs to create and handle input devices
|
|
16
18
|
that can inject events directly into the input subsystem.
|
|
17
19
|
|
|
18
|
-
***Documentation:***
|
|
20
|
+
***Documentation:***
|
|
19
21
|
https://python-evdev.readthedocs.io/en/latest/
|
|
20
22
|
|
|
21
|
-
***Development:***
|
|
23
|
+
***Development:***
|
|
22
24
|
https://github.com/gvalkov/python-evdev
|
|
23
25
|
|
|
24
|
-
***Package:***
|
|
25
|
-
https://pypi.
|
|
26
|
+
***Package:***
|
|
27
|
+
https://pypi.org/pypi/evdev
|
|
26
28
|
|
|
27
|
-
***Changelog:***
|
|
28
|
-
https://python-evdev.readthedocs.io/en/latest/changelog.html
|
|
29
|
+
***Changelog:***
|
|
30
|
+
https://python-evdev.readthedocs.io/en/latest/changelog.html
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=
|
|
2
|
+
requires = ["setuptools>=77.0"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "evdev"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.0.0"
|
|
8
8
|
description = "Bindings to the Linux input handling subsystem"
|
|
9
9
|
keywords = ["evdev", "input", "uinput"]
|
|
10
10
|
readme = "README.md"
|
|
11
|
-
license =
|
|
12
|
-
requires-python = ">=3.
|
|
11
|
+
license = "BSD-3-Clause"
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
13
|
authors = [
|
|
14
14
|
{ name="Georgi Valkov", email="georgi.t.valkov@gmail.com" },
|
|
15
15
|
]
|
|
@@ -22,7 +22,6 @@ classifiers = [
|
|
|
22
22
|
"Operating System :: POSIX :: Linux",
|
|
23
23
|
"Intended Audience :: Developers",
|
|
24
24
|
"Topic :: Software Development :: Libraries",
|
|
25
|
-
"License :: OSI Approved :: BSD License",
|
|
26
25
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
27
26
|
]
|
|
28
27
|
|
|
@@ -35,18 +34,6 @@ line-length = 120
|
|
|
35
34
|
[tool.ruff.lint]
|
|
36
35
|
ignore = ["E265", "E241", "F403", "F401", "E401", "E731"]
|
|
37
36
|
|
|
38
|
-
[tool.bumpversion]
|
|
39
|
-
current_version = "1.9.2"
|
|
40
|
-
commit = true
|
|
41
|
-
tag = true
|
|
42
|
-
allow_dirty = true
|
|
43
|
-
|
|
44
|
-
[[tool.bumpversion.files]]
|
|
45
|
-
filename = "pyproject.toml"
|
|
46
|
-
|
|
47
|
-
[[tool.bumpversion.files]]
|
|
48
|
-
filename = "docs/conf.py"
|
|
49
|
-
|
|
50
37
|
[tool.pylint.'MESSAGES CONTROL']
|
|
51
38
|
disable = """
|
|
52
39
|
no-member,
|
|
@@ -55,3 +42,18 @@ disable = """
|
|
|
55
42
|
[tool.pylint.typecheck]
|
|
56
43
|
generated-members = ["evdev.ecodes.*"]
|
|
57
44
|
ignored-modules= ["evdev._*"]
|
|
45
|
+
|
|
46
|
+
[dependency-groups]
|
|
47
|
+
dev = [
|
|
48
|
+
"build>=1.5.0",
|
|
49
|
+
"cibuildwheel>=2.23.4",
|
|
50
|
+
"furo>=2025.12.19",
|
|
51
|
+
"pytest>=9.1.1",
|
|
52
|
+
"ruff>=0.16.4",
|
|
53
|
+
"setuptools>=82.0.1",
|
|
54
|
+
"sphinx>=9.0.4",
|
|
55
|
+
"sphinx-autobuild>=2025.8.25",
|
|
56
|
+
"sphinx-autodoc-typehints>=2.3.0",
|
|
57
|
+
"sphinx-copybutton>=0.5.2",
|
|
58
|
+
"twine>=7.0.0",
|
|
59
|
+
]
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import os
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Generic, Iterator, Literal, NamedTuple, AnyStr, overload
|
|
4
4
|
|
|
5
|
-
from . import _input, ecodes, util
|
|
5
|
+
from . import _input, ecodes, ff, util
|
|
6
6
|
|
|
7
7
|
try:
|
|
8
8
|
from .eventio_async import EvdevError, EventIO
|
|
9
9
|
except ImportError:
|
|
10
10
|
from .eventio import EvdevError, EventIO
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
PathLike = AnyStr | os.PathLike[AnyStr]
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class AbsInfo(NamedTuple):
|
|
@@ -58,7 +58,7 @@ class AbsInfo(NamedTuple):
|
|
|
58
58
|
flat: int
|
|
59
59
|
resolution: int
|
|
60
60
|
|
|
61
|
-
def __str__(self):
|
|
61
|
+
def __str__(self) -> str:
|
|
62
62
|
return "value {}, min {}, max {}, fuzz {}, flat {}, res {}".format(*self) # pylint: disable=not-an-iterable
|
|
63
63
|
|
|
64
64
|
|
|
@@ -78,7 +78,7 @@ class KbdInfo(NamedTuple):
|
|
|
78
78
|
delay: int
|
|
79
79
|
repeat: int
|
|
80
80
|
|
|
81
|
-
def __str__(self):
|
|
81
|
+
def __str__(self) -> str:
|
|
82
82
|
return "delay {}, repeat {}".format(self.delay, self.repeat)
|
|
83
83
|
|
|
84
84
|
|
|
@@ -102,26 +102,31 @@ class DeviceInfo(NamedTuple):
|
|
|
102
102
|
return msg.format(*self) # pylint: disable=not-an-iterable
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
class InputDevice(EventIO, Generic[
|
|
105
|
+
class InputDevice(EventIO, Generic[AnyStr]):
|
|
106
106
|
"""
|
|
107
107
|
A linux input device from which input events can be read.
|
|
108
108
|
"""
|
|
109
109
|
|
|
110
110
|
__slots__ = ("path", "fd", "info", "name", "phys", "uniq", "_rawcapabilities", "version", "ff_effects_count")
|
|
111
111
|
|
|
112
|
-
def __init__(self, dev:
|
|
112
|
+
def __init__(self, dev: PathLike, readonly: bool = False):
|
|
113
113
|
"""
|
|
114
114
|
Arguments
|
|
115
115
|
---------
|
|
116
|
-
dev :
|
|
116
|
+
dev :
|
|
117
117
|
Path to input device
|
|
118
|
+
readonly : bool
|
|
119
|
+
Open in read-only mode (``O_RDONLY``) without attempting ``O_RDWR`` first.
|
|
118
120
|
"""
|
|
119
121
|
|
|
120
122
|
#: Path to input device.
|
|
121
|
-
self.path:
|
|
123
|
+
self.path: str | bytes = dev if not hasattr(dev, "__fspath__") else dev.__fspath__()
|
|
122
124
|
|
|
123
|
-
# Certain operations are possible only when the device is opened in read-write mode.
|
|
124
125
|
try:
|
|
126
|
+
# Certain operations are possible only when the device is opened in read-write mode.
|
|
127
|
+
# This avoids triggering firmware side-effects (such as LED state re-assertion) on certain hardware.
|
|
128
|
+
if readonly:
|
|
129
|
+
raise OSError
|
|
125
130
|
fd = os.open(dev, os.O_RDWR | os.O_NONBLOCK)
|
|
126
131
|
except OSError:
|
|
127
132
|
fd = os.open(dev, os.O_RDONLY | os.O_NONBLOCK)
|
|
@@ -133,7 +138,7 @@ class InputDevice(EventIO, Generic[_AnyStr]):
|
|
|
133
138
|
info_res = _input.ioctl_devinfo(self.fd)
|
|
134
139
|
|
|
135
140
|
#: A :class:`DeviceInfo <evdev.device.DeviceInfo>` instance.
|
|
136
|
-
self.info = DeviceInfo(*info_res[:4])
|
|
141
|
+
self.info: DeviceInfo = DeviceInfo(*info_res[:4])
|
|
137
142
|
|
|
138
143
|
#: The name of the event device.
|
|
139
144
|
self.name: str = info_res[4]
|
|
@@ -179,21 +184,23 @@ class InputDevice(EventIO, Generic[_AnyStr]):
|
|
|
179
184
|
return res
|
|
180
185
|
|
|
181
186
|
@overload
|
|
182
|
-
def capabilities(self, verbose: Literal[False] = ..., absinfo: bool = ...) ->
|
|
187
|
+
def capabilities(self, verbose: Literal[False] = ..., absinfo: bool = ...) -> dict[int, list[int]]:
|
|
183
188
|
...
|
|
184
189
|
@overload
|
|
185
|
-
def capabilities(self, verbose: Literal[True], absinfo: bool = ...) ->
|
|
190
|
+
def capabilities(self, verbose: Literal[True], absinfo: bool = ...) -> dict[tuple[str, int], list[tuple[str, int]]]:
|
|
186
191
|
...
|
|
187
|
-
def capabilities(
|
|
192
|
+
def capabilities(
|
|
193
|
+
self, verbose: bool = False, absinfo: bool = True
|
|
194
|
+
) -> dict[int, list[int]] | dict[tuple[str, int], list[tuple[str, int]]]:
|
|
188
195
|
"""
|
|
189
196
|
Return the event types that this device supports as a mapping of
|
|
190
197
|
supported event types to lists of handled event codes.
|
|
191
198
|
|
|
192
199
|
Example
|
|
193
200
|
--------
|
|
201
|
+
|
|
194
202
|
>>> device.capabilities()
|
|
195
|
-
{ 1: [272, 273, 274],
|
|
196
|
-
2: [0, 1, 6, 8] }
|
|
203
|
+
{ 1: [272, 273, 274], 2: [0, 1, 6, 8] }
|
|
197
204
|
|
|
198
205
|
If ``verbose`` is ``True``, event codes and types will be resolved
|
|
199
206
|
to their names.
|
|
@@ -281,7 +288,7 @@ class InputDevice(EventIO, Generic[_AnyStr]):
|
|
|
281
288
|
"""
|
|
282
289
|
self.write(ecodes.EV_LED, led_num, value)
|
|
283
290
|
|
|
284
|
-
def __eq__(self, other):
|
|
291
|
+
def __eq__(self, other) -> bool:
|
|
285
292
|
"""
|
|
286
293
|
Two devices are equal if their :data:`info` attributes are equal.
|
|
287
294
|
"""
|
|
@@ -295,7 +302,7 @@ class InputDevice(EventIO, Generic[_AnyStr]):
|
|
|
295
302
|
msg = (self.__class__.__name__, self.path)
|
|
296
303
|
return "{}({!r})".format(*msg)
|
|
297
304
|
|
|
298
|
-
def __fspath__(self):
|
|
305
|
+
def __fspath__(self) -> AnyStr:
|
|
299
306
|
return self.path
|
|
300
307
|
|
|
301
308
|
def close(self) -> None:
|
|
@@ -341,16 +348,19 @@ class InputDevice(EventIO, Generic[_AnyStr]):
|
|
|
341
348
|
yield
|
|
342
349
|
self.ungrab()
|
|
343
350
|
|
|
344
|
-
def upload_effect(self, effect: "ff.Effect"):
|
|
351
|
+
def upload_effect(self, effect: "ff.Effect", writeback_id: bool = True) -> int:
|
|
345
352
|
"""
|
|
346
|
-
Upload a force feedback effect to a force feedback device.
|
|
353
|
+
Upload a force feedback effect to a force feedback device and return its effect id.
|
|
354
|
+
If ``writeback_id`` is ``True``, the effect id is written back to the ``effect`` struct.
|
|
347
355
|
"""
|
|
348
356
|
|
|
349
357
|
data = memoryview(effect).tobytes()
|
|
350
358
|
ff_id = _input.upload_effect(self.fd, data)
|
|
359
|
+
if writeback_id:
|
|
360
|
+
effect.id = ff_id
|
|
351
361
|
return ff_id
|
|
352
362
|
|
|
353
|
-
def erase_effect(self, ff_id) -> None:
|
|
363
|
+
def erase_effect(self, ff_id: int) -> None:
|
|
354
364
|
"""
|
|
355
365
|
Erase a force effect from a force feedback device. This also
|
|
356
366
|
stops the effect.
|
|
@@ -368,7 +378,7 @@ class InputDevice(EventIO, Generic[_AnyStr]):
|
|
|
368
378
|
return KbdInfo(*_input.ioctl_EVIOCGREP(self.fd))
|
|
369
379
|
|
|
370
380
|
@repeat.setter
|
|
371
|
-
def repeat(self, value:
|
|
381
|
+
def repeat(self, value: tuple[int, int]):
|
|
372
382
|
return _input.ioctl_EVIOCSREP(self.fd, *value)
|
|
373
383
|
|
|
374
384
|
def active_keys(self, verbose: bool = False):
|
|
@@ -410,7 +420,16 @@ class InputDevice(EventIO, Generic[_AnyStr]):
|
|
|
410
420
|
"""
|
|
411
421
|
return AbsInfo(*_input.ioctl_EVIOCGABS(self.fd, axis_num))
|
|
412
422
|
|
|
413
|
-
def set_absinfo(
|
|
423
|
+
def set_absinfo(
|
|
424
|
+
self,
|
|
425
|
+
axis_num: int,
|
|
426
|
+
value: int | None = None,
|
|
427
|
+
min: int | None = None,
|
|
428
|
+
max: int | None = None,
|
|
429
|
+
fuzz: int | None = None,
|
|
430
|
+
flat: int | None = None,
|
|
431
|
+
resolution: int | None = None,
|
|
432
|
+
) -> None:
|
|
414
433
|
"""
|
|
415
434
|
Update :class:`AbsInfo` values. Only specified values will be overwritten.
|
|
416
435
|
|
|
@@ -2,7 +2,7 @@ import fcntl
|
|
|
2
2
|
import functools
|
|
3
3
|
import os
|
|
4
4
|
import select
|
|
5
|
-
from typing import Iterator,
|
|
5
|
+
from typing import Iterator, Callable
|
|
6
6
|
|
|
7
7
|
from . import _input, _uinput, ecodes
|
|
8
8
|
from .events import InputEvent
|
|
@@ -28,7 +28,7 @@ class EventIO:
|
|
|
28
28
|
beeps).
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
|
-
def fileno(self):
|
|
31
|
+
def fileno(self) -> int:
|
|
32
32
|
"""
|
|
33
33
|
Return the file descriptor to the open event device. This makes
|
|
34
34
|
it possible to pass instances directly to :func:`select.select()` and
|
|
@@ -46,7 +46,7 @@ class EventIO:
|
|
|
46
46
|
for event in self.read():
|
|
47
47
|
yield event
|
|
48
48
|
|
|
49
|
-
def read_one(self) ->
|
|
49
|
+
def read_one(self) -> InputEvent | None:
|
|
50
50
|
"""
|
|
51
51
|
Read and return a single input event as an instance of
|
|
52
52
|
:class:`InputEvent <evdev.events.InputEvent>`.
|
|
@@ -74,7 +74,7 @@ class EventIO:
|
|
|
74
74
|
yield InputEvent(*event)
|
|
75
75
|
|
|
76
76
|
# pylint: disable=no-self-argument
|
|
77
|
-
def need_write(func):
|
|
77
|
+
def need_write(func: Callable) -> Callable:
|
|
78
78
|
"""
|
|
79
79
|
Decorator that raises :class:`EvdevError` if there is no write access to the
|
|
80
80
|
input device.
|
|
@@ -91,7 +91,7 @@ class EventIO:
|
|
|
91
91
|
|
|
92
92
|
return wrapper
|
|
93
93
|
|
|
94
|
-
def write_event(self, event):
|
|
94
|
+
def write_event(self, event: InputEvent):
|
|
95
95
|
"""
|
|
96
96
|
Inject an input event into the input subsystem. Events are
|
|
97
97
|
queued until a synchronization event is received.
|
|
@@ -139,7 +139,7 @@ class EventIO:
|
|
|
139
139
|
|
|
140
140
|
_uinput.write(self.fd, etype, code, value)
|
|
141
141
|
|
|
142
|
-
def syn(self):
|
|
142
|
+
def syn(self) -> None:
|
|
143
143
|
"""
|
|
144
144
|
Inject a ``SYN_REPORT`` event into the input subsystem. Events
|
|
145
145
|
queued by :func:`write()` will be fired. If possible, events
|
|
@@ -148,5 +148,5 @@ class EventIO:
|
|
|
148
148
|
|
|
149
149
|
self.write(ecodes.EV_SYN, ecodes.SYN_REPORT, 0)
|
|
150
150
|
|
|
151
|
-
def close(self):
|
|
151
|
+
def close(self) -> None:
|
|
152
152
|
pass
|
|
@@ -15,7 +15,7 @@ else:
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class ReadIterator:
|
|
18
|
-
def __init__(self, device):
|
|
18
|
+
def __init__(self, device: "EventIO"):
|
|
19
19
|
self.current_batch = iter(())
|
|
20
20
|
self.device = device
|
|
21
21
|
|
|
@@ -36,7 +36,7 @@ class ReadIterator:
|
|
|
36
36
|
return self
|
|
37
37
|
|
|
38
38
|
def __anext__(self) -> "asyncio.Future[InputEvent]":
|
|
39
|
-
future = asyncio.
|
|
39
|
+
future = asyncio.get_running_loop().create_future()
|
|
40
40
|
try:
|
|
41
41
|
# Read from the previous batch of events.
|
|
42
42
|
future.set_result(next(self.current_batch))
|
|
@@ -54,8 +54,15 @@ class ReadIterator:
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
class EventIO(eventio.EventIO):
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
# The event loop a reader was last registered on, or None if no async read
|
|
58
|
+
# has been awaited yet. Set in _do_when_readable, used by close().
|
|
59
|
+
_loop: "asyncio.AbstractEventLoop | None" = None
|
|
60
|
+
|
|
61
|
+
def _do_when_readable(self, callback) -> None:
|
|
62
|
+
# Remember the loop the reader is registered on so that close() can
|
|
63
|
+
# remove it later, even when called without a running event loop.
|
|
64
|
+
loop = asyncio.get_running_loop()
|
|
65
|
+
self._loop = loop
|
|
59
66
|
|
|
60
67
|
def ready():
|
|
61
68
|
loop.remove_reader(self.fileno())
|
|
@@ -63,28 +70,28 @@ class EventIO(eventio.EventIO):
|
|
|
63
70
|
|
|
64
71
|
loop.add_reader(self.fileno(), ready)
|
|
65
72
|
|
|
66
|
-
def _set_result(self, future, cb):
|
|
73
|
+
def _set_result(self, future, cb) -> None:
|
|
67
74
|
try:
|
|
68
75
|
future.set_result(cb())
|
|
69
76
|
except Exception as error:
|
|
70
77
|
future.set_exception(error)
|
|
71
78
|
|
|
72
|
-
def async_read_one(self):
|
|
79
|
+
def async_read_one(self) -> "asyncio.Future[InputEvent]":
|
|
73
80
|
"""
|
|
74
81
|
Asyncio coroutine to read and return a single input event as
|
|
75
82
|
an instance of :class:`InputEvent <evdev.events.InputEvent>`.
|
|
76
83
|
"""
|
|
77
|
-
future = asyncio.
|
|
84
|
+
future = asyncio.get_running_loop().create_future()
|
|
78
85
|
self._do_when_readable(lambda: self._set_result(future, self.read_one))
|
|
79
86
|
return future
|
|
80
87
|
|
|
81
|
-
def async_read(self):
|
|
88
|
+
def async_read(self) -> "asyncio.Future[InputEvent]":
|
|
82
89
|
"""
|
|
83
90
|
Asyncio coroutine to read multiple input events from device. Return
|
|
84
91
|
a generator object that yields :class:`InputEvent <evdev.events.InputEvent>`
|
|
85
92
|
instances.
|
|
86
93
|
"""
|
|
87
|
-
future = asyncio.
|
|
94
|
+
future = asyncio.get_running_loop().create_future()
|
|
88
95
|
self._do_when_readable(lambda: self._set_result(future, self.read))
|
|
89
96
|
return future
|
|
90
97
|
|
|
@@ -96,11 +103,12 @@ class EventIO(eventio.EventIO):
|
|
|
96
103
|
"""
|
|
97
104
|
return ReadIterator(self)
|
|
98
105
|
|
|
99
|
-
def close(self):
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
# remove the reader from.
|
|
106
|
-
|
|
106
|
+
def close(self) -> None:
|
|
107
|
+
# A reader is only registered once an async read has been awaited, in
|
|
108
|
+
# which case _do_when_readable recorded the loop it was added to.
|
|
109
|
+
loop = self._loop
|
|
110
|
+
if loop is None or loop.is_closed():
|
|
111
|
+
# No reader was ever registered, or its loop is already gone, so
|
|
112
|
+
# there is nothing to remove the reader from.
|
|
113
|
+
return
|
|
114
|
+
loop.remove_reader(self.fileno())
|
|
@@ -47,7 +47,7 @@ class InputEvent:
|
|
|
47
47
|
|
|
48
48
|
__slots__ = "sec", "usec", "type", "code", "value"
|
|
49
49
|
|
|
50
|
-
def __init__(self, sec, usec, type, code, value):
|
|
50
|
+
def __init__(self, sec: int, usec: int, type: int, code: int, value: int):
|
|
51
51
|
#: Time in seconds since epoch at which event occurred.
|
|
52
52
|
self.sec: int = sec
|
|
53
53
|
|
|
@@ -67,11 +67,11 @@ class InputEvent:
|
|
|
67
67
|
"""Return event timestamp as a float."""
|
|
68
68
|
return self.sec + (self.usec / 1000000.0)
|
|
69
69
|
|
|
70
|
-
def __str__(self):
|
|
70
|
+
def __str__(self) -> str:
|
|
71
71
|
msg = "event at {:f}, code {:02d}, type {:02d}, val {:02d}"
|
|
72
72
|
return msg.format(self.timestamp(), self.code, self.type, self.value)
|
|
73
73
|
|
|
74
|
-
def __repr__(self):
|
|
74
|
+
def __repr__(self) -> str:
|
|
75
75
|
msg = "{}({!r}, {!r}, {!r}, {!r}, {!r})"
|
|
76
76
|
return msg.format(self.__class__.__name__, self.sec, self.usec, self.type, self.code, self.value)
|
|
77
77
|
|
|
@@ -95,24 +95,24 @@ class KeyEvent:
|
|
|
95
95
|
self.scancode: int = event.code
|
|
96
96
|
|
|
97
97
|
if event.value == 0:
|
|
98
|
-
self.keystate = KeyEvent.key_up
|
|
98
|
+
self.keystate: int = KeyEvent.key_up
|
|
99
99
|
elif event.value == 2:
|
|
100
|
-
self.keystate = KeyEvent.key_hold
|
|
100
|
+
self.keystate: int = KeyEvent.key_hold
|
|
101
101
|
elif event.value == 1:
|
|
102
|
-
self.keystate = KeyEvent.key_down
|
|
102
|
+
self.keystate: int = KeyEvent.key_down
|
|
103
103
|
|
|
104
104
|
try:
|
|
105
|
-
self.keycode = keys[event.code]
|
|
105
|
+
self.keycode: int = keys[event.code]
|
|
106
106
|
except KeyError:
|
|
107
107
|
if allow_unknown:
|
|
108
|
-
self.keycode = "0x{:02X}".format(event.code)
|
|
108
|
+
self.keycode: int = "0x{:02X}".format(event.code)
|
|
109
109
|
else:
|
|
110
110
|
raise
|
|
111
111
|
|
|
112
112
|
#: Reference to an :class:`InputEvent` instance.
|
|
113
113
|
self.event: InputEvent = event
|
|
114
114
|
|
|
115
|
-
def __str__(self):
|
|
115
|
+
def __str__(self) -> str:
|
|
116
116
|
try:
|
|
117
117
|
ks = ("up", "down", "hold")[self.keystate]
|
|
118
118
|
except IndexError:
|
|
@@ -121,7 +121,7 @@ class KeyEvent:
|
|
|
121
121
|
msg = "key event at {:f}, {} ({}), {}"
|
|
122
122
|
return msg.format(self.event.timestamp(), self.scancode, self.keycode, ks)
|
|
123
123
|
|
|
124
|
-
def __repr__(self):
|
|
124
|
+
def __repr__(self) -> str:
|
|
125
125
|
return "{}({!r})".format(self.__class__.__name__, self.event)
|
|
126
126
|
|
|
127
127
|
|
|
@@ -134,11 +134,11 @@ class RelEvent:
|
|
|
134
134
|
#: Reference to an :class:`InputEvent` instance.
|
|
135
135
|
self.event: InputEvent = event
|
|
136
136
|
|
|
137
|
-
def __str__(self):
|
|
137
|
+
def __str__(self) -> str:
|
|
138
138
|
msg = "relative axis event at {:f}, {}"
|
|
139
139
|
return msg.format(self.event.timestamp(), REL[self.event.code])
|
|
140
140
|
|
|
141
|
-
def __repr__(self):
|
|
141
|
+
def __repr__(self) -> str:
|
|
142
142
|
return "{}({!r})".format(self.__class__.__name__, self.event)
|
|
143
143
|
|
|
144
144
|
|
|
@@ -151,11 +151,11 @@ class AbsEvent:
|
|
|
151
151
|
#: Reference to an :class:`InputEvent` instance.
|
|
152
152
|
self.event: InputEvent = event
|
|
153
153
|
|
|
154
|
-
def __str__(self):
|
|
154
|
+
def __str__(self) -> str:
|
|
155
155
|
msg = "absolute axis event at {:f}, {}"
|
|
156
156
|
return msg.format(self.event.timestamp(), ABS[self.event.code])
|
|
157
157
|
|
|
158
|
-
def __repr__(self):
|
|
158
|
+
def __repr__(self) -> str:
|
|
159
159
|
return "{}({!r})".format(self.__class__.__name__, self.event)
|
|
160
160
|
|
|
161
161
|
|
|
@@ -171,17 +171,17 @@ class SynEvent:
|
|
|
171
171
|
#: Reference to an :class:`InputEvent` instance.
|
|
172
172
|
self.event: InputEvent = event
|
|
173
173
|
|
|
174
|
-
def __str__(self):
|
|
174
|
+
def __str__(self) -> str:
|
|
175
175
|
msg = "synchronization event at {:f}, {}"
|
|
176
176
|
return msg.format(self.event.timestamp(), SYN[self.event.code])
|
|
177
177
|
|
|
178
|
-
def __repr__(self):
|
|
178
|
+
def __repr__(self) -> str:
|
|
179
179
|
return "{}({!r})".format(self.__class__.__name__, self.event)
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
#: A mapping of event types to :class:`InputEvent` sub-classes. Used
|
|
183
183
|
#: by :func:`evdev.util.categorize()`
|
|
184
|
-
event_factory = {
|
|
184
|
+
event_factory: dict[int, type[InputEvent]] = {
|
|
185
185
|
EV_KEY: KeyEvent,
|
|
186
186
|
EV_REL: RelEvent,
|
|
187
187
|
EV_ABS: AbsEvent,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from unittest import mock
|
|
3
|
+
from pprint import PrettyPrinter
|
|
4
|
+
|
|
5
|
+
sys.modules["evdev.ecodes"] = mock.Mock()
|
|
6
|
+
from evdev import ecodes_runtime as ecodes
|
|
7
|
+
|
|
8
|
+
pprint = PrettyPrinter(indent=2, sort_dicts=True, width=120).pprint
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
print("# Automatically generated by evdev.genecodes_py")
|
|
12
|
+
print()
|
|
13
|
+
print('"""')
|
|
14
|
+
print(ecodes.__doc__.strip())
|
|
15
|
+
print('"""')
|
|
16
|
+
|
|
17
|
+
print()
|
|
18
|
+
print("from typing import Final")
|
|
19
|
+
print()
|
|
20
|
+
|
|
21
|
+
for name, value in ecodes.ecodes.items():
|
|
22
|
+
print(f"{name}: Final[int] = {value}")
|
|
23
|
+
print()
|
|
24
|
+
|
|
25
|
+
entries = [
|
|
26
|
+
("ecodes", "dict[str, int]", "#: Mapping of names to values."),
|
|
27
|
+
("bytype", "dict[int, dict[int, str | tuple[str]]]", "#: Mapping of event types to other value/name mappings."),
|
|
28
|
+
("keys", "dict[int, str | tuple[str]]", "#: Keys are a combination of all BTN and KEY codes."),
|
|
29
|
+
("KEY", "dict[int, str | tuple[str]]", None),
|
|
30
|
+
("ABS", "dict[int, str | tuple[str]]", None),
|
|
31
|
+
("REL", "dict[int, str | tuple[str]]", None),
|
|
32
|
+
("SW", "dict[int, str | tuple[str]]", None),
|
|
33
|
+
("MSC", "dict[int, str | tuple[str]]", None),
|
|
34
|
+
("LED", "dict[int, str | tuple[str]]", None),
|
|
35
|
+
("BTN", "dict[int, str | tuple[str]]", None),
|
|
36
|
+
("REP", "dict[int, str | tuple[str]]", None),
|
|
37
|
+
("SND", "dict[int, str | tuple[str]]", None),
|
|
38
|
+
("ID", "dict[int, str | tuple[str]]", None),
|
|
39
|
+
("EV", "dict[int, str | tuple[str]]", None),
|
|
40
|
+
("BUS", "dict[int, str | tuple[str]]", None),
|
|
41
|
+
("SYN", "dict[int, str | tuple[str]]", None),
|
|
42
|
+
("FF", "dict[int, str | tuple[str]]", None),
|
|
43
|
+
("UI_FF", "dict[int, str | tuple[str]]", None),
|
|
44
|
+
("FF_STATUS", "dict[int, str | tuple[str]]", None),
|
|
45
|
+
("INPUT_PROP", "dict[int, str | tuple[str]]", None)
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
for key, annotation, doc in entries:
|
|
49
|
+
if doc:
|
|
50
|
+
print(doc)
|
|
51
|
+
|
|
52
|
+
print(f"{key}: {annotation} = ", end="")
|
|
53
|
+
pprint(getattr(ecodes, key))
|
|
54
|
+
print()
|