evdev 1.8.0__tar.gz → 1.9.1__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.
Files changed (33) hide show
  1. {evdev-1.8.0 → evdev-1.9.1}/MANIFEST.in +2 -2
  2. {evdev-1.8.0/evdev.egg-info → evdev-1.9.1}/PKG-INFO +1 -1
  3. {evdev-1.8.0 → evdev-1.9.1}/pyproject.toml +2 -5
  4. {evdev-1.8.0 → evdev-1.9.1}/setup.py +13 -8
  5. evdev-1.9.1/src/evdev/__init__.py +39 -0
  6. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/device.py +55 -52
  7. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/ecodes.py +3 -3
  8. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/ecodes_runtime.py +1 -1
  9. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/eventio.py +6 -5
  10. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/eventio_async.py +47 -40
  11. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/events.py +19 -18
  12. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/evtest.py +5 -4
  13. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/genecodes_c.py +5 -1
  14. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/genecodes_py.py +2 -1
  15. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/input.c +22 -51
  16. evdev-1.9.1/src/evdev/py.typed +0 -0
  17. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/uinput.c +2 -23
  18. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/uinput.py +36 -29
  19. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/util.py +6 -7
  20. {evdev-1.8.0 → evdev-1.9.1/src/evdev.egg-info}/PKG-INFO +1 -1
  21. evdev-1.9.1/src/evdev.egg-info/SOURCES.txt +29 -0
  22. {evdev-1.8.0 → evdev-1.9.1}/tests/test_ecodes.py +13 -3
  23. {evdev-1.8.0 → evdev-1.9.1}/tests/test_util.py +1 -1
  24. evdev-1.8.0/evdev/__init__.py +0 -9
  25. evdev-1.8.0/evdev.egg-info/SOURCES.txt +0 -28
  26. {evdev-1.8.0 → evdev-1.9.1}/LICENSE +0 -0
  27. {evdev-1.8.0 → evdev-1.9.1}/README.md +0 -0
  28. {evdev-1.8.0 → evdev-1.9.1}/setup.cfg +0 -0
  29. {evdev-1.8.0 → evdev-1.9.1/src}/evdev/ff.py +0 -0
  30. {evdev-1.8.0 → evdev-1.9.1/src}/evdev.egg-info/dependency_links.txt +0 -0
  31. {evdev-1.8.0 → evdev-1.9.1/src}/evdev.egg-info/top_level.txt +0 -0
  32. {evdev-1.8.0 → evdev-1.9.1}/tests/test_events.py +0 -0
  33. {evdev-1.8.0 → evdev-1.9.1}/tests/test_uinput.py +0 -0
@@ -1,5 +1,5 @@
1
1
  # The _ecodes extension module source file needs to be generated against the
2
2
  # evdev headers of the running kernel. Refer to the 'build_ecodes' distutils
3
3
  # command in setup.py.
4
- exclude evdev/ecodes.c
5
- include evdev/ecodes.py
4
+ exclude src/evdev/ecodes.c
5
+ include src/evdev/ecodes.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: evdev
3
- Version: 1.8.0
3
+ Version: 1.9.1
4
4
  Summary: Bindings to the Linux input handling subsystem
5
5
  Author-email: Georgi Valkov <georgi.t.valkov@gmail.com>
6
6
  Maintainer-email: Tobi <proxima@sezanzeb.de>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "evdev"
7
- version = "1.8.0"
7
+ version = "1.9.1"
8
8
  description = "Bindings to the Linux input handling subsystem"
9
9
  keywords = ["evdev", "input", "uinput"]
10
10
  readme = "README.md"
@@ -29,9 +29,6 @@ classifiers = [
29
29
  [project.urls]
30
30
  "Homepage" = "https://github.com/gvalkov/python-evdev"
31
31
 
32
- [tool.setuptools]
33
- packages = ["evdev"]
34
-
35
32
  [tool.ruff]
36
33
  line-length = 120
37
34
 
@@ -39,7 +36,7 @@ line-length = 120
39
36
  ignore = ["E265", "E241", "F403", "F401", "E401", "E731"]
40
37
 
41
38
  [tool.bumpversion]
42
- current_version = "1.8.0"
39
+ current_version = "1.9.1"
43
40
  commit = true
44
41
  tag = true
45
42
  allow_dirty = true
@@ -2,6 +2,7 @@ import os
2
2
  import sys
3
3
  import shutil
4
4
  import textwrap
5
+ import platform
5
6
  from pathlib import Path
6
7
  from subprocess import run
7
8
 
@@ -10,7 +11,7 @@ from setuptools.command import build_ext as _build_ext
10
11
 
11
12
 
12
13
  curdir = Path(__file__).resolve().parent
13
- ecodes_c_path = curdir / "evdev/ecodes.c"
14
+ ecodes_c_path = curdir / "src/evdev/ecodes.c"
14
15
 
15
16
 
16
17
  def create_ecodes(headers=None):
@@ -25,7 +26,11 @@ def create_ecodes(headers=None):
25
26
  include_paths.update(c_inc_path.split(":"))
26
27
 
27
28
  include_paths.add("/usr/include")
28
- files = ["linux/input.h", "linux/input-event-codes.h", "linux/uinput.h"]
29
+ if platform.system().lower() == "freebsd":
30
+ files = ["dev/evdev/input.h", "dev/evdev/input-event-codes.h", "dev/evdev/uinput.h"]
31
+ else:
32
+ files = ["linux/input.h", "linux/input-event-codes.h", "linux/uinput.h"]
33
+
29
34
  headers = [os.path.join(path, file) for path in include_paths for file in files]
30
35
 
31
36
  headers = [header for header in headers if os.path.isfile(header)]
@@ -60,7 +65,7 @@ def create_ecodes(headers=None):
60
65
 
61
66
  print("writing %s (using %s)" % (ecodes_c_path, " ".join(headers)))
62
67
  with ecodes_c_path.open("w") as fh:
63
- cmd = [sys.executable, "evdev/genecodes_c.py", "--ecodes", *headers]
68
+ cmd = [sys.executable, "src/evdev/genecodes_c.py", "--ecodes", *headers]
64
69
  run(cmd, check=True, stdout=fh)
65
70
 
66
71
 
@@ -93,12 +98,12 @@ class build_ext(_build_ext.build_ext):
93
98
  ecodes_py = Path(self.build_lib) / "evdev/ecodes.py"
94
99
  print(f"writing {ecodes_py}")
95
100
  with ecodes_py.open("w") as fh:
96
- cmd = [sys.executable, "-B", "evdev/genecodes_py.py"]
101
+ cmd = [sys.executable, "-B", "src/evdev/genecodes_py.py"]
97
102
  res = run(cmd, env={"PYTHONPATH": self.build_lib}, stdout=fh)
98
103
 
99
104
  if res.returncode != 0:
100
105
  print(f"failed to generate static {ecodes_py} - will use ecodes_runtime.py")
101
- shutil.copy("evdev/ecodes_runtime.py", ecodes_py)
106
+ shutil.copy("src/evdev/ecodes_runtime.py", ecodes_py)
102
107
 
103
108
  def run(self):
104
109
  for cmd_name in self.get_sub_commands():
@@ -112,9 +117,9 @@ class build_ext(_build_ext.build_ext):
112
117
  cflags = ["-std=c99", "-Wno-error=declaration-after-statement"]
113
118
  setup(
114
119
  ext_modules=[
115
- Extension("evdev._input", sources=["evdev/input.c"], extra_compile_args=cflags),
116
- Extension("evdev._uinput", sources=["evdev/uinput.c"], extra_compile_args=cflags),
117
- Extension("evdev._ecodes", sources=["evdev/ecodes.c"], extra_compile_args=cflags),
120
+ Extension("evdev._input", sources=["src/evdev/input.c"], extra_compile_args=cflags),
121
+ Extension("evdev._uinput", sources=["src/evdev/uinput.c"], extra_compile_args=cflags),
122
+ Extension("evdev._ecodes", sources=["src/evdev/ecodes.c"], extra_compile_args=cflags),
118
123
  ],
119
124
  cmdclass={
120
125
  "build_ext": build_ext,
@@ -0,0 +1,39 @@
1
+ # --------------------------------------------------------------------------
2
+ # Gather everything into a single, convenient namespace.
3
+ # --------------------------------------------------------------------------
4
+
5
+ # The superfluous "import name as name" syntax is here to satisfy mypy's attrs-defined rule.
6
+ # Alternatively all exported objects can be listed in __all__.
7
+
8
+ from . import (
9
+ ecodes as ecodes,
10
+ ff as ff,
11
+ )
12
+
13
+ from .device import (
14
+ AbsInfo as AbsInfo,
15
+ DeviceInfo as DeviceInfo,
16
+ EvdevError as EvdevError,
17
+ InputDevice as InputDevice,
18
+ )
19
+
20
+ from .events import (
21
+ AbsEvent as AbsEvent,
22
+ InputEvent as InputEvent,
23
+ KeyEvent as KeyEvent,
24
+ RelEvent as RelEvent,
25
+ SynEvent as SynEvent,
26
+ event_factory as event_factory,
27
+ )
28
+
29
+ from .uinput import (
30
+ UInput as UInput,
31
+ UInputError as UInputError,
32
+ )
33
+
34
+ from .util import (
35
+ categorize as categorize,
36
+ list_devices as list_devices,
37
+ resolve_ecodes as resolve_ecodes,
38
+ resolve_ecodes_dict as resolve_ecodes_dict,
39
+ )
@@ -1,9 +1,6 @@
1
- # encoding: utf-8
2
-
3
- import collections
4
1
  import contextlib
5
2
  import os
6
- import warnings
3
+ from typing import Dict, Iterator, List, Literal, NamedTuple, Tuple, Union, overload
7
4
 
8
5
  from . import _input, ecodes, util
9
6
 
@@ -13,18 +10,10 @@ except ImportError:
13
10
  from .eventio import EvdevError, EventIO
14
11
 
15
12
 
16
- # --------------------------------------------------------------------------
17
- _AbsInfo = collections.namedtuple("AbsInfo", ["value", "min", "max", "fuzz", "flat", "resolution"])
18
-
19
- _KbdInfo = collections.namedtuple("KbdInfo", ["delay", "repeat"])
20
-
21
- _DeviceInfo = collections.namedtuple("DeviceInfo", ["bustype", "vendor", "product", "version"])
22
-
23
-
24
- class AbsInfo(_AbsInfo):
13
+ class AbsInfo(NamedTuple):
25
14
  """Absolute axis information.
26
15
 
27
- A ``namedtuple`` used for storing absolute axis information -
16
+ A ``namedtuple`` with absolute axis information -
28
17
  corresponds to the ``input_absinfo`` struct:
29
18
 
30
19
  Attributes
@@ -60,11 +49,18 @@ class AbsInfo(_AbsInfo):
60
49
 
61
50
  """
62
51
 
52
+ value: int
53
+ min: int
54
+ max: int
55
+ fuzz: int
56
+ flat: int
57
+ resolution: int
58
+
63
59
  def __str__(self):
64
- return "val {}, min {}, max {}, fuzz {}, flat {}, res {}".format(*self)
60
+ return "value {}, min {}, max {}, fuzz {}, flat {}, res {}".format(*self) # pylint: disable=not-an-iterable
65
61
 
66
62
 
67
- class KbdInfo(_KbdInfo):
63
+ class KbdInfo(NamedTuple):
68
64
  """Keyboard repeat rate.
69
65
 
70
66
  Attributes
@@ -77,11 +73,14 @@ class KbdInfo(_KbdInfo):
77
73
  Keyboard repeat rate in characters per second.
78
74
  """
79
75
 
76
+ delay: int
77
+ repeat: int
78
+
80
79
  def __str__(self):
81
- return "delay {}, repeat {}".format(*self)
80
+ return "delay {}, repeat {}".format(self.delay, self.repeat)
82
81
 
83
82
 
84
- class DeviceInfo(_DeviceInfo):
83
+ class DeviceInfo(NamedTuple):
85
84
  """
86
85
  Attributes
87
86
  ----------
@@ -91,9 +90,14 @@ class DeviceInfo(_DeviceInfo):
91
90
  version
92
91
  """
93
92
 
94
- def __str__(self):
93
+ bustype: int
94
+ vendor: int
95
+ product: int
96
+ version: int
97
+
98
+ def __str__(self) -> str:
95
99
  msg = "bus: {:04x}, vendor {:04x}, product {:04x}, version {:04x}"
96
- return msg.format(*self)
100
+ return msg.format(*self) # pylint: disable=not-an-iterable
97
101
 
98
102
 
99
103
  class InputDevice(EventIO):
@@ -103,7 +107,7 @@ class InputDevice(EventIO):
103
107
 
104
108
  __slots__ = ("path", "fd", "info", "name", "phys", "uniq", "_rawcapabilities", "version", "ff_effects_count")
105
109
 
106
- def __init__(self, dev):
110
+ def __init__(self, dev: Union[str, bytes, os.PathLike]):
107
111
  """
108
112
  Arguments
109
113
  ---------
@@ -114,15 +118,14 @@ class InputDevice(EventIO):
114
118
  #: Path to input device.
115
119
  self.path = dev if not hasattr(dev, "__fspath__") else dev.__fspath__()
116
120
 
117
- # Certain operations are possible only when the device is opened in
118
- # read-write mode.
121
+ # Certain operations are possible only when the device is opened in read-write mode.
119
122
  try:
120
123
  fd = os.open(dev, os.O_RDWR | os.O_NONBLOCK)
121
124
  except OSError:
122
125
  fd = os.open(dev, os.O_RDONLY | os.O_NONBLOCK)
123
126
 
124
127
  #: A non-blocking file descriptor to the device file.
125
- self.fd = fd
128
+ self.fd: int = fd
126
129
 
127
130
  # Returns (bustype, vendor, product, version, name, phys, capabilities).
128
131
  info_res = _input.ioctl_devinfo(self.fd)
@@ -131,16 +134,16 @@ class InputDevice(EventIO):
131
134
  self.info = DeviceInfo(*info_res[:4])
132
135
 
133
136
  #: The name of the event device.
134
- self.name = info_res[4]
137
+ self.name: str = info_res[4]
135
138
 
136
139
  #: The physical topology of the device.
137
- self.phys = info_res[5]
140
+ self.phys: str = info_res[5]
138
141
 
139
142
  #: The unique identifier of the device.
140
- self.uniq = info_res[6]
143
+ self.uniq: str = info_res[6]
141
144
 
142
145
  #: The evdev protocol version.
143
- self.version = _input.ioctl_EVIOCGVERSION(self.fd)
146
+ self.version: int = _input.ioctl_EVIOCGVERSION(self.fd)
144
147
 
145
148
  #: The raw dictionary of device capabilities - see `:func:capabilities()`.
146
149
  self._rawcapabilities = _input.ioctl_capabilities(self.fd)
@@ -148,14 +151,14 @@ class InputDevice(EventIO):
148
151
  #: The number of force feedback effects the device can keep in its memory.
149
152
  self.ff_effects_count = _input.ioctl_EVIOCGEFFECTS(self.fd)
150
153
 
151
- def __del__(self):
154
+ def __del__(self) -> None:
152
155
  if hasattr(self, "fd") and self.fd is not None:
153
156
  try:
154
157
  self.close()
155
158
  except (OSError, ImportError, AttributeError):
156
159
  pass
157
160
 
158
- def _capabilities(self, absinfo=True):
161
+ def _capabilities(self, absinfo: bool = True):
159
162
  res = {}
160
163
 
161
164
  for etype, _ecodes in self._rawcapabilities.items():
@@ -173,7 +176,13 @@ class InputDevice(EventIO):
173
176
 
174
177
  return res
175
178
 
176
- def capabilities(self, verbose=False, absinfo=True):
179
+ @overload
180
+ def capabilities(self, verbose: Literal[False] = ..., absinfo: bool = ...) -> Dict[int, List[int]]:
181
+ ...
182
+ @overload
183
+ def capabilities(self, verbose: Literal[True], absinfo: bool = ...) -> Dict[Tuple[str, int], List[Tuple[str, int]]]:
184
+ ...
185
+ def capabilities(self, verbose: bool = False, absinfo: bool = True) -> Union[Dict[int, List[int]], Dict[Tuple[str, int], List[Tuple[str, int]]]]:
177
186
  """
178
187
  Return the event types that this device supports as a mapping of
179
188
  supported event types to lists of handled event codes.
@@ -218,7 +227,7 @@ class InputDevice(EventIO):
218
227
  else:
219
228
  return self._capabilities(absinfo)
220
229
 
221
- def input_props(self, verbose=False):
230
+ def input_props(self, verbose: bool = False):
222
231
  """
223
232
  Get device properties and quirks.
224
233
 
@@ -239,7 +248,7 @@ class InputDevice(EventIO):
239
248
 
240
249
  return props
241
250
 
242
- def leds(self, verbose=False):
251
+ def leds(self, verbose: bool = False):
243
252
  """
244
253
  Return currently set LED keys.
245
254
 
@@ -260,7 +269,7 @@ class InputDevice(EventIO):
260
269
 
261
270
  return leds
262
271
 
263
- def set_led(self, led_num, value):
272
+ def set_led(self, led_num: int, value: int) -> None:
264
273
  """
265
274
  Set the state of the selected LED.
266
275
 
@@ -276,18 +285,18 @@ class InputDevice(EventIO):
276
285
  """
277
286
  return isinstance(other, self.__class__) and self.info == other.info and self.path == other.path
278
287
 
279
- def __str__(self):
288
+ def __str__(self) -> str:
280
289
  msg = 'device {}, name "{}", phys "{}", uniq "{}"'
281
290
  return msg.format(self.path, self.name, self.phys, self.uniq or "")
282
291
 
283
- def __repr__(self):
292
+ def __repr__(self) -> str:
284
293
  msg = (self.__class__.__name__, self.path)
285
294
  return "{}({!r})".format(*msg)
286
295
 
287
296
  def __fspath__(self):
288
297
  return self.path
289
298
 
290
- def close(self):
299
+ def close(self) -> None:
291
300
  if self.fd > -1:
292
301
  try:
293
302
  super().close()
@@ -295,7 +304,7 @@ class InputDevice(EventIO):
295
304
  finally:
296
305
  self.fd = -1
297
306
 
298
- def grab(self):
307
+ def grab(self) -> None:
299
308
  """
300
309
  Grab input device using ``EVIOCGRAB`` - other applications will
301
310
  be unable to receive events until the device is released. Only
@@ -308,7 +317,7 @@ class InputDevice(EventIO):
308
317
 
309
318
  _input.ioctl_EVIOCGRAB(self.fd, 1)
310
319
 
311
- def ungrab(self):
320
+ def ungrab(self) -> None:
312
321
  """
313
322
  Release device if it has been already grabbed (uses `EVIOCGRAB`).
314
323
 
@@ -321,7 +330,7 @@ class InputDevice(EventIO):
321
330
  _input.ioctl_EVIOCGRAB(self.fd, 0)
322
331
 
323
332
  @contextlib.contextmanager
324
- def grab_context(self):
333
+ def grab_context(self) -> Iterator[None]:
325
334
  """
326
335
  A context manager for the duration of which only the current
327
336
  process will be able to receive events from the device.
@@ -330,7 +339,7 @@ class InputDevice(EventIO):
330
339
  yield
331
340
  self.ungrab()
332
341
 
333
- def upload_effect(self, effect):
342
+ def upload_effect(self, effect: "ff.Effect"):
334
343
  """
335
344
  Upload a force feedback effect to a force feedback device.
336
345
  """
@@ -339,7 +348,7 @@ class InputDevice(EventIO):
339
348
  ff_id = _input.upload_effect(self.fd, data)
340
349
  return ff_id
341
350
 
342
- def erase_effect(self, ff_id):
351
+ def erase_effect(self, ff_id) -> None:
343
352
  """
344
353
  Erase a force effect from a force feedback device. This also
345
354
  stops the effect.
@@ -357,10 +366,10 @@ class InputDevice(EventIO):
357
366
  return KbdInfo(*_input.ioctl_EVIOCGREP(self.fd))
358
367
 
359
368
  @repeat.setter
360
- def repeat(self, value):
369
+ def repeat(self, value: Tuple[int, int]):
361
370
  return _input.ioctl_EVIOCSREP(self.fd, *value)
362
371
 
363
- def active_keys(self, verbose=False):
372
+ def active_keys(self, verbose: bool = False):
364
373
  """
365
374
  Return currently active keys.
366
375
 
@@ -383,13 +392,7 @@ class InputDevice(EventIO):
383
392
 
384
393
  return active_keys
385
394
 
386
- @property
387
- def fn(self):
388
- msg = "Please use {0}.path instead of {0}.fn".format(self.__class__.__name__)
389
- warnings.warn(msg, DeprecationWarning, stacklevel=2)
390
- return self.path
391
-
392
- def absinfo(self, axis_num):
395
+ def absinfo(self, axis_num: int):
393
396
  """
394
397
  Return current :class:`AbsInfo` for input device axis
395
398
 
@@ -405,7 +408,7 @@ class InputDevice(EventIO):
405
408
  """
406
409
  return AbsInfo(*_input.ioctl_EVIOCGABS(self.fd, axis_num))
407
410
 
408
- def set_absinfo(self, axis_num, value=None, min=None, max=None, fuzz=None, flat=None, resolution=None):
411
+ def set_absinfo(self, axis_num: int, value=None, min=None, max=None, fuzz=None, flat=None, resolution=None) -> None:
409
412
  """
410
413
  Update :class:`AbsInfo` values. Only specified values will be overwritten.
411
414
 
@@ -1,5 +1,5 @@
1
- # When installed, this module is replaced by an ecodes.py generated at
1
+ # When installed, this module is replaced by an ecodes.py generated at
2
2
  # build time by genecodes_py.py (see build_ext in setup.py).
3
3
 
4
- # This stub exists to make development of evdev itself more convenient.
5
- from . ecodes_runtime import *
4
+ # This stub exists to make development of evdev itself more convenient.
5
+ from .ecodes_runtime import *
@@ -46,7 +46,7 @@ from . import _ecodes
46
46
  #: Mapping of names to values.
47
47
  ecodes = {}
48
48
 
49
- prefixes = "KEY ABS REL SW MSC LED BTN REP SND ID EV BUS SYN FF_STATUS FF INPUT_PROP".split()
49
+ prefixes = "KEY ABS REL SW MSC LED BTN REP SND ID EV BUS SYN FF_STATUS FF INPUT_PROP UI_FF".split()
50
50
  prev_prefix = ""
51
51
  g = globals()
52
52
 
@@ -2,6 +2,7 @@ import fcntl
2
2
  import functools
3
3
  import os
4
4
  import select
5
+ from typing import Iterator, Union
5
6
 
6
7
  from . import _input, _uinput, ecodes
7
8
  from .events import InputEvent
@@ -35,7 +36,7 @@ class EventIO:
35
36
  """
36
37
  return self.fd
37
38
 
38
- def read_loop(self):
39
+ def read_loop(self) -> Iterator[InputEvent]:
39
40
  """
40
41
  Enter an endless :func:`select.select()` loop that yields input events.
41
42
  """
@@ -45,7 +46,7 @@ class EventIO:
45
46
  for event in self.read():
46
47
  yield event
47
48
 
48
- def read_one(self):
49
+ def read_one(self) -> Union[InputEvent, None]:
49
50
  """
50
51
  Read and return a single input event as an instance of
51
52
  :class:`InputEvent <evdev.events.InputEvent>`.
@@ -59,14 +60,14 @@ class EventIO:
59
60
  if event:
60
61
  return InputEvent(*event)
61
62
 
62
- def read(self):
63
+ def read(self) -> Iterator[InputEvent]:
63
64
  """
64
65
  Read multiple input events from device. Return a generator object that
65
66
  yields :class:`InputEvent <evdev.events.InputEvent>` instances. Raises
66
67
  `BlockingIOError` if there are no available events at the moment.
67
68
  """
68
69
 
69
- # events -> [(sec, usec, type, code, val), ...]
70
+ # events -> ((sec, usec, type, code, val), ...)
70
71
  events = _input.device_read_many(self.fd)
71
72
 
72
73
  for event in events:
@@ -114,7 +115,7 @@ class EventIO:
114
115
  self.write(event.type, event.code, event.value)
115
116
 
116
117
  @need_write
117
- def write(self, etype, code, value):
118
+ def write(self, etype: int, code: int, value: int):
118
119
  """
119
120
  Inject an input event into the input subsystem. Events are
120
121
  queued until a synchronization event is received.
@@ -1,11 +1,57 @@
1
1
  import asyncio
2
2
  import select
3
+ import sys
3
4
 
4
5
  from . import eventio
6
+ from .events import InputEvent
5
7
 
6
8
  # needed for compatibility
7
9
  from .eventio import EvdevError
8
10
 
11
+ if sys.version_info >= (3, 11):
12
+ from typing import Self
13
+ else:
14
+ from typing import Any as Self
15
+
16
+
17
+ class ReadIterator:
18
+ def __init__(self, device):
19
+ self.current_batch = iter(())
20
+ self.device = device
21
+
22
+ # Standard iterator protocol.
23
+ def __iter__(self) -> Self:
24
+ return self
25
+
26
+ def __next__(self) -> InputEvent:
27
+ try:
28
+ # Read from the previous batch of events.
29
+ return next(self.current_batch)
30
+ except StopIteration:
31
+ r, w, x = select.select([self.device.fd], [], [])
32
+ self.current_batch = self.device.read()
33
+ return next(self.current_batch)
34
+
35
+ def __aiter__(self) -> Self:
36
+ return self
37
+
38
+ def __anext__(self) -> "asyncio.Future[InputEvent]":
39
+ future = asyncio.Future()
40
+ try:
41
+ # Read from the previous batch of events.
42
+ future.set_result(next(self.current_batch))
43
+ except StopIteration:
44
+
45
+ def next_batch_ready(batch):
46
+ try:
47
+ self.current_batch = batch.result()
48
+ future.set_result(next(self.current_batch))
49
+ except Exception as e:
50
+ future.set_exception(e)
51
+
52
+ self.device.async_read().add_done_callback(next_batch_ready)
53
+ return future
54
+
9
55
 
10
56
  class EventIO(eventio.EventIO):
11
57
  def _do_when_readable(self, callback):
@@ -42,7 +88,7 @@ class EventIO(eventio.EventIO):
42
88
  self._do_when_readable(lambda: self._set_result(future, self.read))
43
89
  return future
44
90
 
45
- def async_read_loop(self):
91
+ def async_read_loop(self) -> ReadIterator:
46
92
  """
47
93
  Return an iterator that yields input events. This iterator is
48
94
  compatible with the ``async for`` syntax.
@@ -58,42 +104,3 @@ class EventIO(eventio.EventIO):
58
104
  # no event loop present, so there is nothing to
59
105
  # remove the reader from. Ignore
60
106
  pass
61
-
62
-
63
- class ReadIterator:
64
- def __init__(self, device):
65
- self.current_batch = iter(())
66
- self.device = device
67
-
68
- # Standard iterator protocol.
69
- def __iter__(self):
70
- return self
71
-
72
- def __next__(self):
73
- try:
74
- # Read from the previous batch of events.
75
- return next(self.current_batch)
76
- except StopIteration:
77
- r, w, x = select.select([self.device.fd], [], [])
78
- self.current_batch = self.device.read()
79
- return next(self.current_batch)
80
-
81
- def __aiter__(self):
82
- return self
83
-
84
- def __anext__(self):
85
- future = asyncio.Future()
86
- try:
87
- # Read from the previous batch of events.
88
- future.set_result(next(self.current_batch))
89
- except StopIteration:
90
-
91
- def next_batch_ready(batch):
92
- try:
93
- self.current_batch = batch.result()
94
- future.set_result(next(self.current_batch))
95
- except Exception as e:
96
- future.set_exception(e)
97
-
98
- self.device.async_read().add_done_callback(next_batch_ready)
99
- return future