evdev 1.7.1__tar.gz → 1.9.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.
Files changed (34) hide show
  1. {evdev-1.7.1 → evdev-1.9.0}/LICENSE +1 -1
  2. {evdev-1.7.1 → evdev-1.9.0}/MANIFEST.in +2 -1
  3. {evdev-1.7.1/evdev.egg-info → evdev-1.9.0}/PKG-INFO +4 -3
  4. {evdev-1.7.1 → evdev-1.9.0}/README.md +1 -0
  5. {evdev-1.7.1 → evdev-1.9.0}/pyproject.toml +12 -6
  6. {evdev-1.7.1 → evdev-1.9.0}/setup.py +34 -12
  7. evdev-1.9.0/src/evdev/__init__.py +9 -0
  8. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/device.py +47 -51
  9. evdev-1.9.0/src/evdev/ecodes.py +5 -0
  10. evdev-1.7.1/evdev/ecodes.py → evdev-1.9.0/src/evdev/ecodes_runtime.py +15 -5
  11. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/eventio.py +21 -9
  12. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/eventio_async.py +2 -2
  13. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/events.py +33 -31
  14. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/evtest.py +9 -14
  15. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/ff.py +1 -1
  16. evdev-1.9.0/src/evdev/genecodes_c.py +142 -0
  17. evdev-1.9.0/src/evdev/genecodes_py.py +53 -0
  18. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/input.c +22 -51
  19. evdev-1.9.0/src/evdev/py.typed +0 -0
  20. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/uinput.c +2 -23
  21. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/uinput.py +41 -47
  22. {evdev-1.7.1 → evdev-1.9.0/src}/evdev/util.py +9 -10
  23. {evdev-1.7.1 → evdev-1.9.0/src/evdev.egg-info}/PKG-INFO +4 -3
  24. evdev-1.9.0/src/evdev.egg-info/SOURCES.txt +29 -0
  25. {evdev-1.7.1 → evdev-1.9.0}/tests/test_uinput.py +27 -6
  26. {evdev-1.7.1 → evdev-1.9.0}/tests/test_util.py +1 -1
  27. evdev-1.7.1/evdev/__init__.py +0 -10
  28. evdev-1.7.1/evdev/genecodes.py +0 -96
  29. evdev-1.7.1/evdev.egg-info/SOURCES.txt +0 -26
  30. {evdev-1.7.1 → evdev-1.9.0}/setup.cfg +0 -0
  31. {evdev-1.7.1 → evdev-1.9.0/src}/evdev.egg-info/dependency_links.txt +0 -0
  32. {evdev-1.7.1 → evdev-1.9.0/src}/evdev.egg-info/top_level.txt +0 -0
  33. {evdev-1.7.1 → evdev-1.9.0}/tests/test_ecodes.py +0 -0
  34. {evdev-1.7.1 → evdev-1.9.0}/tests/test_events.py +0 -0
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-2023 Georgi Valkov. All rights reserved.
1
+ Copyright (c) 2012-2025 Georgi Valkov. All rights reserved.
2
2
 
3
3
  Redistribution and use in source and binary forms, with or without
4
4
  modification, are permitted provided that the following conditions are
@@ -1,4 +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
4
+ exclude src/evdev/ecodes.c
5
+ include src/evdev/ecodes.py
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: evdev
3
- Version: 1.7.1
3
+ Version: 1.9.0
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>
7
- License: Copyright (c) 2012-2023 Georgi Valkov. All rights reserved.
7
+ License: Copyright (c) 2012-2025 Georgi Valkov. All rights reserved.
8
8
 
9
9
  Redistribution and use in source and binary forms, with or without
10
10
  modification, are permitted provided that the following conditions are
@@ -43,7 +43,7 @@ Classifier: Intended Audience :: Developers
43
43
  Classifier: Topic :: Software Development :: Libraries
44
44
  Classifier: License :: OSI Approved :: BSD License
45
45
  Classifier: Programming Language :: Python :: Implementation :: CPython
46
- Requires-Python: >=3.6
46
+ Requires-Python: >=3.8
47
47
  Description-Content-Type: text/markdown
48
48
  License-File: LICENSE
49
49
 
@@ -52,6 +52,7 @@ License-File: LICENSE
52
52
  <p>
53
53
  <a href="https://pypi.python.org/pypi/evdev"><img alt="pypi version" src="https://img.shields.io/pypi/v/evdev.svg"></a>
54
54
  <a href="https://github.com/gvalkov/python-evdev/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/pypi/l/evdev"></a>
55
+ <a href="https://repology.org/project/python:evdev/versions"><img alt="Packaging status" src="https://repology.org/badge/tiny-repos/python:evdev.svg"></a>
55
56
  </p>
56
57
 
57
58
  This package provides bindings to the generic input event interface in Linux.
@@ -3,6 +3,7 @@
3
3
  <p>
4
4
  <a href="https://pypi.python.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:evdev/versions"><img alt="Packaging status" src="https://repology.org/badge/tiny-repos/python:evdev.svg"></a>
6
7
  </p>
7
8
 
8
9
  This package provides bindings to the generic input event interface in Linux.
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "evdev"
7
- version = "1.7.1"
7
+ version = "1.9.0"
8
8
  description = "Bindings to the Linux input handling subsystem"
9
9
  keywords = ["evdev", "input", "uinput"]
10
10
  readme = "README.md"
11
11
  license = {file = "LICENSE"}
12
- requires-python = ">=3.6"
12
+ requires-python = ">=3.8"
13
13
  authors = [
14
14
  { name="Georgi Valkov", email="georgi.t.valkov@gmail.com" },
15
15
  ]
@@ -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.7.1"
39
+ current_version = "1.9.0"
43
40
  commit = true
44
41
  tag = true
45
42
  allow_dirty = true
@@ -49,3 +46,12 @@ filename = "pyproject.toml"
49
46
 
50
47
  [[tool.bumpversion.files]]
51
48
  filename = "docs/conf.py"
49
+
50
+ [tool.pylint.'MESSAGES CONTROL']
51
+ disable = """
52
+ no-member,
53
+ """
54
+
55
+ [tool.pylint.typecheck]
56
+ generated-members = ["evdev.ecodes.*"]
57
+ ignored-modules= ["evdev._*"]
@@ -1,14 +1,17 @@
1
1
  import os
2
2
  import sys
3
+ import shutil
3
4
  import textwrap
5
+ import platform
4
6
  from pathlib import Path
7
+ from subprocess import run
5
8
 
6
9
  from setuptools import setup, Extension, Command
7
10
  from setuptools.command import build_ext as _build_ext
8
11
 
9
12
 
10
13
  curdir = Path(__file__).resolve().parent
11
- ecodes_path = curdir / "evdev/ecodes.c"
14
+ ecodes_c_path = curdir / "src/evdev/ecodes.c"
12
15
 
13
16
 
14
17
  def create_ecodes(headers=None):
@@ -23,7 +26,11 @@ def create_ecodes(headers=None):
23
26
  include_paths.update(c_inc_path.split(":"))
24
27
 
25
28
  include_paths.add("/usr/include")
26
- 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
+
27
34
  headers = [os.path.join(path, file) for path in include_paths for file in files]
28
35
 
29
36
  headers = [header for header in headers if os.path.isfile(header)]
@@ -47,16 +54,18 @@ def create_ecodes(headers=None):
47
54
  build_ecodes --evdev-headers path/input.h:path/input-event-codes.h \\
48
55
  build_ext --include-dirs path/ \\
49
56
  install
57
+
58
+ If you want to avoid building this package from source, then please consider
59
+ installing the `evdev-binary` package instead. Keep in mind that it may not be
60
+ fully compatible with, or support all the features of your current kernel.
50
61
  """
51
62
 
52
63
  sys.stderr.write(textwrap.dedent(msg))
53
64
  sys.exit(1)
54
65
 
55
- from subprocess import run
56
-
57
- print("writing %s (using %s)" % (ecodes_path, " ".join(headers)))
58
- with ecodes_path.open("w") as fh:
59
- cmd = [sys.executable, "evdev/genecodes.py", *headers]
66
+ print("writing %s (using %s)" % (ecodes_c_path, " ".join(headers)))
67
+ with ecodes_c_path.open("w") as fh:
68
+ cmd = [sys.executable, "src/evdev/genecodes_c.py", "--ecodes", *headers]
60
69
  run(cmd, check=True, stdout=fh)
61
70
 
62
71
 
@@ -80,14 +89,27 @@ class build_ecodes(Command):
80
89
 
81
90
  class build_ext(_build_ext.build_ext):
82
91
  def has_ecodes(self):
83
- if ecodes_path.exists():
92
+ if ecodes_c_path.exists():
84
93
  print("ecodes.c already exists ... skipping build_ecodes")
85
- return not ecodes_path.exists()
94
+ return False
95
+ return True
96
+
97
+ def generate_ecodes_py(self):
98
+ ecodes_py = Path(self.build_lib) / "evdev/ecodes.py"
99
+ print(f"writing {ecodes_py}")
100
+ with ecodes_py.open("w") as fh:
101
+ cmd = [sys.executable, "-B", "src/evdev/genecodes_py.py"]
102
+ res = run(cmd, env={"PYTHONPATH": self.build_lib}, stdout=fh)
103
+
104
+ if res.returncode != 0:
105
+ print(f"failed to generate static {ecodes_py} - will use ecodes_runtime.py")
106
+ shutil.copy("src/evdev/ecodes_runtime.py", ecodes_py)
86
107
 
87
108
  def run(self):
88
109
  for cmd_name in self.get_sub_commands():
89
110
  self.run_command(cmd_name)
90
111
  _build_ext.build_ext.run(self)
112
+ self.generate_ecodes_py()
91
113
 
92
114
  sub_commands = [("build_ecodes", has_ecodes)] + _build_ext.build_ext.sub_commands
93
115
 
@@ -95,9 +117,9 @@ class build_ext(_build_ext.build_ext):
95
117
  cflags = ["-std=c99", "-Wno-error=declaration-after-statement"]
96
118
  setup(
97
119
  ext_modules=[
98
- Extension("evdev._input", sources=["evdev/input.c"], extra_compile_args=cflags),
99
- Extension("evdev._uinput", sources=["evdev/uinput.c"], extra_compile_args=cflags),
100
- 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),
101
123
  ],
102
124
  cmdclass={
103
125
  "build_ext": build_ext,
@@ -0,0 +1,9 @@
1
+ # --------------------------------------------------------------------------
2
+ # Gather everything into a single, convenient namespace.
3
+ # --------------------------------------------------------------------------
4
+
5
+ from . import ecodes, ff
6
+ from .device import AbsInfo, DeviceInfo, EvdevError, InputDevice
7
+ from .events import AbsEvent, InputEvent, KeyEvent, RelEvent, SynEvent, event_factory
8
+ from .uinput import UInput, UInputError
9
+ from .util import categorize, list_devices, resolve_ecodes, resolve_ecodes_dict
@@ -1,31 +1,19 @@
1
- # encoding: utf-8
2
-
3
- import os
4
- import warnings
5
1
  import contextlib
6
- import collections
2
+ import os
3
+ from typing import NamedTuple, Tuple, Union
7
4
 
8
- from evdev import _input, ecodes, util
9
- from evdev.events import InputEvent
5
+ from . import _input, ecodes, util
10
6
 
11
7
  try:
12
- from evdev.eventio_async import EventIO, EvdevError
8
+ from .eventio_async import EvdevError, EventIO
13
9
  except ImportError:
14
- from evdev.eventio import EventIO, EvdevError
15
-
16
-
17
- # --------------------------------------------------------------------------
18
- _AbsInfo = collections.namedtuple("AbsInfo", ["value", "min", "max", "fuzz", "flat", "resolution"])
10
+ from .eventio import EvdevError, EventIO
19
11
 
20
- _KbdInfo = collections.namedtuple("KbdInfo", ["repeat", "delay"])
21
12
 
22
- _DeviceInfo = collections.namedtuple("DeviceInfo", ["bustype", "vendor", "product", "version"])
23
-
24
-
25
- class AbsInfo(_AbsInfo):
13
+ class AbsInfo(NamedTuple):
26
14
  """Absolute axis information.
27
15
 
28
- A ``namedtuple`` used for storing absolute axis information -
16
+ A ``namedtuple`` with absolute axis information -
29
17
  corresponds to the ``input_absinfo`` struct:
30
18
 
31
19
  Attributes
@@ -61,28 +49,38 @@ class AbsInfo(_AbsInfo):
61
49
 
62
50
  """
63
51
 
52
+ value: int
53
+ min: int
54
+ max: int
55
+ fuzz: int
56
+ flat: int
57
+ resolution: int
58
+
64
59
  def __str__(self):
65
- return "val {}, min {}, max {}, fuzz {}, flat {}, res {}".format(*self)
60
+ return "value {}, min {}, max {}, fuzz {}, flat {}, res {}".format(*self) # pylint: disable=not-an-iterable
66
61
 
67
62
 
68
- class KbdInfo(_KbdInfo):
63
+ class KbdInfo(NamedTuple):
69
64
  """Keyboard repeat rate.
70
65
 
71
66
  Attributes
72
67
  ----------
73
- repeat
74
- Keyboard repeat rate in characters per second.
75
-
76
68
  delay
77
69
  Amount of time that a key must be depressed before it will start
78
70
  to repeat (in milliseconds).
71
+
72
+ repeat
73
+ Keyboard repeat rate in characters per second.
79
74
  """
80
75
 
76
+ delay: int
77
+ repeat: int
78
+
81
79
  def __str__(self):
82
- return "repeat {}, delay {}".format(*self)
80
+ return "delay {}, repeat {}".format(self.delay, self.repeat)
83
81
 
84
82
 
85
- class DeviceInfo(_DeviceInfo):
83
+ class DeviceInfo(NamedTuple):
86
84
  """
87
85
  Attributes
88
86
  ----------
@@ -92,9 +90,14 @@ class DeviceInfo(_DeviceInfo):
92
90
  version
93
91
  """
94
92
 
93
+ bustype: int
94
+ vendor: int
95
+ product: int
96
+ version: int
97
+
95
98
  def __str__(self):
96
99
  msg = "bus: {:04x}, vendor {:04x}, product {:04x}, version {:04x}"
97
- return msg.format(*self)
100
+ return msg.format(*self) # pylint: disable=not-an-iterable
98
101
 
99
102
 
100
103
  class InputDevice(EventIO):
@@ -104,7 +107,7 @@ class InputDevice(EventIO):
104
107
 
105
108
  __slots__ = ("path", "fd", "info", "name", "phys", "uniq", "_rawcapabilities", "version", "ff_effects_count")
106
109
 
107
- def __init__(self, dev):
110
+ def __init__(self, dev: Union[str, bytes, os.PathLike]):
108
111
  """
109
112
  Arguments
110
113
  ---------
@@ -115,15 +118,14 @@ class InputDevice(EventIO):
115
118
  #: Path to input device.
116
119
  self.path = dev if not hasattr(dev, "__fspath__") else dev.__fspath__()
117
120
 
118
- # Certain operations are possible only when the device is opened in
119
- # read-write mode.
121
+ # Certain operations are possible only when the device is opened in read-write mode.
120
122
  try:
121
123
  fd = os.open(dev, os.O_RDWR | os.O_NONBLOCK)
122
124
  except OSError:
123
125
  fd = os.open(dev, os.O_RDONLY | os.O_NONBLOCK)
124
126
 
125
127
  #: A non-blocking file descriptor to the device file.
126
- self.fd = fd
128
+ self.fd: int = fd
127
129
 
128
130
  # Returns (bustype, vendor, product, version, name, phys, capabilities).
129
131
  info_res = _input.ioctl_devinfo(self.fd)
@@ -132,16 +134,16 @@ class InputDevice(EventIO):
132
134
  self.info = DeviceInfo(*info_res[:4])
133
135
 
134
136
  #: The name of the event device.
135
- self.name = info_res[4]
137
+ self.name: str = info_res[4]
136
138
 
137
139
  #: The physical topology of the device.
138
- self.phys = info_res[5]
140
+ self.phys: str = info_res[5]
139
141
 
140
142
  #: The unique identifier of the device.
141
- self.uniq = info_res[6]
143
+ self.uniq: str = info_res[6]
142
144
 
143
145
  #: The evdev protocol version.
144
- self.version = _input.ioctl_EVIOCGVERSION(self.fd)
146
+ self.version: int = _input.ioctl_EVIOCGVERSION(self.fd)
145
147
 
146
148
  #: The raw dictionary of device capabilities - see `:func:capabilities()`.
147
149
  self._rawcapabilities = _input.ioctl_capabilities(self.fd)
@@ -156,7 +158,7 @@ class InputDevice(EventIO):
156
158
  except (OSError, ImportError, AttributeError):
157
159
  pass
158
160
 
159
- def _capabilities(self, absinfo=True):
161
+ def _capabilities(self, absinfo: bool = True):
160
162
  res = {}
161
163
 
162
164
  for etype, _ecodes in self._rawcapabilities.items():
@@ -174,7 +176,7 @@ class InputDevice(EventIO):
174
176
 
175
177
  return res
176
178
 
177
- def capabilities(self, verbose=False, absinfo=True):
179
+ def capabilities(self, verbose: bool = False, absinfo: bool = True):
178
180
  """
179
181
  Return the event types that this device supports as a mapping of
180
182
  supported event types to lists of handled event codes.
@@ -219,7 +221,7 @@ class InputDevice(EventIO):
219
221
  else:
220
222
  return self._capabilities(absinfo)
221
223
 
222
- def input_props(self, verbose=False):
224
+ def input_props(self, verbose: bool = False):
223
225
  """
224
226
  Get device properties and quirks.
225
227
 
@@ -240,7 +242,7 @@ class InputDevice(EventIO):
240
242
 
241
243
  return props
242
244
 
243
- def leds(self, verbose=False):
245
+ def leds(self, verbose: bool = False):
244
246
  """
245
247
  Return currently set LED keys.
246
248
 
@@ -261,7 +263,7 @@ class InputDevice(EventIO):
261
263
 
262
264
  return leds
263
265
 
264
- def set_led(self, led_num, value):
266
+ def set_led(self, led_num: int, value: int):
265
267
  """
266
268
  Set the state of the selected LED.
267
269
 
@@ -331,7 +333,7 @@ class InputDevice(EventIO):
331
333
  yield
332
334
  self.ungrab()
333
335
 
334
- def upload_effect(self, effect):
336
+ def upload_effect(self, effect: "ff.Effect"):
335
337
  """
336
338
  Upload a force feedback effect to a force feedback device.
337
339
  """
@@ -358,10 +360,10 @@ class InputDevice(EventIO):
358
360
  return KbdInfo(*_input.ioctl_EVIOCGREP(self.fd))
359
361
 
360
362
  @repeat.setter
361
- def repeat(self, value):
363
+ def repeat(self, value: Tuple[int, int]):
362
364
  return _input.ioctl_EVIOCSREP(self.fd, *value)
363
365
 
364
- def active_keys(self, verbose=False):
366
+ def active_keys(self, verbose: bool = False):
365
367
  """
366
368
  Return currently active keys.
367
369
 
@@ -384,13 +386,7 @@ class InputDevice(EventIO):
384
386
 
385
387
  return active_keys
386
388
 
387
- @property
388
- def fn(self):
389
- msg = "Please use {0}.path instead of {0}.fn".format(self.__class__.__name__)
390
- warnings.warn(msg, DeprecationWarning, stacklevel=2)
391
- return self.path
392
-
393
- def absinfo(self, axis_num):
389
+ def absinfo(self, axis_num: int):
394
390
  """
395
391
  Return current :class:`AbsInfo` for input device axis
396
392
 
@@ -406,7 +402,7 @@ class InputDevice(EventIO):
406
402
  """
407
403
  return AbsInfo(*_input.ioctl_EVIOCGABS(self.fd, axis_num))
408
404
 
409
- def set_absinfo(self, axis_num, value=None, min=None, max=None, fuzz=None, flat=None, resolution=None):
405
+ def set_absinfo(self, axis_num: int, value=None, min=None, max=None, fuzz=None, flat=None, resolution=None):
410
406
  """
411
407
  Update :class:`AbsInfo` values. Only specified values will be overwritten.
412
408
 
@@ -0,0 +1,5 @@
1
+ # When installed, this module is replaced by an ecodes.py generated at
2
+ # build time by genecodes_py.py (see build_ext in setup.py).
3
+
4
+ # This stub exists to make development of evdev itself more convenient.
5
+ from .ecodes_runtime import *
@@ -1,3 +1,4 @@
1
+ # pylint: disable=undefined-variable
1
2
  """
2
3
  This modules exposes the integer constants defined in ``linux/input.h`` and
3
4
  ``linux/input-event-codes.h``.
@@ -32,26 +33,26 @@ Keep in mind that values in reverse mappings may point to one or more event
32
33
  codes. For example::
33
34
 
34
35
  >>> evdev.ecodes.FF[80]
35
- ['FF_EFFECT_MIN', 'FF_RUMBLE']
36
+ ('FF_EFFECT_MIN', 'FF_RUMBLE')
36
37
 
37
38
  >>> evdev.ecodes.FF[81]
38
39
  'FF_PERIODIC'
39
40
  """
40
41
 
41
42
  from inspect import getmembers
42
- from evdev import _ecodes
43
43
 
44
+ from . import _ecodes
44
45
 
45
46
  #: Mapping of names to values.
46
47
  ecodes = {}
47
48
 
48
- prefixes = "KEY ABS REL SW MSC LED BTN REP SND ID EV BUS SYN FF_STATUS FF INPUT_PROP"
49
+ prefixes = "KEY ABS REL SW MSC LED BTN REP SND ID EV BUS SYN FF_STATUS FF INPUT_PROP".split()
49
50
  prev_prefix = ""
50
51
  g = globals()
51
52
 
52
53
  # eg. code: 'REL_Z', val: 2
53
54
  for code, val in getmembers(_ecodes):
54
- for prefix in prefixes.split(): # eg. 'REL'
55
+ for prefix in prefixes: # eg. 'REL'
55
56
  if code.startswith(prefix):
56
57
  ecodes[code] = val
57
58
  # FF_STATUS codes should not appear in the FF reverse mapping
@@ -70,6 +71,15 @@ for code, val in getmembers(_ecodes):
70
71
 
71
72
  prev_prefix = prefix
72
73
 
74
+
75
+ # Convert lists to tuples.
76
+ k, v = None, None
77
+ for prefix in prefixes:
78
+ for k, v in g[prefix].items():
79
+ if isinstance(v, list):
80
+ g[prefix][k] = tuple(v)
81
+
82
+
73
83
  #: Keys are a combination of all BTN and KEY codes.
74
84
  keys = {}
75
85
  keys.update(BTN)
@@ -98,4 +108,4 @@ bytype = {
98
108
  from evdev._ecodes import *
99
109
 
100
110
  # cheaper than whitelisting in an __all__
101
- del code, val, prefix, getmembers, g, d, prefixes, prev_prefix
111
+ del code, val, prefix, getmembers, g, d, k, v, prefixes, prev_prefix
@@ -1,10 +1,11 @@
1
- import os
2
1
  import fcntl
3
- import select
4
2
  import functools
3
+ import os
4
+ import select
5
+ from typing import Iterator, Union
5
6
 
6
- from evdev import _input, _uinput, ecodes, util
7
- from evdev.events import InputEvent
7
+ from . import _input, _uinput, ecodes
8
+ from .events import InputEvent
8
9
 
9
10
 
10
11
  # --------------------------------------------------------------------------
@@ -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,19 +60,20 @@ 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:
73
74
  yield InputEvent(*event)
74
75
 
76
+ # pylint: disable=no-self-argument
75
77
  def need_write(func):
76
78
  """
77
79
  Decorator that raises :class:`EvdevError` if there is no write access to the
@@ -82,6 +84,7 @@ class EventIO:
82
84
  def wrapper(*args):
83
85
  fd = args[0].fd
84
86
  if fcntl.fcntl(fd, fcntl.F_GETFL) & os.O_RDWR:
87
+ # pylint: disable=not-callable
85
88
  return func(*args)
86
89
  msg = 'no write access to device "%s"' % args[0].path
87
90
  raise EvdevError(msg)
@@ -112,7 +115,7 @@ class EventIO:
112
115
  self.write(event.type, event.code, event.value)
113
116
 
114
117
  @need_write
115
- def write(self, etype, code, value):
118
+ def write(self, etype: int, code: int, value: int):
116
119
  """
117
120
  Inject an input event into the input subsystem. Events are
118
121
  queued until a synchronization event is received.
@@ -136,5 +139,14 @@ class EventIO:
136
139
 
137
140
  _uinput.write(self.fd, etype, code, value)
138
141
 
142
+ def syn(self):
143
+ """
144
+ Inject a ``SYN_REPORT`` event into the input subsystem. Events
145
+ queued by :func:`write()` will be fired. If possible, events
146
+ will be merged into an 'atomic' event.
147
+ """
148
+
149
+ self.write(ecodes.EV_SYN, ecodes.SYN_REPORT, 0)
150
+
139
151
  def close(self):
140
152
  pass
@@ -1,10 +1,10 @@
1
1
  import asyncio
2
2
  import select
3
3
 
4
- from evdev import eventio
4
+ from . import eventio
5
5
 
6
6
  # needed for compatibility
7
- from evdev.eventio import EvdevError
7
+ from .eventio import EvdevError
8
8
 
9
9
 
10
10
  class EventIO(eventio.EventIO):