Use python black for all pyhon file formatting
authorPeter Hutterer <peter.hutterer@who-t.net>
Mon, 25 Jan 2021 03:12:25 +0000 (13:12 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Thu, 28 Jan 2021 21:02:45 +0000 (21:02 +0000)
Let's enforce a consistent (and verifiable) style everywhere.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
12 files changed:
.gitlab-ci.yml
.gitlab-ci/ci.template
.gitlab-ci/meson-junit-report.py
tools/libinput-analyze-per-slot-delta.py
tools/libinput-measure-fuzz.py
tools/libinput-measure-touch-size.py
tools/libinput-measure-touchpad-pressure.py
tools/libinput-measure-touchpad-size.py
tools/libinput-measure-touchpad-tap.py
tools/libinput-record-verify-yaml.py
tools/libinput-replay
tools/test_tool_option_parsing.py

index 04f235f4dbf7e36e1afdc4dc15391a5361e1350f..821722d3c8ce4d562506f8bf0e12b5526d9b7b11 100644 (file)
@@ -747,13 +747,14 @@ usr-bin-env-python@fedora:33:
         /bin/false
       fi
 
-flake8@fedora:33:
+python-format@fedora:33:
   extends:
     - .fedora-build@template
   before_script:
-    - dnf install -y python3-flake8
+    - dnf install -y black
   script:
-    - flake8-3 --ignore=W501,E501,W504,E741 $(git grep -l '^#!/usr/bin/env python3')
+    - black $(git grep -l '^#!/usr/bin/env python3')
+    - git diff --exit-code || (echo "Please run Black against all Python files" && false)
 
 
 #
index 285325420f80b24e836aaff445af72d6f08550c2..2d6566a2031040453e0c54d32b27a765d3f8734f 100644 (file)
@@ -542,13 +542,14 @@ usr-bin-env-python@{{distro.name}}:{{version}}:
         /bin/false
       fi
 
-flake8@{{distro.name}}:{{version}}:
+python-format@{{distro.name}}:{{version}}:
   extends:
     - .{{distro.name}}-build@template
   before_script:
-    - dnf install -y python3-flake8
+    - dnf install -y black
   script:
-    - flake8-3 --ignore=W501,E501,W504,E741 $(git grep -l '^#!/usr/bin/env python3')
+    - black $(git grep -l '^#!/usr/bin/env python3')
+    - git diff --exit-code || (echo "Please run Black against all Python files" && false)
 
 {% endfor %}
 
index 542065be741053fd6f8673d35622c30b4fa57d6a..aad52f4fc1bd43107f42cf32160dea204d93db76 100755 (executable)
@@ -12,95 +12,106 @@ import json
 import sys
 import xml.etree.ElementTree as ET
 
-aparser = argparse.ArgumentParser(description='Turns a Meson test log into a JUnit report')
-aparser.add_argument('--project-name', metavar='NAME',
-                     help='The project name',
-                     default='unknown')
-aparser.add_argument('--job-id', metavar='ID',
-                     help='The job ID for the report',
-                     default='Unknown')
-aparser.add_argument('--branch', metavar='NAME',
-                     help='Branch of the project being tested',
-                     default='master')
-aparser.add_argument('--output', metavar='FILE',
-                     help='The output file, stdout by default',
-                     type=argparse.FileType('w', encoding='UTF-8'),
-                     default=sys.stdout)
-aparser.add_argument('infile', metavar='FILE',
-                     help='The input testlog.json, stdin by default',
-                     type=argparse.FileType('r', encoding='UTF-8'),
-                     default=sys.stdin)
+aparser = argparse.ArgumentParser(
+    description="Turns a Meson test log into a JUnit report"
+)
+aparser.add_argument(
+    "--project-name", metavar="NAME", help="The project name", default="unknown"
+)
+aparser.add_argument(
+    "--job-id", metavar="ID", help="The job ID for the report", default="Unknown"
+)
+aparser.add_argument(
+    "--branch",
+    metavar="NAME",
+    help="Branch of the project being tested",
+    default="master",
+)
+aparser.add_argument(
+    "--output",
+    metavar="FILE",
+    help="The output file, stdout by default",
+    type=argparse.FileType("w", encoding="UTF-8"),
+    default=sys.stdout,
+)
+aparser.add_argument(
+    "infile",
+    metavar="FILE",
+    help="The input testlog.json, stdin by default",
+    type=argparse.FileType("r", encoding="UTF-8"),
+    default=sys.stdin,
+)
 
 args = aparser.parse_args()
 
 outfile = args.output
 
-testsuites = ET.Element('testsuites')
-testsuites.set('id', '{}/{}'.format(args.job_id, args.branch))
-testsuites.set('package', args.project_name)
-testsuites.set('timestamp', datetime.datetime.utcnow().isoformat(timespec='minutes'))
+testsuites = ET.Element("testsuites")
+testsuites.set("id", "{}/{}".format(args.job_id, args.branch))
+testsuites.set("package", args.project_name)
+testsuites.set("timestamp", datetime.datetime.utcnow().isoformat(timespec="minutes"))
 
 suites = {}
 for line in args.infile:
     data = json.loads(line)
-    (full_suite, unit_name) = data['name'].split(' / ')
-    (project_name, suite_name) = full_suite.split(':')
+    (full_suite, unit_name) = data["name"].split(" / ")
+    (project_name, suite_name) = full_suite.split(":")
 
-    duration = data['duration']
-    return_code = data['returncode']
-    log = data['stdout']
+    duration = data["duration"]
+    return_code = data["returncode"]
+    log = data["stdout"]
 
     unit = {
-        'suite': suite_name,
-        'name': unit_name,
-        'duration': duration,
-        'returncode': return_code,
-        'stdout': log,
+        "suite": suite_name,
+        "name": unit_name,
+        "duration": duration,
+        "returncode": return_code,
+        "stdout": log,
     }
 
     units = suites.setdefault(suite_name, [])
     units.append(unit)
 
 for name, units in suites.items():
-    print('Processing suite {} (units: {})'.format(name, len(units)))
+    print("Processing suite {} (units: {})".format(name, len(units)))
 
     def if_failed(unit):
-        if unit['returncode'] != 0:
+        if unit["returncode"] != 0:
             return True
         return False
 
     def if_succeded(unit):
-        if unit['returncode'] == 0:
+        if unit["returncode"] == 0:
             return True
         return False
 
     successes = list(filter(if_succeded, units))
     failures = list(filter(if_failed, units))
-    print(' - {}: {} pass, {} fail'.format(name, len(successes), len(failures)))
+    print(" - {}: {} pass, {} fail".format(name, len(successes), len(failures)))
 
-    testsuite = ET.SubElement(testsuites, 'testsuite')
-    testsuite.set('name', '{}/{}'.format(args.project_name, name))
-    testsuite.set('tests', str(len(units)))
-    testsuite.set('errors', str(len(failures)))
-    testsuite.set('failures', str(len(failures)))
+    testsuite = ET.SubElement(testsuites, "testsuite")
+    testsuite.set("name", "{}/{}".format(args.project_name, name))
+    testsuite.set("tests", str(len(units)))
+    testsuite.set("errors", str(len(failures)))
+    testsuite.set("failures", str(len(failures)))
 
     for unit in successes:
-        testcase = ET.SubElement(testsuite, 'testcase')
-        testcase.set('classname', '{}/{}'.format(args.project_name, unit['suite']))
-        testcase.set('name', unit['name'])
-        testcase.set('time', str(unit['duration']))
+        testcase = ET.SubElement(testsuite, "testcase")
+        testcase.set("classname", "{}/{}".format(args.project_name, unit["suite"]))
+        testcase.set("name", unit["name"])
+        testcase.set("time", str(unit["duration"]))
 
     for unit in failures:
-        testcase = ET.SubElement(testsuite, 'testcase')
-        testcase.set('classname', '{}/{}'.format(args.project_name, unit['suite']))
-        testcase.set('name', unit['name'])
-        testcase.set('time', str(unit['duration']))
-
-        failure = ET.SubElement(testcase, 'failure')
-        failure.set('classname', '{}/{}'.format(args.project_name, unit['suite']))
-        failure.set('name', unit['name'])
-        failure.set('type', 'error')
-        failure.text = unit['stdout']
-
-output = ET.tostring(testsuites, encoding='unicode')
+        testcase = ET.SubElement(testsuite, "testcase")
+        testcase.set("classname", "{}/{}".format(args.project_name, unit["suite"]))
+        testcase.set("name", unit["name"])
+        testcase.set("time", str(unit["duration"]))
+
+        failure = ET.SubElement(testcase, "failure")
+        failure.set("classname", "{}/{}".format(args.project_name, unit["suite"]))
+        failure.set("name", unit["name"])
+        failure.set("type", "error")
+        failure.text = unit["stdout"]
+
+output = ET.tostring(testsuites, encoding="unicode")
 outfile.write(output)
index 1cf181d0883ad466379b276788cbd0661d39eff6..8379ff19f2a16e235d36a0f754966ce9d9d55207 100755 (executable)
@@ -36,15 +36,16 @@ import yaml
 import libevdev
 
 
-COLOR_RESET = '\x1b[0m'
-COLOR_RED = '\x1b[6;31m'
+COLOR_RESET = "\x1b[0m"
+COLOR_RED = "\x1b[6;31m"
 
 
-class SlotFormatter():
+class SlotFormatter:
     width = 16
 
-    def __init__(self, is_absolute=False, resolution=None,
-                 threshold=None, ignore_below=None):
+    def __init__(
+        self, is_absolute=False, resolution=None, threshold=None, ignore_below=None
+    ):
         self.threshold = threshold
         self.ignore_below = ignore_below
         self.resolution = resolution
@@ -54,19 +55,19 @@ class SlotFormatter():
         self.filtered = False
 
     def __str__(self):
-        return ' | '.join(self.slots)
+        return " | ".join(self.slots)
 
     def format_slot(self, slot):
         if slot.state == SlotState.BEGIN:
-            self.slots.append('+++++++'.center(self.width))
+            self.slots.append("+++++++".center(self.width))
             self.have_data = True
         elif slot.state == SlotState.END:
-            self.slots.append('-------'.center(self.width))
+            self.slots.append("-------".center(self.width))
             self.have_data = True
         elif slot.state == SlotState.NONE:
-            self.slots.append(('*' * (self.width - 2)).center(self.width))
+            self.slots.append(("*" * (self.width - 2)).center(self.width))
         elif not slot.dirty:
-            self.slots.append(' '.center(self.width))
+            self.slots.append(" ".center(self.width))
         else:
             if self.resolution is not None:
                 dx, dy = slot.dx / self.resolution[0], slot.dy / self.resolution[1]
@@ -81,35 +82,39 @@ class SlotFormatter():
                 else:
                     t = t * 180.0 / math.pi
 
-                directions = ['↖↑', '↖←', '↙←', '↙↓', '↓↘', '→↘', '→↗', '↑↗']
+                directions = ["↖↑", "↖←", "↙←", "↙↓", "↓↘", "→↘", "→↗", "↑↗"]
                 direction = directions[int(t / 45)]
             elif dy == 0:
                 if dx < 0:
-                    direction = '←←'
+                    direction = "←←"
                 else:
-                    direction = '→→'
+                    direction = "→→"
             else:
                 if dy < 0:
-                    direction = '↑↑'
+                    direction = "↑↑"
                 else:
-                    direction = '↓↓'
+                    direction = "↓↓"
 
-            color = ''
-            reset = ''
+            color = ""
+            reset = ""
             if not self.is_absolute:
                 if self.ignore_below is not None or self.threshold is not None:
                     dist = math.hypot(dx, dy)
                     if self.ignore_below is not None and dist < self.ignore_below:
-                        self.slots.append(' '.center(self.width))
+                        self.slots.append(" ".center(self.width))
                         self.filtered = True
                         return
                     if self.threshold is not None and dist >= self.threshold:
                         color = COLOR_RED
                         reset = COLOR_RESET
                 if isinstance(dx, int) and isinstance(dy, int):
-                    string = "{} {}{:+4d}/{:+4d}{}".format(direction, color, dx, dy, reset)
+                    string = "{} {}{:+4d}/{:+4d}{}".format(
+                        direction, color, dx, dy, reset
+                    )
                 else:
-                    string = "{} {}{:+3.2f}/{:+03.2f}{}".format(direction, color, dx, dy, reset)
+                    string = "{} {}{:+3.2f}/{:+03.2f}{}".format(
+                        direction, color, dx, dy, reset
+                    )
             else:
                 x, y = slot.x, slot.y
                 string = "{} {}{:4d}/{:4d}{}".format(direction, color, x, y, reset)
@@ -144,23 +149,46 @@ def main(argv):
     slots = []
     xres, yres = 1, 1
 
-    parser = argparse.ArgumentParser(description="Measure delta between event frames for each slot")
-    parser.add_argument("--use-mm", action='store_true', help="Use mm instead of device deltas")
-    parser.add_argument("--use-st", action='store_true', help="Use ABS_X/ABS_Y instead of ABS_MT_POSITION_X/Y")
-    parser.add_argument("--use-absolute", action='store_true', help="Use absolute coordinates, not deltas")
-    parser.add_argument("path", metavar="recording",
-                        nargs=1, help="Path to libinput-record YAML file")
-    parser.add_argument("--threshold", type=float, default=None, help="Mark any delta above this threshold")
-    parser.add_argument("--ignore-below", type=float, default=None, help="Ignore any delta below this threshold")
+    parser = argparse.ArgumentParser(
+        description="Measure delta between event frames for each slot"
+    )
+    parser.add_argument(
+        "--use-mm", action="store_true", help="Use mm instead of device deltas"
+    )
+    parser.add_argument(
+        "--use-st",
+        action="store_true",
+        help="Use ABS_X/ABS_Y instead of ABS_MT_POSITION_X/Y",
+    )
+    parser.add_argument(
+        "--use-absolute",
+        action="store_true",
+        help="Use absolute coordinates, not deltas",
+    )
+    parser.add_argument(
+        "path", metavar="recording", nargs=1, help="Path to libinput-record YAML file"
+    )
+    parser.add_argument(
+        "--threshold",
+        type=float,
+        default=None,
+        help="Mark any delta above this threshold",
+    )
+    parser.add_argument(
+        "--ignore-below",
+        type=float,
+        default=None,
+        help="Ignore any delta below this threshold",
+    )
     args = parser.parse_args()
 
     if not sys.stdout.isatty():
-        COLOR_RESET = ''
-        COLOR_RED = ''
+        COLOR_RESET = ""
+        COLOR_RED = ""
 
     yml = yaml.safe_load(open(args.path[0]))
-    device = yml['devices'][0]
-    absinfo = device['evdev']['absinfo']
+    device = yml["devices"][0]
+    absinfo = device["evdev"]["absinfo"]
     try:
         nslots = absinfo[libevdev.EV_ABS.ABS_MT_SLOT.value][1] + 1
     except KeyError:
@@ -195,11 +223,15 @@ def main(argv):
 
     nskipped_lines = 0
 
-    for event in device['events']:
-        for evdev in event['evdev']:
+    for event in device["events"]:
+        for evdev in event["evdev"]:
             s = slots[slot]
-            e = libevdev.InputEvent(code=libevdev.evbit(evdev[2], evdev[3]),
-                                    value=evdev[4], sec=evdev[0], usec=evdev[1])
+            e = libevdev.InputEvent(
+                code=libevdev.evbit(evdev[2], evdev[3]),
+                value=evdev[4],
+                sec=evdev[0],
+                usec=evdev[1],
+            )
 
             if e.code in tool_bits:
                 tool_bits[e.code] = e.value
@@ -208,8 +240,10 @@ def main(argv):
                 # Note: this relies on the EV_KEY events to come in before the
                 # x/y events, otherwise the last/first event in each slot will
                 # be wrong.
-                if (e.code == libevdev.EV_KEY.BTN_TOOL_FINGER or
-                        e.code == libevdev.EV_KEY.BTN_TOOL_PEN):
+                if (
+                    e.code == libevdev.EV_KEY.BTN_TOOL_FINGER
+                    or e.code == libevdev.EV_KEY.BTN_TOOL_PEN
+                ):
                     slot = 0
                     s = slots[slot]
                     s.dirty = True
@@ -251,7 +285,7 @@ def main(argv):
                     s.dirty = True
                     # bcm5974 cycles through slot numbers, so let's say all below
                     # our current slot number was used
-                    for sl in slots[:slot + 1]:
+                    for sl in slots[: slot + 1]:
                         sl.used = True
                 elif e.code == libevdev.EV_ABS.ABS_MT_TRACKING_ID:
                     if e.value == -1:
@@ -290,11 +324,11 @@ def main(argv):
                     last_time = t
 
                 tools = [
-                    (libevdev.EV_KEY.BTN_TOOL_QUINTTAP, 'QIN'),
-                    (libevdev.EV_KEY.BTN_TOOL_QUADTAP, 'QAD'),
-                    (libevdev.EV_KEY.BTN_TOOL_TRIPLETAP, 'TRI'),
-                    (libevdev.EV_KEY.BTN_TOOL_DOUBLETAP, 'DBL'),
-                    (libevdev.EV_KEY.BTN_TOUCH, 'TOU'),
+                    (libevdev.EV_KEY.BTN_TOOL_QUINTTAP, "QIN"),
+                    (libevdev.EV_KEY.BTN_TOOL_QUADTAP, "QAD"),
+                    (libevdev.EV_KEY.BTN_TOOL_TRIPLETAP, "TRI"),
+                    (libevdev.EV_KEY.BTN_TOOL_DOUBLETAP, "DBL"),
+                    (libevdev.EV_KEY.BTN_TOUCH, "TOU"),
                 ]
 
                 for bit, string in tools:
@@ -302,12 +336,14 @@ def main(argv):
                         tool_state = string
                         break
                 else:
-                    tool_state = '   '
-
-                fmt = SlotFormatter(is_absolute=args.use_absolute,
-                                    resolution=(xres, yres) if args.use_mm else None,
-                                    threshold=args.threshold,
-                                    ignore_below=args.ignore_below)
+                    tool_state = "   "
+
+                fmt = SlotFormatter(
+                    is_absolute=args.use_absolute,
+                    resolution=(xres, yres) if args.use_mm else None,
+                    threshold=args.threshold,
+                    ignore_below=args.ignore_below,
+                )
                 for sl in [s for s in slots if s.used]:
                     fmt.format_slot(sl)
 
@@ -323,11 +359,21 @@ def main(argv):
                     if nskipped_lines > 0:
                         print("")
                         nskipped_lines = 0
-                    print("{:2d}.{:06d} {:+5d}ms {}: {}".format(e.sec, e.usec, tdelta, tool_state, fmt))
+                    print(
+                        "{:2d}.{:06d} {:+5d}ms {}: {}".format(
+                            e.sec, e.usec, tdelta, tool_state, fmt
+                        )
+                    )
                 elif fmt.filtered:
                     nskipped_lines += 1
-                    print("\r", " " * 21, "... {} below threshold".format(nskipped_lines), flush=True, end='')
+                    print(
+                        "\r",
+                        " " * 21,
+                        "... {} below threshold".format(nskipped_lines),
+                        flush=True,
+                        end="",
+                    )
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main(sys.argv)
index 6cb9f893e8d744eda508fc18230ce89451ddbd42..223a4cdf534c6358b239ca771122dad55435876c 100755 (executable)
@@ -28,26 +28,29 @@ import os
 import sys
 import argparse
 import subprocess
+
 try:
     import libevdev
     import pyudev
 except ModuleNotFoundError as e:
-    print('Error: {}'.format(str(e)), file=sys.stderr)
-    print('One or more python modules are missing. Please install those '
-          'modules and re-run this tool.')
+    print("Error: {}".format(str(e)), file=sys.stderr)
+    print(
+        "One or more python modules are missing. Please install those "
+        "modules and re-run this tool."
+    )
     sys.exit(1)
 
 
-DEFAULT_HWDB_FILE = '/usr/lib/udev/hwdb.d/60-evdev.hwdb'
-OVERRIDE_HWDB_FILE = '/etc/udev/hwdb.d/99-touchpad-fuzz-override.hwdb'
+DEFAULT_HWDB_FILE = "/usr/lib/udev/hwdb.d/60-evdev.hwdb"
+OVERRIDE_HWDB_FILE = "/etc/udev/hwdb.d/99-touchpad-fuzz-override.hwdb"
 
 
 class tcolors:
-    GREEN = '\033[92m'
-    RED = '\033[91m'
-    YELLOW = '\033[93m'
-    BOLD = '\033[1m'
-    NORMAL = '\033[0m'
+    GREEN = "\033[92m"
+    RED = "\033[91m"
+    YELLOW = "\033[93m"
+    BOLD = "\033[1m"
+    NORMAL = "\033[0m"
 
 
 def print_bold(msg, **kwargs):
@@ -81,44 +84,53 @@ class Device(libevdev.Device):
         else:
             self.path = path
 
-        fd = open(self.path, 'rb')
+        fd = open(self.path, "rb")
         super().__init__(fd)
         context = pyudev.Context()
         self.udev_device = pyudev.Devices.from_device_file(context, self.path)
 
     def find_touch_device(self):
         context = pyudev.Context()
-        for device in context.list_devices(subsystem='input'):
-            if not device.get('ID_INPUT_TOUCHPAD', 0):
+        for device in context.list_devices(subsystem="input"):
+            if not device.get("ID_INPUT_TOUCHPAD", 0):
                 continue
 
-            if not device.device_node or \
-                    not device.device_node.startswith('/dev/input/event'):
+            if not device.device_node or not device.device_node.startswith(
+                "/dev/input/event"
+            ):
                 continue
 
             return device.device_node
 
-        print('Unable to find a touch device.', file=sys.stderr)
+        print("Unable to find a touch device.", file=sys.stderr)
         sys.exit(1)
 
     def check_property(self):
-        '''Return a tuple of (xfuzz, yfuzz) with the fuzz as set in the libinput
-           property. Returns None if the property doesn't exist'''
+        """Return a tuple of (xfuzz, yfuzz) with the fuzz as set in the libinput
+        property. Returns None if the property doesn't exist"""
 
         axes = {
-            0x00: self.udev_device.get('LIBINPUT_FUZZ_00'),
-            0x01: self.udev_device.get('LIBINPUT_FUZZ_01'),
-            0x35: self.udev_device.get('LIBINPUT_FUZZ_35'),
-            0x36: self.udev_device.get('LIBINPUT_FUZZ_36'),
+            0x00: self.udev_device.get("LIBINPUT_FUZZ_00"),
+            0x01: self.udev_device.get("LIBINPUT_FUZZ_01"),
+            0x35: self.udev_device.get("LIBINPUT_FUZZ_35"),
+            0x36: self.udev_device.get("LIBINPUT_FUZZ_36"),
         }
 
         if axes[0x35] is not None:
             if axes[0x35] != axes[0x00]:
-                print_bold('WARNING: fuzz mismatch ABS_X: {}, ABS_MT_POSITION_X: {}'.format(axes[0x00], axes[0x35]))
+                print_bold(
+                    "WARNING: fuzz mismatch ABS_X: {}, ABS_MT_POSITION_X: {}".format(
+                        axes[0x00], axes[0x35]
+                    )
+                )
 
         if axes[0x36] is not None:
             if axes[0x36] != axes[0x01]:
-                print_bold('WARNING: fuzz mismatch ABS_Y: {}, ABS_MT_POSITION_Y: {}'.format(axes[0x01], axes[0x36]))
+                print_bold(
+                    "WARNING: fuzz mismatch ABS_Y: {}, ABS_MT_POSITION_Y: {}".format(
+                        axes[0x01], axes[0x36]
+                    )
+                )
 
         xfuzz = axes[0x35] or axes[0x00]
         yfuzz = axes[0x36] or axes[0x01]
@@ -126,27 +138,34 @@ class Device(libevdev.Device):
         if xfuzz is None and yfuzz is None:
             return None
 
-        if ((xfuzz is not None and yfuzz is None) or
-                (xfuzz is None and yfuzz is not None)):
-            raise InvalidConfigurationError('fuzz should be set for both axes')
+        if (xfuzz is not None and yfuzz is None) or (
+            xfuzz is None and yfuzz is not None
+        ):
+            raise InvalidConfigurationError("fuzz should be set for both axes")
 
         return (int(xfuzz), int(yfuzz))
 
     def check_axes(self):
-        '''
+        """
         Returns a tuple of (xfuzz, yfuzz) with the fuzz as set on the device
         axis. Returns None if no fuzz is set.
-        '''
+        """
         if not self.has(libevdev.EV_ABS.ABS_X) or not self.has(libevdev.EV_ABS.ABS_Y):
-            raise InvalidDeviceError('device does not have x/y axes')
-
-        if self.has(libevdev.EV_ABS.ABS_MT_POSITION_X) != self.has(libevdev.EV_ABS.ABS_MT_POSITION_Y):
-            raise InvalidDeviceError('device does not have both multitouch axes')
-
-        xfuzz = (self.absinfo[libevdev.EV_ABS.ABS_X].fuzz or
-                 self.absinfo[libevdev.EV_ABS.ABS_MT_POSITION_X].fuzz)
-        yfuzz = (self.absinfo[libevdev.EV_ABS.ABS_Y].fuzz or
-                 self.absinfo[libevdev.EV_ABS.ABS_MT_POSITION_Y].fuzz)
+            raise InvalidDeviceError("device does not have x/y axes")
+
+        if self.has(libevdev.EV_ABS.ABS_MT_POSITION_X) != self.has(
+            libevdev.EV_ABS.ABS_MT_POSITION_Y
+        ):
+            raise InvalidDeviceError("device does not have both multitouch axes")
+
+        xfuzz = (
+            self.absinfo[libevdev.EV_ABS.ABS_X].fuzz
+            or self.absinfo[libevdev.EV_ABS.ABS_MT_POSITION_X].fuzz
+        )
+        yfuzz = (
+            self.absinfo[libevdev.EV_ABS.ABS_Y].fuzz
+            or self.absinfo[libevdev.EV_ABS.ABS_MT_POSITION_Y].fuzz
+        )
 
         if xfuzz == 0 and yfuzz == 0:
             return None
@@ -155,13 +174,13 @@ class Device(libevdev.Device):
 
 
 def print_fuzz(what, fuzz):
-    print('  Checking {}... '.format(what), end='')
+    print("  Checking {}... ".format(what), end="")
     if fuzz is None:
-        print('not set')
+        print("not set")
     elif fuzz == (0, 0):
-        print('is zero')
+        print("is zero")
     else:
-        print('x={} y={}'.format(*fuzz))
+        print("x={} y={}".format(*fuzz))
 
 
 def handle_existing_entry(device, fuzz):
@@ -174,10 +193,10 @@ def handle_existing_entry(device, fuzz):
     # If the lines aren't in the same order in the file, it'll be a false
     # negative.
     overrides = {
-        0x00: device.udev_device.get('EVDEV_ABS_00'),
-        0x01: device.udev_device.get('EVDEV_ABS_01'),
-        0x35: device.udev_device.get('EVDEV_ABS_35'),
-        0x36: device.udev_device.get('EVDEV_ABS_36'),
+        0x00: device.udev_device.get("EVDEV_ABS_00"),
+        0x01: device.udev_device.get("EVDEV_ABS_01"),
+        0x35: device.udev_device.get("EVDEV_ABS_35"),
+        0x36: device.udev_device.get("EVDEV_ABS_36"),
     }
 
     has_existing_rules = False
@@ -188,85 +207,98 @@ def handle_existing_entry(device, fuzz):
     if not has_existing_rules:
         return False
 
-    print_red('Error! ', end='')
-    print('This device already has axis overrides defined')
-    print('')
-    print_bold('Searching for existing override...')
+    print_red("Error! ", end="")
+    print("This device already has axis overrides defined")
+    print("")
+    print_bold("Searching for existing override...")
 
     # Construct a template that looks like a hwdb entry (values only) from
     # the udev property values
-    template = [' EVDEV_ABS_00={}'.format(overrides[0x00]),
-                ' EVDEV_ABS_01={}'.format(overrides[0x01])]
+    template = [
+        " EVDEV_ABS_00={}".format(overrides[0x00]),
+        " EVDEV_ABS_01={}".format(overrides[0x01]),
+    ]
     if overrides[0x35] is not None:
-        template += [' EVDEV_ABS_35={}'.format(overrides[0x35]),
-                     ' EVDEV_ABS_36={}'.format(overrides[0x36])]
+        template += [
+            " EVDEV_ABS_35={}".format(overrides[0x35]),
+            " EVDEV_ABS_36={}".format(overrides[0x36]),
+        ]
 
-    print('Checking in {}... '.format(OVERRIDE_HWDB_FILE), end='')
+    print("Checking in {}... ".format(OVERRIDE_HWDB_FILE), end="")
     entry, prefix, lineno = check_file_for_lines(OVERRIDE_HWDB_FILE, template)
     if entry is not None:
-        print_green('found')
-        print('The existing hwdb entry can be overwritten')
+        print_green("found")
+        print("The existing hwdb entry can be overwritten")
         return False
     else:
-        print_red('not found')
-        print('Checking in {}... '.format(DEFAULT_HWDB_FILE), end='')
+        print_red("not found")
+        print("Checking in {}... ".format(DEFAULT_HWDB_FILE), end="")
         entry, prefix, lineno = check_file_for_lines(DEFAULT_HWDB_FILE, template)
         if entry is not None:
-            print_green('found')
+            print_green("found")
         else:
-            print_red('not found')
-            print('The device has a hwdb override defined but it\'s not where I expected it to be.')
-            print('Please look at the libinput documentation for more details.')
-            print('Exiting now.')
+            print_red("not found")
+            print(
+                "The device has a hwdb override defined but it's not where I expected it to be."
+            )
+            print("Please look at the libinput documentation for more details.")
+            print("Exiting now.")
             return True
 
-    print_bold('Probable entry for this device found in line {}:'.format(lineno))
-    print('\n'.join(prefix + entry))
-    print('')
+    print_bold("Probable entry for this device found in line {}:".format(lineno))
+    print("\n".join(prefix + entry))
+    print("")
 
-    print_bold('Suggested new entry for this device:')
+    print_bold("Suggested new entry for this device:")
     new_entry = []
     for i in range(0, len(template)):
-        parts = entry[i].split(':')
+        parts = entry[i].split(":")
         while len(parts) < 4:
-            parts.append('')
+            parts.append("")
         parts[3] = str(fuzz)
-        new_entry.append(':'.join(parts))
-    print('\n'.join(prefix + new_entry))
-    print('')
+        new_entry.append(":".join(parts))
+    print("\n".join(prefix + new_entry))
+    print("")
 
     # Not going to overwrite the 60-evdev.hwdb entry with this program, too
     # risky. And it may not be our device match anyway.
-    print_bold('You must now:')
-    print('\n'.join((
-        '1. Check the above suggestion for sanity. Does it match your device?',
-        '2. Open {} and amend the existing entry'.format(DEFAULT_HWDB_FILE),
-        '   as recommended above',
-        '',
-        '   The property format is:',
-        '    EVDEV_ABS_00=min:max:resolution:fuzz',
-        '',
-        '   Leave the entry as-is and only add or amend the fuzz value.',
-        '   A non-existent value can be skipped, e.g. this entry sets the ',
-        '   resolution to 32 and the fuzz to 8',
-        '    EVDEV_ABS_00=::32:8',
-        '',
-        '3. Save the edited file',
-        '4. Say Y to the next prompt')))
-
-    cont = input('Continue? [Y/n] ')
-    if cont == 'n':
+    print_bold("You must now:")
+    print(
+        "\n".join(
+            (
+                "1. Check the above suggestion for sanity. Does it match your device?",
+                "2. Open {} and amend the existing entry".format(DEFAULT_HWDB_FILE),
+                "   as recommended above",
+                "",
+                "   The property format is:",
+                "    EVDEV_ABS_00=min:max:resolution:fuzz",
+                "",
+                "   Leave the entry as-is and only add or amend the fuzz value.",
+                "   A non-existent value can be skipped, e.g. this entry sets the ",
+                "   resolution to 32 and the fuzz to 8",
+                "    EVDEV_ABS_00=::32:8",
+                "",
+                "3. Save the edited file",
+                "4. Say Y to the next prompt",
+            )
+        )
+    )
+
+    cont = input("Continue? [Y/n] ")
+    if cont == "n":
         raise KeyboardInterrupt
 
     if test_hwdb_entry(device, fuzz):
-        print_bold('Please test the new fuzz setting by restarting libinput')
-        print_bold('Then submit a pull request for this hwdb entry change to '
-                   'to systemd at http://github.com/systemd/systemd')
+        print_bold("Please test the new fuzz setting by restarting libinput")
+        print_bold(
+            "Then submit a pull request for this hwdb entry change to "
+            "to systemd at http://github.com/systemd/systemd"
+        )
     else:
-        print_bold('The new fuzz setting did not take effect.')
-        print_bold('Did you edit the correct file?')
-        print('Please look at the libinput documentation for more details.')
-        print('Exiting now.')
+        print_bold("The new fuzz setting did not take effect.")
+        print_bold("Did you edit the correct file?")
+        print("Please look at the libinput documentation for more details.")
+        print("Exiting now.")
 
     return True
 
@@ -274,47 +306,49 @@ def handle_existing_entry(device, fuzz):
 def reload_and_trigger_udev(device):
     import time
 
-    print('Running systemd-hwdb update')
-    subprocess.run(['systemd-hwdb', 'update'], check=True)
-    syspath = device.path.replace('/dev/input/', '/sys/class/input/')
+    print("Running systemd-hwdb update")
+    subprocess.run(["systemd-hwdb", "update"], check=True)
+    syspath = device.path.replace("/dev/input/", "/sys/class/input/")
     time.sleep(2)
-    print('Running udevadm trigger {}'.format(syspath))
-    subprocess.run(['udevadm', 'trigger', syspath], check=True)
+    print("Running udevadm trigger {}".format(syspath))
+    subprocess.run(["udevadm", "trigger", syspath], check=True)
     time.sleep(2)
 
 
 def test_hwdb_entry(device, fuzz):
     reload_and_trigger_udev(device)
-    print_bold('Testing... ', end='')
+    print_bold("Testing... ", end="")
 
     d = Device(device.path)
     f = d.check_axes()
     if f is not None:
         if f == (fuzz, fuzz):
-            print_yellow('Warning')
-            print_bold('The hwdb applied to the device but libinput\'s udev '
-                       'rules have not picked it up. This should only happen'
-                       'if libinput is not installed')
+            print_yellow("Warning")
+            print_bold(
+                "The hwdb applied to the device but libinput's udev "
+                "rules have not picked it up. This should only happen"
+                "if libinput is not installed"
+            )
             return True
         else:
-            print_red('Error')
+            print_red("Error")
             return False
     else:
         f = d.check_property()
         if f is not None and f == (fuzz, fuzz):
-            print_green('Success')
+            print_green("Success")
             return True
         else:
-            print_red('Error')
+            print_red("Error")
             return False
 
 
 def check_file_for_lines(path, template):
-    '''
+    """
     Checks file at path for the lines given in template. If found, the
     return value is a tuple of the matching lines and the prefix (i.e. the
     two lines before the matching lines)
-    '''
+    """
     try:
         lines = [l[:-1] for l in open(path).readlines()]
         idx = -1
@@ -322,12 +356,12 @@ def check_file_for_lines(path, template):
             while idx < len(lines) - 1:
                 idx += 1
                 line = lines[idx]
-                if not line.startswith(' EVDEV_ABS_00'):
+                if not line.startswith(" EVDEV_ABS_00"):
                     continue
-                if lines[idx:idx + len(template)] != template:
+                if lines[idx : idx + len(template)] != template:
                     continue
 
-                return (lines[idx:idx + len(template)], lines[idx - 2:idx], idx)
+                return (lines[idx : idx + len(template)], lines[idx - 2 : idx], idx)
 
         except IndexError:
             pass
@@ -338,43 +372,51 @@ def check_file_for_lines(path, template):
 
 
 def write_udev_rule(device, fuzz):
-    '''Write out a udev rule that may match the device, run udevadm trigger and
+    """Write out a udev rule that may match the device, run udevadm trigger and
     check if the udev rule worked. Of course, there's plenty to go wrong...
-    '''
-    print('')
-    print_bold('Guessing a udev rule to overwrite the fuzz')
+    """
+    print("")
+    print_bold("Guessing a udev rule to overwrite the fuzz")
 
     # Some devices match better on pvr, others on pn, so we get to try both. yay
-    modalias = open('/sys/class/dmi/id/modalias').readlines()[0]
-    ms = modalias.split(':')
+    modalias = open("/sys/class/dmi/id/modalias").readlines()[0]
+    ms = modalias.split(":")
     svn, pn, pvr = None, None, None
     for m in ms:
-        if m.startswith('svn'):
+        if m.startswith("svn"):
             svn = m
-        elif m.startswith('pn'):
+        elif m.startswith("pn"):
             pn = m
-        elif m.startswith('pvr'):
+        elif m.startswith("pvr"):
             pvr = m
 
     # Let's print out both to inform and/or confuse the user
-    template = '\n'.join(('# {} {}',
-                          'evdev:name:{}:dmi:*:{}*:{}*:',
-                          ' EVDEV_ABS_00=:::{}',
-                          ' EVDEV_ABS_01=:::{}',
-                          ' EVDEV_ABS_35=:::{}',
-                          ' EVDEV_ABS_36=:::{}',
-                          ''))
-    rule1 = template.format(svn[3:], device.name, device.name, svn, pvr, fuzz, fuzz, fuzz, fuzz)
-    rule2 = template.format(svn[3:], device.name, device.name, svn, pn, fuzz, fuzz, fuzz, fuzz)
-
-    print('Full modalias is: {}'.format(modalias))
+    template = "\n".join(
+        (
+            "# {} {}",
+            "evdev:name:{}:dmi:*:{}*:{}*:",
+            " EVDEV_ABS_00=:::{}",
+            " EVDEV_ABS_01=:::{}",
+            " EVDEV_ABS_35=:::{}",
+            " EVDEV_ABS_36=:::{}",
+            "",
+        )
+    )
+    rule1 = template.format(
+        svn[3:], device.name, device.name, svn, pvr, fuzz, fuzz, fuzz, fuzz
+    )
+    rule2 = template.format(
+        svn[3:], device.name, device.name, svn, pn, fuzz, fuzz, fuzz, fuzz
+    )
+
+    print("Full modalias is: {}".format(modalias))
     print()
-    print_bold('Suggested udev rule, option 1:')
+    print_bold("Suggested udev rule, option 1:")
     print(rule1)
     print()
-    print_bold('Suggested udev rule, option 2:')
+    print_bold("Suggested udev rule, option 2:")
     print(rule2)
-    print('')
+    print("")
 
     # The weird hwdb matching behavior means we match on the least specific
     # rule (i.e. most wildcards) first although that was supposed to be fixed in
@@ -386,77 +428,88 @@ def write_udev_rule(device, fuzz):
         return
 
     while True:
-        print_bold('Wich rule do you want to to test? 1 or 2? ', end='')
-        yesno = input('Ctrl+C to exit ')
+        print_bold("Wich rule do you want to to test? 1 or 2? ", end="")
+        yesno = input("Ctrl+C to exit ")
 
-        if yesno == '1':
+        if yesno == "1":
             rule = rule1
             break
-        elif yesno == '2':
+        elif yesno == "2":
             rule = rule2
             break
 
     fname = OVERRIDE_HWDB_FILE
     try:
-        fd = open(fname, 'x')
+        fd = open(fname, "x")
     except FileExistsError:
-        yesno = input('File {} exists, overwrite? [Y/n] '.format(fname))
-        if yesno.lower == 'n':
+        yesno = input("File {} exists, overwrite? [Y/n] ".format(fname))
+        if yesno.lower == "n":
             return
 
-        fd = open(fname, 'w')
+        fd = open(fname, "w")
 
-    fd.write('# File generated by libinput measure fuzz\n\n')
+    fd.write("# File generated by libinput measure fuzz\n\n")
     fd.write(rule)
     fd.close()
 
     if test_hwdb_entry(device, fuzz):
-        print('Your hwdb override file is in {}'.format(fname))
-        print_bold('Please test the new fuzz setting by restarting libinput')
-        print_bold('Then submit a pull request for this hwdb entry to '
-                   'systemd at http://github.com/systemd/systemd')
+        print("Your hwdb override file is in {}".format(fname))
+        print_bold("Please test the new fuzz setting by restarting libinput")
+        print_bold(
+            "Then submit a pull request for this hwdb entry to "
+            "systemd at http://github.com/systemd/systemd"
+        )
     else:
-        print('The hwdb entry failed to apply to the device.')
-        print('Removing hwdb file again.')
+        print("The hwdb entry failed to apply to the device.")
+        print("Removing hwdb file again.")
         os.remove(fname)
         reload_and_trigger_udev(device)
-        print_bold('What now?')
-        print('1. Re-run this program and try the other suggested udev rule. If that fails,')
-        print('2. File a bug with the suggested udev rule at http://github.com/systemd/systemd')
+        print_bold("What now?")
+        print(
+            "1. Re-run this program and try the other suggested udev rule. If that fails,"
+        )
+        print(
+            "2. File a bug with the suggested udev rule at http://github.com/systemd/systemd"
+        )
 
 
 def main(args):
     parser = argparse.ArgumentParser(
-        description='Print fuzz settings and/or suggest udev rules for the fuzz to be adjusted.'
+        description="Print fuzz settings and/or suggest udev rules for the fuzz to be adjusted."
+    )
+    parser.add_argument(
+        "path",
+        metavar="/dev/input/event0",
+        nargs="?",
+        type=str,
+        help="Path to device (optional)",
     )
-    parser.add_argument('path', metavar='/dev/input/event0',
-                        nargs='?', type=str, help='Path to device (optional)')
-    parser.add_argument('--fuzz', type=int, help='Suggested fuzz')
+    parser.add_argument("--fuzz", type=int, help="Suggested fuzz")
     args = parser.parse_args()
 
     try:
         device = Device(args.path)
-        print_bold('Using {}: {}'.format(device.name, device.path))
+        print_bold("Using {}: {}".format(device.name, device.path))
 
         fuzz = device.check_property()
-        print_fuzz('udev property', fuzz)
+        print_fuzz("udev property", fuzz)
 
         fuzz = device.check_axes()
-        print_fuzz('axes', fuzz)
+        print_fuzz("axes", fuzz)
 
         userfuzz = args.fuzz
         if userfuzz is not None:
             write_udev_rule(device, userfuzz)
 
     except PermissionError:
-        print('Permission denied, please re-run as root')
+        print("Permission denied, please re-run as root")
     except InvalidConfigurationError as e:
-        print('Error: {}'.format(e))
+        print("Error: {}".format(e))
     except InvalidDeviceError as e:
-        print('Error: {}'.format(e))
+        print("Error: {}".format(e))
     except KeyboardInterrupt:
-        print('Exited on user request')
+        print("Exited on user request")
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main(sys.argv)
index 5d98bc28fe8a9c9749e01dd9612ce3b8e6036553..43f14cdafb598be603a882b6e6396ae8fa36b551 100755 (executable)
 import sys
 import subprocess
 import argparse
+
 try:
     import libevdev
     import pyudev
 except ModuleNotFoundError as e:
-    print('Error: {}'.format(str(e)), file=sys.stderr)
-    print('One or more python modules are missing. Please install those '
-          'modules and re-run this tool.')
+    print("Error: {}".format(str(e)), file=sys.stderr)
+    print(
+        "One or more python modules are missing. Please install those "
+        "modules and re-run this tool."
+    )
     sys.exit(1)
 
 
 class Range(object):
     """Class to keep a min/max of a value around"""
+
     def __init__(self):
-        self.min = float('inf')
-        self.max = float('-inf')
+        self.min = float("inf")
+        self.max = float("-inf")
 
     def update(self, value):
         self.min = min(self.min, value)
@@ -148,7 +152,9 @@ class TouchSequence(object):
             self.major_range.min, self.major_range.max
         )
         if self.device.has_minor:
-            s += "minor: [{:3d}..{:3d}] ".format(self.minor_range.min, self.minor_range.max)
+            s += "minor: [{:3d}..{:3d}] ".format(
+                self.minor_range.min, self.minor_range.max
+            )
         if self.was_down:
             s += " down"
         if self.was_palm:
@@ -160,10 +166,12 @@ class TouchSequence(object):
 
     def _str_state(self):
         touch = self.points[-1]
-        s = "{}, tags: {} {} {}".format(touch,
-                                        "down" if self.is_down else "    ",
-                                        "palm" if self.is_palm else "    ",
-                                        "thumb" if self.is_thumb else "     ")
+        s = "{}, tags: {} {} {}".format(
+            touch,
+            "down" if self.is_down else "    ",
+            "palm" if self.is_palm else "    ",
+            "thumb" if self.is_thumb else "     ",
+        )
         return s
 
 
@@ -178,7 +186,7 @@ class Device(libevdev.Device):
         else:
             self.path = path
 
-        fd = open(self.path, 'rb')
+        fd = open(self.path, "rb")
         super().__init__(fd)
 
         print("Using {}: {}\n".format(self.name, self.path))
@@ -200,13 +208,15 @@ class Device(libevdev.Device):
 
     def find_touch_device(self):
         context = pyudev.Context()
-        for device in context.list_devices(subsystem='input'):
-            if not device.get('ID_INPUT_TOUCHPAD', 0) and \
-               not device.get('ID_INPUT_TOUCHSCREEN', 0):
+        for device in context.list_devices(subsystem="input"):
+            if not device.get("ID_INPUT_TOUCHPAD", 0) and not device.get(
+                "ID_INPUT_TOUCHSCREEN", 0
+            ):
                 continue
 
-            if not device.device_node or \
-                    not device.device_node.startswith('/dev/input/event'):
+            if not device.device_node or not device.device_node.startswith(
+                "/dev/input/event"
+            ):
                 continue
 
             return device.device_node
@@ -215,21 +225,24 @@ class Device(libevdev.Device):
         sys.exit(1)
 
     def _init_thresholds_from_quirks(self):
-        command = ['libinput', 'quirks', 'list', self.path]
+        command = ["libinput", "quirks", "list", self.path]
         cmd = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         if cmd.returncode != 0:
-            print("Error querying quirks: {}".format(cmd.stderr.decode('utf-8')), file=sys.stderr)
+            print(
+                "Error querying quirks: {}".format(cmd.stderr.decode("utf-8")),
+                file=sys.stderr,
+            )
             return
 
-        stdout = cmd.stdout.decode('utf-8')
-        quirks = [q.split('=') for q in stdout.split('\n')]
+        stdout = cmd.stdout.decode("utf-8")
+        quirks = [q.split("=") for q in stdout.split("\n")]
 
         for q in quirks:
-            if q[0] == 'AttrPalmSizeThreshold':
+            if q[0] == "AttrPalmSizeThreshold":
                 self.palm = int(q[1])
-            elif q[0] == 'AttrTouchSizeRange':
+            elif q[0] == "AttrTouchSizeRange":
                 self.down, self.up = colon_tuple(q[1])
-            elif q[0] == 'AttrThumbSizeThreshold':
+            elif q[0] == "AttrThumbSizeThreshold":
                 self.thumb = int(q[1])
 
     def start_new_sequence(self, tracking_id):
@@ -239,13 +252,17 @@ class Device(libevdev.Device):
         return self.sequences[-1]
 
     def handle_key(self, event):
-        tapcodes = [libevdev.EV_KEY.BTN_TOOL_DOUBLETAP,
-                    libevdev.EV_KEY.BTN_TOOL_TRIPLETAP,
-                    libevdev.EV_KEY.BTN_TOOL_QUADTAP,
-                    libevdev.EV_KEY.BTN_TOOL_QUINTTAP]
+        tapcodes = [
+            libevdev.EV_KEY.BTN_TOOL_DOUBLETAP,
+            libevdev.EV_KEY.BTN_TOOL_TRIPLETAP,
+            libevdev.EV_KEY.BTN_TOOL_QUADTAP,
+            libevdev.EV_KEY.BTN_TOOL_QUINTTAP,
+        ]
         if event.code in tapcodes and event.value > 0:
-            print("\rThis tool cannot handle multiple fingers, "
-                  "output will be invalid", file=sys.stderr)
+            print(
+                "\rThis tool cannot handle multiple fingers, " "output will be invalid",
+                file=sys.stderr,
+            )
 
     def handle_abs(self, event):
         if event.matches(libevdev.EV_ABS.ABS_MT_TRACKING_ID):
@@ -271,9 +288,11 @@ class Device(libevdev.Device):
             try:
                 self.current_sequence().append(self.touch)
                 print("\r{}".format(self.current_sequence()), end="")
-                self.touch = Touch(major=self.touch.major,
-                                   minor=self.touch.minor,
-                                   orientation=self.touch.orientation)
+                self.touch = Touch(
+                    major=self.touch.major,
+                    minor=self.touch.minor,
+                    orientation=self.touch.orientation,
+                )
             except IndexError:
                 pass
 
@@ -290,8 +309,10 @@ class Device(libevdev.Device):
         print("Touch sizes used: {}:{}".format(self.down, self.up))
         print("Palm size used: {}".format(self.palm))
         print("Thumb size used: {}".format(self.thumb))
-        print("Place a single finger on the device to measure touch size.\n"
-              "Ctrl+C to exit\n")
+        print(
+            "Place a single finger on the device to measure touch size.\n"
+            "Ctrl+C to exit\n"
+        )
 
         while True:
             for event in self.events():
@@ -300,11 +321,11 @@ class Device(libevdev.Device):
 
 def colon_tuple(string):
     try:
-        ts = string.split(':')
+        ts = string.split(":")
         t = tuple([int(x) for x in ts])
         if len(t) == 2 and t[0] >= t[1]:
             return t
-    except: # noqa
+    except:  # noqa
         pass
 
     msg = "{} is not in format N:M (N >= M)".format(string)
@@ -313,13 +334,25 @@ def colon_tuple(string):
 
 def main(args):
     parser = argparse.ArgumentParser(description="Measure touch size and orientation")
-    parser.add_argument('path', metavar='/dev/input/event0',
-                        nargs='?', type=str, help='Path to device (optional)')
-    parser.add_argument('--touch-thresholds', metavar='down:up',
-                        type=colon_tuple,
-                        help='Thresholds when a touch is logically down or up')
-    parser.add_argument('--palm-threshold', metavar='t',
-                        type=int, help='Threshold when a touch is a palm')
+    parser.add_argument(
+        "path",
+        metavar="/dev/input/event0",
+        nargs="?",
+        type=str,
+        help="Path to device (optional)",
+    )
+    parser.add_argument(
+        "--touch-thresholds",
+        metavar="down:up",
+        type=colon_tuple,
+        help="Thresholds when a touch is logically down or up",
+    )
+    parser.add_argument(
+        "--palm-threshold",
+        metavar="t",
+        type=int,
+        help="Threshold when a touch is a palm",
+    )
     args = parser.parse_args()
 
     try:
@@ -337,7 +370,9 @@ def main(args):
     except (PermissionError, OSError):
         print("Error: failed to open device")
     except InvalidDeviceError as e:
-        print("This device does not have the capabilities for size-based touch detection.")
+        print(
+            "This device does not have the capabilities for size-based touch detection."
+        )
         print("Details: {}".format(e))
 
 
index a55bad0cd7bde59abf01f439bedf2870b0e2be38..24ab69e04d14b6d5faf99ddbaba5d6434b9ad3f7 100755 (executable)
 import sys
 import subprocess
 import argparse
+
 try:
     import libevdev
     import pyudev
 except ModuleNotFoundError as e:
-    print('Error: {}'.format(str(e)), file=sys.stderr)
-    print('One or more python modules are missing. Please install those '
-          'modules and re-run this tool.')
+    print("Error: {}".format(str(e)), file=sys.stderr)
+    print(
+        "One or more python modules are missing. Please install those "
+        "modules and re-run this tool."
+    )
     sys.exit(1)
 
 
@@ -48,35 +51,35 @@ class TableFormatter(object):
         return sum(self.colwidths) + 1
 
     def headers(self, args):
-        s = '|'
+        s = "|"
         align = self.ALIGNMENT - 1  # account for |
 
         for arg in args:
             # +2 because we want space left/right of text
             w = ((len(arg) + 2 + align) // align) * align
             self.colwidths.append(w + 1)
-            s += ' {:^{width}s} |'.format(arg, width=w - 2)
+            s += " {:^{width}s} |".format(arg, width=w - 2)
 
         return s
 
     def values(self, args):
-        s = '|'
+        s = "|"
         for w, arg in zip(self.colwidths, args):
             w -= 1  # width includes | separator
             if type(arg) == str:
                 # We want space margins for strings
-                s += ' {:{width}s} |'.format(arg, width=w - 2)
+                s += " {:{width}s} |".format(arg, width=w - 2)
             elif type(arg) == bool:
-                s += '{:^{width}s}|'.format('x' if arg else ' ', width=w)
+                s += "{:^{width}s}|".format("x" if arg else " ", width=w)
             else:
-                s += '{:^{width}d}|'.format(arg, width=w)
+                s += "{:^{width}d}|".format(arg, width=w)
 
         if len(args) < len(self.colwidths):
-            s += '|'.rjust(self.width - len(s), ' ')
+            s += "|".rjust(self.width - len(s), " ")
         return s
 
     def separator(self):
-        return '+' + '-' * (self.width - 2) + '+'
+        return "+" + "-" * (self.width - 2) + "+"
 
 
 fmt = TableFormatter()
@@ -84,9 +87,10 @@ fmt = TableFormatter()
 
 class Range(object):
     """Class to keep a min/max of a value around"""
+
     def __init__(self):
-        self.min = float('inf')
-        self.max = float('-inf')
+        self.min = float("inf")
+        self.max = float("-inf")
 
     def update(self, value):
         self.min = min(self.min, value)
@@ -157,19 +161,47 @@ class TouchSequence(object):
 
     def _str_summary(self):
         if not self.points:
-            return fmt.values([self.tracking_id, False, False, False, False,
-                              'No pressure values recorded'])
-
-        s = fmt.values([self.tracking_id, self.was_down, True, self.was_palm,
-                        self.was_thumb, self.prange.min, self.prange.max, 0,
-                        self.avg(), self.median()])
+            return fmt.values(
+                [
+                    self.tracking_id,
+                    False,
+                    False,
+                    False,
+                    False,
+                    "No pressure values recorded",
+                ]
+            )
+
+        s = fmt.values(
+            [
+                self.tracking_id,
+                self.was_down,
+                True,
+                self.was_palm,
+                self.was_thumb,
+                self.prange.min,
+                self.prange.max,
+                0,
+                self.avg(),
+                self.median(),
+            ]
+        )
 
         return s
 
     def _str_state(self):
-        s = fmt.values([self.tracking_id, self.is_down, not self.is_down,
-                        self.is_palm, self.is_thumb, self.prange.min,
-                        self.prange.max, self.points[-1].pressure])
+        s = fmt.values(
+            [
+                self.tracking_id,
+                self.is_down,
+                not self.is_down,
+                self.is_palm,
+                self.is_thumb,
+                self.prange.min,
+                self.prange.max,
+                self.points[-1].pressure,
+            ]
+        )
         return s
 
 
@@ -184,7 +216,7 @@ class Device(libevdev.Device):
         else:
             self.path = path
 
-        fd = open(self.path, 'rb')
+        fd = open(self.path, "rb")
         super().__init__(fd)
 
         print("Using {}: {}\n".format(self.name, self.path))
@@ -195,7 +227,9 @@ class Device(libevdev.Device):
             absinfo = self.absinfo[libevdev.EV_ABS.ABS_PRESSURE]
             self.has_mt_pressure = False
             if absinfo is None:
-                raise InvalidDeviceError("Device does not have ABS_PRESSURE or ABS_MT_PRESSURE")
+                raise InvalidDeviceError(
+                    "Device does not have ABS_PRESSURE or ABS_MT_PRESSURE"
+                )
 
         prange = absinfo.maximum - absinfo.minimum
 
@@ -210,12 +244,13 @@ class Device(libevdev.Device):
 
     def find_touchpad_device(self):
         context = pyudev.Context()
-        for device in context.list_devices(subsystem='input'):
-            if not device.get('ID_INPUT_TOUCHPAD', 0):
+        for device in context.list_devices(subsystem="input"):
+            if not device.get("ID_INPUT_TOUCHPAD", 0):
                 continue
 
-            if not device.device_node or \
-               not device.device_node.startswith('/dev/input/event'):
+            if not device.device_node or not device.device_node.startswith(
+                "/dev/input/event"
+            ):
                 continue
 
             return device.device_node
@@ -223,21 +258,24 @@ class Device(libevdev.Device):
         sys.exit(1)
 
     def _init_thresholds_from_quirks(self):
-        command = ['libinput', 'quirks', 'list', self.path]
+        command = ["libinput", "quirks", "list", self.path]
         cmd = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         if cmd.returncode != 0:
-            print("Error querying quirks: {}".format(cmd.stderr.decode('utf-8')), file=sys.stderr)
+            print(
+                "Error querying quirks: {}".format(cmd.stderr.decode("utf-8")),
+                file=sys.stderr,
+            )
             return
 
-        stdout = cmd.stdout.decode('utf-8')
-        quirks = [q.split('=') for q in stdout.split('\n')]
+        stdout = cmd.stdout.decode("utf-8")
+        quirks = [q.split("=") for q in stdout.split("\n")]
 
         for q in quirks:
-            if q[0] == 'AttrPalmPressureThreshold':
+            if q[0] == "AttrPalmPressureThreshold":
                 self.palm = int(q[1])
-            elif q[0] == 'AttrPressureRange':
+            elif q[0] == "AttrPressureRange":
                 self.down, self.up = colon_tuple(q[1])
-            elif q[0] == 'AttrThumbPressureThreshold':
+            elif q[0] == "AttrThumbPressureThreshold":
                 self.thumb = int(q[1])
 
     def start_new_sequence(self, tracking_id):
@@ -252,11 +290,13 @@ def handle_key(device, event):
         libevdev.EV_KEY.BTN_TOOL_DOUBLETAP,
         libevdev.EV_KEY.BTN_TOOL_TRIPLETAP,
         libevdev.EV_KEY.BTN_TOOL_QUADTAP,
-        libevdev.EV_KEY.BTN_TOOL_QUINTTAP
+        libevdev.EV_KEY.BTN_TOOL_QUINTTAP,
     ]
     if event.code in tapcodes and event.value > 0:
-        print('\r\033[2KThis tool cannot handle multiple fingers, '
-              'output will be invalid')
+        print(
+            "\r\033[2KThis tool cannot handle multiple fingers, "
+            "output will be invalid"
+        )
 
 
 def handle_abs(device, event):
@@ -271,8 +311,9 @@ def handle_abs(device, event):
             except IndexError:
                 # If the finger was down at startup
                 pass
-    elif (event.matches(libevdev.EV_ABS.ABS_MT_PRESSURE) or
-          (event.matches(libevdev.EV_ABS.ABS_PRESSURE) and not device.has_mt_pressure)):
+    elif event.matches(libevdev.EV_ABS.ABS_MT_PRESSURE) or (
+        event.matches(libevdev.EV_ABS.ABS_PRESSURE) and not device.has_mt_pressure
+    ):
         try:
             s = device.current_sequence()
             s.append(Touch(pressure=event.value))
@@ -290,24 +331,26 @@ def handle_event(device, event):
 
 
 def loop(device):
-    print('This is an interactive tool')
+    print("This is an interactive tool")
     print()
     print("Place a single finger on the touchpad to measure pressure values.")
-    print('Check that:')
-    print('- touches subjectively perceived as down are tagged as down')
-    print('- touches with a thumb are tagged as thumb')
-    print('- touches with a palm are tagged as palm')
+    print("Check that:")
+    print("- touches subjectively perceived as down are tagged as down")
+    print("- touches with a thumb are tagged as thumb")
+    print("- touches with a palm are tagged as palm")
     print()
-    print('If the touch states do not match the interaction, re-run')
-    print('with --touch-thresholds=down:up using observed pressure values.')
-    print('See --help for more options.')
+    print("If the touch states do not match the interaction, re-run")
+    print("with --touch-thresholds=down:up using observed pressure values.")
+    print("See --help for more options.")
     print()
     print("Press Ctrl+C to exit")
     print()
 
-    headers = fmt.headers(['Touch', 'down', 'up', 'palm', 'thumb', 'min', 'max', 'p', 'avg', 'median'])
+    headers = fmt.headers(
+        ["Touch", "down", "up", "palm", "thumb", "min", "max", "p", "avg", "median"]
+    )
     print(fmt.separator())
-    print(fmt.values(['Thresh', device.down, device.up, device.palm, device.thumb]))
+    print(fmt.values(["Thresh", device.down, device.up, device.palm, device.thumb]))
     print(fmt.separator())
     print(headers)
     print(fmt.separator())
@@ -319,11 +362,11 @@ def loop(device):
 
 def colon_tuple(string):
     try:
-        ts = string.split(':')
+        ts = string.split(":")
         t = tuple([int(x) for x in ts])
         if len(t) == 2 and t[0] >= t[1]:
             return t
-    except: # noqa
+    except:  # noqa
         pass
 
     msg = "{} is not in format N:M (N >= M)".format(string)
@@ -331,24 +374,31 @@ def colon_tuple(string):
 
 
 def main(args):
-    parser = argparse.ArgumentParser(
-        description="Measure touchpad pressure values"
-    )
+    parser = argparse.ArgumentParser(description="Measure touchpad pressure values")
     parser.add_argument(
-        'path', metavar='/dev/input/event0', nargs='?', type=str,
-        help='Path to device (optional)'
+        "path",
+        metavar="/dev/input/event0",
+        nargs="?",
+        type=str,
+        help="Path to device (optional)",
     )
     parser.add_argument(
-        '--touch-thresholds', metavar='down:up', type=colon_tuple,
-        help='Thresholds when a touch is logically down or up'
+        "--touch-thresholds",
+        metavar="down:up",
+        type=colon_tuple,
+        help="Thresholds when a touch is logically down or up",
     )
     parser.add_argument(
-        '--palm-threshold', metavar='t', type=int,
-        help='Threshold when a touch is a palm'
+        "--palm-threshold",
+        metavar="t",
+        type=int,
+        help="Threshold when a touch is a palm",
     )
     parser.add_argument(
-        '--thumb-threshold', metavar='t', type=int,
-        help='Threshold when a touch is a thumb'
+        "--thumb-threshold",
+        metavar="t",
+        type=int,
+        help="Threshold when a touch is a thumb",
     )
     args = parser.parse_args()
 
@@ -366,13 +416,15 @@ def main(args):
 
         loop(device)
     except KeyboardInterrupt:
-        print('\r\033[2K{}'.format(fmt.separator()))
+        print("\r\033[2K{}".format(fmt.separator()))
         print()
 
     except (PermissionError, OSError):
         print("Error: failed to open device")
     except InvalidDeviceError as e:
-        print("This device does not have the capabilities for pressure-based touch detection.")
+        print(
+            "This device does not have the capabilities for pressure-based touch detection."
+        )
         print("Details: {}".format(e))
 
 
index 27c618c46b99df4912ce0890fb0c50c41b31ff0a..2c10953e7bc979f8939f5a89db77775b448216fc 100755 (executable)
 
 import sys
 import argparse
+
 try:
     import libevdev
     import pyudev
 except ModuleNotFoundError as e:
-    print('Error: {}'.format(str(e)), file=sys.stderr)
-    print('One or more python modules are missing. Please install those '
-          'modules and re-run this tool.')
+    print("Error: {}".format(str(e)), file=sys.stderr)
+    print(
+        "One or more python modules are missing. Please install those "
+        "modules and re-run this tool."
+    )
     sys.exit(1)
 
 
@@ -51,15 +54,15 @@ class Touchpad(object):
         x = evdev.absinfo[libevdev.EV_ABS.ABS_X]
         y = evdev.absinfo[libevdev.EV_ABS.ABS_Y]
         if not x or not y:
-            raise DeviceError('Device does not have an x or axis')
+            raise DeviceError("Device does not have an x or axis")
 
         if not x.resolution or not y.resolution:
-            print('Device does not have resolutions.', file=sys.stderr)
+            print("Device does not have resolutions.", file=sys.stderr)
             x.resolution = 1
             y.resolution = 1
 
-        self.xrange = (x.maximum - x.minimum)
-        self.yrange = (y.maximum - y.minimum)
+        self.xrange = x.maximum - x.minimum
+        self.yrange = y.maximum - y.minimum
         self.width = self.xrange / x.resolution
         self.height = self.yrange / y.resolution
 
@@ -70,7 +73,12 @@ class Touchpad(object):
         # terminal character space is (guesswork) ca 2.3 times as high as
         # wide.
         self.columns = 30
-        self.rows = int(self.columns * (self.yrange // y.resolution) // (self.xrange // x.resolution) / 2.3)
+        self.rows = int(
+            self.columns
+            * (self.yrange // y.resolution)
+            // (self.xrange // x.resolution)
+            / 2.3
+        )
         self.pos = Point(0, 0)
         self.min = Point()
         self.max = Point()
@@ -87,8 +95,8 @@ class Touchpad(object):
     def x(self, x):
         self._x.minimum = min(self.x.minimum, x)
         self._x.maximum = max(self.x.maximum, x)
-        self.min.x = min(x, self.min.x or 0xffffffff)
-        self.max.x = max(x, self.max.x or -0xffffffff)
+        self.min.x = min(x, self.min.x or 0xFFFFFFFF)
+        self.max.x = max(x, self.max.x or -0xFFFFFFFF)
         # we calculate the position based on the original range.
         # this means on devices with a narrower range than advertised, not
         # all corners may be reachable in the touchpad drawing.
@@ -98,8 +106,8 @@ class Touchpad(object):
     def y(self, y):
         self._y.minimum = min(self.y.minimum, y)
         self._y.maximum = max(self.y.maximum, y)
-        self.min.y = min(y, self.min.y or 0xffffffff)
-        self.max.y = max(y, self.max.y or -0xffffffff)
+        self.min.y = min(y, self.min.y or 0xFFFFFFFF)
+        self.max.y = max(y, self.max.y or -0xFFFFFFFF)
         # we calculate the position based on the original range.
         # this means on devices with a narrower range than advertised, not
         # all corners may be reachable in the touchpad drawing.
@@ -107,61 +115,61 @@ class Touchpad(object):
 
     def update_from_data(self):
         if None in [self.min.x, self.min.y, self.max.x, self.max.y]:
-            raise DeviceError('Insufficient data to continue')
+            raise DeviceError("Insufficient data to continue")
         self._x.minimum = self.min.x
         self._x.maximum = self.max.x
         self._y.minimum = self.min.y
         self._y.maximum = self.max.y
 
     def draw(self):
-        print('Detected axis range: x [{:4d}..{:4d}], y [{:4d}..{:4d}]'.format(
-              self.min.x if self.min.x is not None else 0,
-              self.max.x if self.max.x is not None else 0,
-              self.min.y if self.min.y is not None else 0,
-              self.max.y if self.max.y is not None else 0))
+        print(
+            "Detected axis range: x [{:4d}..{:4d}], y [{:4d}..{:4d}]".format(
+                self.min.x if self.min.x is not None else 0,
+                self.max.x if self.max.x is not None else 0,
+                self.min.y if self.min.y is not None else 0,
+                self.max.y if self.max.y is not None else 0,
+            )
+        )
 
         print()
-        print('Move one finger along all edges of the touchpad'.center(self.columns))
-        print('until the detected axis range stops changing.'.center(self.columns))
+        print("Move one finger along all edges of the touchpad".center(self.columns))
+        print("until the detected axis range stops changing.".center(self.columns))
 
         top = int(self.pos.y * self.rows)
 
-        print('+{}+'.format(''.ljust(self.columns, '-')))
+        print("+{}+".format("".ljust(self.columns, "-")))
         for row in range(0, top):
-            print('|{}|'.format(''.ljust(self.columns)))
+            print("|{}|".format("".ljust(self.columns)))
 
         left = int(self.pos.x * self.columns)
         right = max(0, self.columns - 1 - left)
-        print('|{}{}{}|'.format(
-            ''.ljust(left),
-            'O',
-            ''.ljust(right)))
+        print("|{}{}{}|".format("".ljust(left), "O", "".ljust(right)))
 
         for row in range(top + 1, self.rows):
-            print('|{}|'.format(''.ljust(self.columns)))
+            print("|{}|".format("".ljust(self.columns)))
 
-        print('+{}+'.format(''.ljust(self.columns, '-')))
+        print("+{}+".format("".ljust(self.columns, "-")))
 
-        print('Press Ctrl+C to stop'.center(self.columns))
+        print("Press Ctrl+C to stop".center(self.columns))
 
-        print('\033[{}A'.format(self.rows + 8), flush=True)
+        print("\033[{}A".format(self.rows + 8), flush=True)
 
         self.rows_printed = self.rows + 8
 
     def erase(self):
         # Erase all previous lines so we're not left with rubbish
         for row in range(self.rows_printed):
-            print('\033[K')
-        print('\033[{}A'.format(self.rows_printed))
+            print("\033[K")
+        print("\033[{}A".format(self.rows_printed))
 
 
 def dimension(string):
     try:
-        ts = string.split('x')
+        ts = string.split("x")
         t = tuple([int(x) for x in ts])
         if len(t) == 2:
             return t
-    except: # noqa
+    except:  # noqa
         pass
 
     msg = "{} is not in format WxH".format(string)
@@ -173,82 +181,95 @@ def between(v1, v2, deviation):
 
 
 def dmi_modalias_match(modalias):
-    modalias = modalias.split(':')
-    dmi = {'svn': None, 'pvr': None, 'pn': None}
+    modalias = modalias.split(":")
+    dmi = {"svn": None, "pvr": None, "pn": None}
     for m in modalias:
         for key in dmi:
             if m.startswith(key):
-                dmi[key] = m[len(key):]
+                dmi[key] = m[len(key) :]
 
     # Based on the current 60-evdev.hwdb, Lenovo uses pvr and everyone else
     # uses pn to provide a human-identifiable match
-    if dmi['svn'] == 'LENOVO':
-        return 'dmi:*svn{}:*pvr{}*'.format(dmi['svn'], dmi['pvr'])
+    if dmi["svn"] == "LENOVO":
+        return "dmi:*svn{}:*pvr{}*".format(dmi["svn"], dmi["pvr"])
     else:
-        return 'dmi:*svn{}:*pn{}*'.format(dmi['svn'], dmi['pn'])
+        return "dmi:*svn{}:*pn{}*".format(dmi["svn"], dmi["pn"])
 
 
 def main(args):
-    parser = argparse.ArgumentParser(
-        description="Measure the touchpad size"
-    )
+    parser = argparse.ArgumentParser(description="Measure the touchpad size")
     parser.add_argument(
-        'size', metavar='WxH', type=dimension,
-        help='Touchpad size (width by height) in mm',
+        "size",
+        metavar="WxH",
+        type=dimension,
+        help="Touchpad size (width by height) in mm",
     )
     parser.add_argument(
-        'path', metavar='/dev/input/event0', nargs='?', type=str,
-        help='Path to device (optional)'
+        "path",
+        metavar="/dev/input/event0",
+        nargs="?",
+        type=str,
+        help="Path to device (optional)",
     )
     context = pyudev.Context()
 
     args = parser.parse_args()
     if not args.path:
-        for device in context.list_devices(subsystem='input'):
-            if (device.get('ID_INPUT_TOUCHPAD', 0) and
-                    (device.device_node or '').startswith('/dev/input/event')):
+        for device in context.list_devices(subsystem="input"):
+            if device.get("ID_INPUT_TOUCHPAD", 0) and (
+                device.device_node or ""
+            ).startswith("/dev/input/event"):
                 args.path = device.device_node
-                name = 'unknown'
+                name = "unknown"
                 parent = device
                 while parent is not None:
-                    n = parent.get('NAME', None)
+                    n = parent.get("NAME", None)
                     if n:
                         name = n
                         break
                     parent = parent.parent
 
-                print('Using {}: {}'.format(name, device.device_node))
+                print("Using {}: {}".format(name, device.device_node))
                 break
         else:
-            print('Unable to find a touchpad device.', file=sys.stderr)
+            print("Unable to find a touchpad device.", file=sys.stderr)
             return 1
 
     dev = pyudev.Devices.from_device_file(context, args.path)
-    overrides = [p for p in dev.properties if p.startswith('EVDEV_ABS')]
+    overrides = [p for p in dev.properties if p.startswith("EVDEV_ABS")]
     if overrides:
         print()
-        print('********************************************************************')
-        print('WARNING: axis overrides already in place for this device:')
+        print("********************************************************************")
+        print("WARNING: axis overrides already in place for this device:")
         for prop in overrides:
-            print('  {}={}'.format(prop, dev.properties[prop]))
-        print('The systemd hwdb already overrides the axis ranges and/or resolution.')
-        print('This tool is not needed unless you want to verify the axis overrides.')
-        print('********************************************************************')
+            print("  {}={}".format(prop, dev.properties[prop]))
+        print("The systemd hwdb already overrides the axis ranges and/or resolution.")
+        print("This tool is not needed unless you want to verify the axis overrides.")
+        print("********************************************************************")
         print()
 
     try:
-        fd = open(args.path, 'rb')
+        fd = open(args.path, "rb")
         evdev = libevdev.Device(fd)
         touchpad = Touchpad(evdev)
-        print('Kernel specified touchpad size: {:.1f}x{:.1f}mm'.format(touchpad.width, touchpad.height))
-        print('User specified touchpad size:   {:.1f}x{:.1f}mm'.format(*args.size))
+        print(
+            "Kernel specified touchpad size: {:.1f}x{:.1f}mm".format(
+                touchpad.width, touchpad.height
+            )
+        )
+        print("User specified touchpad size:   {:.1f}x{:.1f}mm".format(*args.size))
 
         print()
-        print('Kernel axis range:   x [{:4d}..{:4d}], y [{:4d}..{:4d}]'.format(
-              touchpad.x.minimum, touchpad.x.maximum,
-              touchpad.y.minimum, touchpad.y.maximum))
+        print(
+            "Kernel axis range:   x [{:4d}..{:4d}], y [{:4d}..{:4d}]".format(
+                touchpad.x.minimum,
+                touchpad.x.maximum,
+                touchpad.y.minimum,
+                touchpad.y.maximum,
+            )
+        )
 
-        print('Put your finger on the touchpad to start\033[1A')
+        print("Put your finger on the touchpad to start\033[1A")
 
         try:
             touchpad.draw()
@@ -264,15 +285,27 @@ def main(args):
             touchpad.erase()
             touchpad.update_from_data()
 
-        print('Detected axis range: x [{:4d}..{:4d}], y [{:4d}..{:4d}]'.format(
-              touchpad.x.minimum, touchpad.x.maximum,
-              touchpad.y.minimum, touchpad.y.maximum))
-
-        touchpad.x.resolution = round((touchpad.x.maximum - touchpad.x.minimum) / args.size[0])
-        touchpad.y.resolution = round((touchpad.y.maximum - touchpad.y.minimum) / args.size[1])
-
-        print('Resolutions calculated based on user-specified size: x {}, y {} units/mm'.format(
-              touchpad.x.resolution, touchpad.y.resolution))
+        print(
+            "Detected axis range: x [{:4d}..{:4d}], y [{:4d}..{:4d}]".format(
+                touchpad.x.minimum,
+                touchpad.x.maximum,
+                touchpad.y.minimum,
+                touchpad.y.maximum,
+            )
+        )
+
+        touchpad.x.resolution = round(
+            (touchpad.x.maximum - touchpad.x.minimum) / args.size[0]
+        )
+        touchpad.y.resolution = round(
+            (touchpad.y.maximum - touchpad.y.minimum) / args.size[1]
+        )
+
+        print(
+            "Resolutions calculated based on user-specified size: x {}, y {} units/mm".format(
+                touchpad.x.resolution, touchpad.y.resolution
+            )
+        )
 
         # If both x/y are within some acceptable deviation, we skip the axis
         # overrides and only override the resolution
@@ -287,50 +320,73 @@ def main(args):
 
         if skip:
             print()
-            print('Note: Axis ranges within acceptable deviation, skipping min/max override')
+            print(
+                "Note: Axis ranges within acceptable deviation, skipping min/max override"
+            )
             print()
 
         print()
-        print('Suggested hwdb entry:')
+        print("Suggested hwdb entry:")
 
-        use_dmi = evdev.id['bustype'] not in [0x03, 0x05]  # USB, Bluetooth
+        use_dmi = evdev.id["bustype"] not in [0x03, 0x05]  # USB, Bluetooth
         if use_dmi:
-            modalias = open('/sys/class/dmi/id/modalias').read().strip()
-            print('Note: the dmi modalias match is a guess based on your machine\'s modalias:')
-            print(' ', modalias)
-            print('Please verify that this is the most sensible match and adjust if necessary.')
-
-        print('-8<--------------------------')
-        print('# Laptop model description (e.g. Lenovo X1 Carbon 5th)')
+            modalias = open("/sys/class/dmi/id/modalias").read().strip()
+            print(
+                "Note: the dmi modalias match is a guess based on your machine's modalias:"
+            )
+            print(" ", modalias)
+            print(
+                "Please verify that this is the most sensible match and adjust if necessary."
+            )
+
+        print("-8<--------------------------")
+        print("# Laptop model description (e.g. Lenovo X1 Carbon 5th)")
         if use_dmi:
-            print('evdev:name:{}:{}*'.format(evdev.name, dmi_modalias_match(modalias)))
+            print("evdev:name:{}:{}*".format(evdev.name, dmi_modalias_match(modalias)))
         else:
-            print('evdev:input:b{:04X}v{:04X}p{:04X}*'.format(
-                  evdev.id['bustype'], evdev.id['vendor'], evdev.id['product']))
-        print(' EVDEV_ABS_00={}:{}:{}'.format(
-              touchpad.x.minimum if not skip else '',
-              touchpad.x.maximum if not skip else '',
-              touchpad.x.resolution))
-        print(' EVDEV_ABS_01={}:{}:{}'.format(
-              touchpad.y.minimum if not skip else '',
-              touchpad.y.maximum if not skip else '',
-              touchpad.y.resolution))
+            print(
+                "evdev:input:b{:04X}v{:04X}p{:04X}*".format(
+                    evdev.id["bustype"], evdev.id["vendor"], evdev.id["product"]
+                )
+            )
+        print(
+            " EVDEV_ABS_00={}:{}:{}".format(
+                touchpad.x.minimum if not skip else "",
+                touchpad.x.maximum if not skip else "",
+                touchpad.x.resolution,
+            )
+        )
+        print(
+            " EVDEV_ABS_01={}:{}:{}".format(
+                touchpad.y.minimum if not skip else "",
+                touchpad.y.maximum if not skip else "",
+                touchpad.y.resolution,
+            )
+        )
         if evdev.absinfo[libevdev.EV_ABS.ABS_MT_POSITION_X]:
-            print(' EVDEV_ABS_35={}:{}:{}'.format(
-                  touchpad.x.minimum if not skip else '',
-                  touchpad.x.maximum if not skip else '',
-                  touchpad.x.resolution))
-            print(' EVDEV_ABS_36={}:{}:{}'.format(
-                  touchpad.y.minimum if not skip else '',
-                  touchpad.y.maximum if not skip else '',
-                  touchpad.y.resolution))
-        print('-8<--------------------------')
-        print('Instructions on what to do with this snippet are in /usr/lib/udev/hwdb.d/60-evdev.hwdb')
+            print(
+                " EVDEV_ABS_35={}:{}:{}".format(
+                    touchpad.x.minimum if not skip else "",
+                    touchpad.x.maximum if not skip else "",
+                    touchpad.x.resolution,
+                )
+            )
+            print(
+                " EVDEV_ABS_36={}:{}:{}".format(
+                    touchpad.y.minimum if not skip else "",
+                    touchpad.y.maximum if not skip else "",
+                    touchpad.y.resolution,
+                )
+            )
+        print("-8<--------------------------")
+        print(
+            "Instructions on what to do with this snippet are in /usr/lib/udev/hwdb.d/60-evdev.hwdb"
+        )
     except DeviceError as e:
-        print('Error: {}'.format(e), file=sys.stderr)
+        print("Error: {}".format(e), file=sys.stderr)
         return 1
     except PermissionError:
-        print('Unable to open device. Please run me as root', file=sys.stderr)
+        print("Unable to open device. Please run me as root", file=sys.stderr)
         return 1
 
     return 0
index fb1b416b46b61651448a5f514205c4ec2c9e9e5f..c33acfc7fbe342598c8f8080aff64de2b3bf4232 100755 (executable)
 
 import sys
 import argparse
+
 try:
     import libevdev
     import textwrap
     import pyudev
 except ModuleNotFoundError as e:
-    print('Error: {}'.format(e), file=sys.stderr)
-    print('One or more python modules are missing. Please install those '
-          'modules and re-run this tool.')
+    print("Error: {}".format(e), file=sys.stderr)
+    print(
+        "One or more python modules are missing. Please install those "
+        "modules and re-run this tool."
+    )
     sys.exit(1)
 
 print_dest = sys.stdout
@@ -66,7 +69,7 @@ class Touch(object):
 
     @up.setter
     def up(self, up):
-        assert(up > self.down)
+        assert up > self.down
         self._up = up
 
     @property
@@ -88,7 +91,7 @@ class Device(libevdev.Device):
             self.path = self._find_touch_device()
         else:
             self.path = path
-        fd = open(self.path, 'rb')
+        fd = open(self.path, "rb")
         super().__init__(fd)
 
         print("Using {}: {}\n".format(self.name, self.path))
@@ -101,18 +104,19 @@ class Device(libevdev.Device):
     def _find_touch_device(self):
         context = pyudev.Context()
         device_node = None
-        for device in context.list_devices(subsystem='input'):
-            if (not device.device_node or
-                    not device.device_node.startswith('/dev/input/event')):
+        for device in context.list_devices(subsystem="input"):
+            if not device.device_node or not device.device_node.startswith(
+                "/dev/input/event"
+            ):
                 continue
 
             # pick the touchpad by default, fallback to the first
             # touchscreen only when there is no touchpad
-            if device.get('ID_INPUT_TOUCHPAD', 0):
+            if device.get("ID_INPUT_TOUCHPAD", 0):
                 device_node = device.device_node
                 break
 
-            if device.get('ID_INPUT_TOUCHSCREEN', 0) and device_node is None:
+            if device.get("ID_INPUT_TOUCHSCREEN", 0) and device_node is None:
                 device_node = device.device_node
 
         if device_node is not None:
@@ -127,17 +131,19 @@ class Device(libevdev.Device):
             self.touches.append(t)
         else:
             self.touches[-1].up = tv2us(event.sec, event.usec)
-            msg("\rTouch sequences detected: {}".format(len(self.touches)),
-                end='')
+            msg("\rTouch sequences detected: {}".format(len(self.touches)), end="")
 
     def handle_key(self, event):
-        tapcodes = [libevdev.EV_KEY.BTN_TOOL_DOUBLETAP,
-                    libevdev.EV_KEY.BTN_TOOL_TRIPLETAP,
-                    libevdev.EV_KEY.BTN_TOOL_QUADTAP,
-                    libevdev.EV_KEY.BTN_TOOL_QUINTTAP]
+        tapcodes = [
+            libevdev.EV_KEY.BTN_TOOL_DOUBLETAP,
+            libevdev.EV_KEY.BTN_TOOL_TRIPLETAP,
+            libevdev.EV_KEY.BTN_TOOL_QUADTAP,
+            libevdev.EV_KEY.BTN_TOOL_QUINTTAP,
+        ]
         if event.code in tapcodes and event.value > 0:
-            error("\rThis tool cannot handle multiple fingers, "
-                  "output will be invalid")
+            error(
+                "\rThis tool cannot handle multiple fingers, " "output will be invalid"
+            )
             return
 
         if event.matches(libevdev.EV_KEY.BTN_TOUCH):
@@ -146,9 +152,11 @@ class Device(libevdev.Device):
     def handle_syn(self, event):
         if self.touch.dirty:
             self.current_sequence().append(self.touch)
-            self.touch = Touch(major=self.touch.major,
-                               minor=self.touch.minor,
-                               orientation=self.touch.orientation)
+            self.touch = Touch(
+                major=self.touch.major,
+                minor=self.touch.minor,
+                orientation=self.touch.orientation,
+            )
 
     def handle_event(self, event):
         if event.matches(libevdev.EV_KEY):
@@ -182,7 +190,9 @@ class Device(libevdev.Device):
 
     def print_dat(self):
         print("# libinput-measure-touchpad-tap")
-        print(textwrap.dedent('''\
+        print(
+            textwrap.dedent(
+                """\
               # File contents:
               #    This file contains multiple prints of the data in
               #    different sort order. Row number is index of touch
@@ -197,7 +207,9 @@ class Device(libevdev.Device):
               #  4: touch down time in ms, offset by first event
               #  5: touch up time in ms, offset by first event
               #  6: time delta in ms
-              '''))
+              """
+            )
+        )
 
         deltas = [t for t in self.touches]
         deltas_sorted = sorted(deltas, key=lambda t: t.tdelta)
@@ -205,28 +217,44 @@ class Device(libevdev.Device):
         offset = deltas[0].down
 
         for t1, t2 in zip(deltas, deltas_sorted):
-            print(t1.down - offset, t1.up - offset, t1.tdelta,
-                  t2.down - offset, t2.up - offset, t2.tdelta)
+            print(
+                t1.down - offset,
+                t1.up - offset,
+                t1.tdelta,
+                t2.down - offset,
+                t2.up - offset,
+                t2.tdelta,
+            )
 
     def print(self, format):
         if not self.touches:
             error("No tap data available")
             return
 
-        if format == 'summary':
+        if format == "summary":
             self.print_summary()
-        elif format == 'dat':
+        elif format == "dat":
             self.print_dat()
 
 
 def main(args):
-    parser = argparse.ArgumentParser(description="Measure tap-to-click properties of devices")
-    parser.add_argument('path', metavar='/dev/input/event0',
-                        nargs='?', type=str, help='Path to device (optional)')
-    parser.add_argument('--format', metavar='format',
-                        choices=['summary', 'dat'],
-                        default='summary',
-                        help='data format to print ("summary" or "dat")')
+    parser = argparse.ArgumentParser(
+        description="Measure tap-to-click properties of devices"
+    )
+    parser.add_argument(
+        "path",
+        metavar="/dev/input/event0",
+        nargs="?",
+        type=str,
+        help="Path to device (optional)",
+    )
+    parser.add_argument(
+        "--format",
+        metavar="format",
+        choices=["summary", "dat"],
+        default="summary",
+        help='data format to print ("summary" or "dat")',
+    )
     args = parser.parse_args()
 
     if not sys.stdout.isatty():
@@ -235,13 +263,15 @@ def main(args):
 
     try:
         device = Device(args.path)
-        error("Ready for recording data.\n"
-              "Tap the touchpad multiple times with a single finger only.\n"
-              "For useful data we recommend at least 20 taps.\n"
-              "Ctrl+C to exit")
+        error(
+            "Ready for recording data.\n"
+            "Tap the touchpad multiple times with a single finger only.\n"
+            "For useful data we recommend at least 20 taps.\n"
+            "Ctrl+C to exit"
+        )
         device.read_events()
     except KeyboardInterrupt:
-        msg('')
+        msg("")
         device.print(args.format)
     except (PermissionError, OSError) as e:
         error("Error: failed to open device. {}".format(e))
index a40ca5e93562a0af728b027a223a256a0404e4e8..ff9717a2ad0698653c7780526c68edfe07e59f7a 100755 (executable)
@@ -34,7 +34,7 @@ from pkg_resources import parse_version
 
 
 class TestYaml(unittest.TestCase):
-    filename = ''
+    filename = ""
 
     @classmethod
     def setUpClass(cls):
@@ -42,163 +42,166 @@ class TestYaml(unittest.TestCase):
             cls.yaml = yaml.safe_load(f)
 
     def dict_key_crosscheck(self, d, keys):
-        '''Check that each key in d is in keys, and that each key is in d'''
+        """Check that each key in d is in keys, and that each key is in d"""
         self.assertEqual(sorted(d.keys()), sorted(keys))
 
     def libinput_events(self, filter=None):
-        '''Returns all libinput events in the recording, regardless of the
-        device'''
-        devices = self.yaml['devices']
+        """Returns all libinput events in the recording, regardless of the
+        device"""
+        devices = self.yaml["devices"]
         for d in devices:
-            events = d['events']
+            events = d["events"]
             if not events:
                 raise unittest.SkipTest()
             for e in events:
                 try:
-                    libinput = e['libinput']
+                    libinput = e["libinput"]
                 except KeyError:
                     continue
 
                 for ev in libinput:
-                    if (filter is None or ev['type'] == filter or
-                            isinstance(filter, list) and ev['type'] in filter):
+                    if (
+                        filter is None
+                        or ev["type"] == filter
+                        or isinstance(filter, list)
+                        and ev["type"] in filter
+                    ):
                         yield ev
 
     def test_sections_exist(self):
-        sections = ['version', 'ndevices', 'libinput', 'system', 'devices']
+        sections = ["version", "ndevices", "libinput", "system", "devices"]
         for section in sections:
             self.assertIn(section, self.yaml)
 
     def test_version(self):
-        version = self.yaml['version']
+        version = self.yaml["version"]
         self.assertTrue(isinstance(version, int))
         self.assertEqual(version, 1)
 
     def test_ndevices(self):
-        ndevices = self.yaml['ndevices']
+        ndevices = self.yaml["ndevices"]
         self.assertTrue(isinstance(ndevices, int))
         self.assertGreaterEqual(ndevices, 1)
-        self.assertEqual(ndevices, len(self.yaml['devices']))
+        self.assertEqual(ndevices, len(self.yaml["devices"]))
 
     def test_libinput(self):
-        libinput = self.yaml['libinput']
-        version = libinput['version']
+        libinput = self.yaml["libinput"]
+        version = libinput["version"]
         self.assertTrue(isinstance(version, str))
-        self.assertGreaterEqual(parse_version(version),
-                                parse_version('1.10.0'))
-        git = libinput['git']
+        self.assertGreaterEqual(parse_version(version), parse_version("1.10.0"))
+        git = libinput["git"]
         self.assertTrue(isinstance(git, str))
-        self.assertNotEqual(git, 'unknown')
+        self.assertNotEqual(git, "unknown")
 
     def test_system(self):
-        system = self.yaml['system']
-        kernel = system['kernel']
+        system = self.yaml["system"]
+        kernel = system["kernel"]
         self.assertTrue(isinstance(kernel, str))
         self.assertEqual(kernel, os.uname().release)
 
-        dmi = system['dmi']
+        dmi = system["dmi"]
         self.assertTrue(isinstance(dmi, str))
-        with open('/sys/class/dmi/id/modalias') as f:
+        with open("/sys/class/dmi/id/modalias") as f:
             sys_dmi = f.read()[:-1]  # trailing newline
             self.assertEqual(dmi, sys_dmi)
 
     def test_devices_sections_exist(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            self.assertIn('node', d)
-            self.assertIn('evdev', d)
-            self.assertIn('udev', d)
+            self.assertIn("node", d)
+            self.assertIn("evdev", d)
+            self.assertIn("udev", d)
 
     def test_evdev_sections_exist(self):
-        sections = ['name', 'id', 'codes', 'properties']
-        devices = self.yaml['devices']
+        sections = ["name", "id", "codes", "properties"]
+        devices = self.yaml["devices"]
         for d in devices:
-            evdev = d['evdev']
+            evdev = d["evdev"]
             for s in sections:
                 self.assertIn(s, evdev)
 
     def test_evdev_name(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            evdev = d['evdev']
-            name = evdev['name']
+            evdev = d["evdev"]
+            name = evdev["name"]
             self.assertTrue(isinstance(name, str))
             self.assertGreaterEqual(len(name), 5)
 
     def test_evdev_id(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            evdev = d['evdev']
-            id = evdev['id']
+            evdev = d["evdev"]
+            id = evdev["id"]
             self.assertTrue(isinstance(id, list))
             self.assertEqual(len(id), 4)
             self.assertGreater(id[0], 0)
             self.assertGreater(id[1], 0)
 
     def test_evdev_properties(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            evdev = d['evdev']
-            properties = evdev['properties']
+            evdev = d["evdev"]
+            properties = evdev["properties"]
             self.assertTrue(isinstance(properties, list))
 
     def test_hid(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            hid = d['hid']
+            hid = d["hid"]
             self.assertTrue(isinstance(hid, list))
             for byte in hid:
                 self.assertGreaterEqual(byte, 0)
                 self.assertLessEqual(byte, 255)
 
     def test_udev_sections_exist(self):
-        sections = ['properties']
-        devices = self.yaml['devices']
+        sections = ["properties"]
+        devices = self.yaml["devices"]
         for d in devices:
-            udev = d['udev']
+            udev = d["udev"]
             for s in sections:
                 self.assertIn(s, udev)
 
     def test_udev_properties(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            udev = d['udev']
-            properties = udev['properties']
+            udev = d["udev"]
+            properties = udev["properties"]
             self.assertTrue(isinstance(properties, list))
             self.assertGreater(len(properties), 0)
 
-            self.assertIn('ID_INPUT=1', properties)
+            self.assertIn("ID_INPUT=1", properties)
             for p in properties:
-                self.assertTrue(re.match('[A-Z0-9_]+=.+', p))
+                self.assertTrue(re.match("[A-Z0-9_]+=.+", p))
 
     def test_udev_id_inputs(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            udev = d['udev']
-            properties = udev['properties']
-            id_inputs = [p for p in properties if p.startswith('ID_INPUT')]
+            udev = d["udev"]
+            properties = udev["properties"]
+            id_inputs = [p for p in properties if p.startswith("ID_INPUT")]
             # We expect ID_INPUT and ID_INPUT_something, but might get more
             # than one of the latter
             self.assertGreaterEqual(len(id_inputs), 2)
 
     def test_events_have_section(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            events = d['events']
+            events = d["events"]
             if not events:
                 raise unittest.SkipTest()
             for e in events:
-                self.assertTrue('evdev' in e or 'libinput' in e)
+                self.assertTrue("evdev" in e or "libinput" in e)
 
     def test_events_evdev(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            events = d['events']
+            events = d["events"]
             if not events:
                 raise unittest.SkipTest()
             for e in events:
                 try:
-                    evdev = e['evdev']
+                    evdev = e["evdev"]
                 except KeyError:
                     continue
 
@@ -213,28 +216,28 @@ class TestYaml(unittest.TestCase):
                 self.assertLessEqual(ev_syn[4], 1)
 
     def test_events_evdev_syn_report(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            events = d['events']
+            events = d["events"]
             if not events:
                 raise unittest.SkipTest()
             for e in events:
                 try:
-                    evdev = e['evdev']
+                    evdev = e["evdev"]
                 except KeyError:
                     continue
                 for ev in evdev[:-1]:
                     self.assertFalse(ev[2] == 0 and ev[3] == 0)
 
     def test_events_libinput(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            events = d['events']
+            events = d["events"]
             if not events:
                 raise unittest.SkipTest()
             for e in events:
                 try:
-                    libinput = e['libinput']
+                    libinput = e["libinput"]
                 except KeyError:
                     continue
 
@@ -243,20 +246,35 @@ class TestYaml(unittest.TestCase):
                     self.assertTrue(isinstance(ev, dict))
 
     def test_events_libinput_type(self):
-        types = ['POINTER_MOTION', 'POINTER_MOTION_ABSOLUTE', 'POINTER_AXIS',
-                 'POINTER_BUTTON', 'DEVICE_ADDED', 'KEYBOARD_KEY',
-                 'TOUCH_DOWN', 'TOUCH_MOTION', 'TOUCH_UP', 'TOUCH_FRAME',
-                 'GESTURE_SWIPE_BEGIN', 'GESTURE_SWIPE_UPDATE',
-                 'GESTURE_SWIPE_END', 'GESTURE_PINCH_BEGIN',
-                 'GESTURE_PINCH_UPDATE', 'GESTURE_PINCH_END',
-                 'TABLET_TOOL_AXIS', 'TABLET_TOOL_PROXIMITY',
-                 'TABLET_TOOL_BUTTON', 'TABLET_TOOL_TIP',
-                 'TABLET_PAD_STRIP', 'TABLET_PAD_RING',
-                 'TABLET_PAD_BUTTON', 'SWITCH_TOGGLE',
-                 ]
+        types = [
+            "POINTER_MOTION",
+            "POINTER_MOTION_ABSOLUTE",
+            "POINTER_AXIS",
+            "POINTER_BUTTON",
+            "DEVICE_ADDED",
+            "KEYBOARD_KEY",
+            "TOUCH_DOWN",
+            "TOUCH_MOTION",
+            "TOUCH_UP",
+            "TOUCH_FRAME",
+            "GESTURE_SWIPE_BEGIN",
+            "GESTURE_SWIPE_UPDATE",
+            "GESTURE_SWIPE_END",
+            "GESTURE_PINCH_BEGIN",
+            "GESTURE_PINCH_UPDATE",
+            "GESTURE_PINCH_END",
+            "TABLET_TOOL_AXIS",
+            "TABLET_TOOL_PROXIMITY",
+            "TABLET_TOOL_BUTTON",
+            "TABLET_TOOL_TIP",
+            "TABLET_PAD_STRIP",
+            "TABLET_PAD_RING",
+            "TABLET_PAD_BUTTON",
+            "SWITCH_TOGGLE",
+        ]
         for e in self.libinput_events():
-            self.assertIn('type', e)
-            self.assertIn(e['type'], types)
+            self.assertIn("type", e)
+            self.assertIn(e["type"], types)
 
     def test_events_libinput_time(self):
         # DEVICE_ADDED has no time
@@ -264,51 +282,51 @@ class TestYaml(unittest.TestCase):
         # libinput event.
         try:
             for e in list(self.libinput_events())[2:]:
-                self.assertIn('time', e)
-                self.assertGreater(e['time'], 0.0)
-                self.assertLess(e['time'], 60.0)
+                self.assertIn("time", e)
+                self.assertGreater(e["time"], 0.0)
+                self.assertLess(e["time"], 60.0)
         except IndexError:
             pass
 
     def test_events_libinput_device_added(self):
-        keys = ['type', 'seat', 'logical_seat']
-        for e in self.libinput_events('DEVICE_ADDED'):
+        keys = ["type", "seat", "logical_seat"]
+        for e in self.libinput_events("DEVICE_ADDED"):
             self.dict_key_crosscheck(e, keys)
-            self.assertEqual(e['seat'], 'seat0')
-            self.assertEqual(e['logical_seat'], 'default')
+            self.assertEqual(e["seat"], "seat0")
+            self.assertEqual(e["logical_seat"], "default")
 
     def test_events_libinput_pointer_motion(self):
-        keys = ['type', 'time', 'delta', 'unaccel']
-        for e in self.libinput_events('POINTER_MOTION'):
+        keys = ["type", "time", "delta", "unaccel"]
+        for e in self.libinput_events("POINTER_MOTION"):
             self.dict_key_crosscheck(e, keys)
-            delta = e['delta']
+            delta = e["delta"]
             self.assertTrue(isinstance(delta, list))
             self.assertEqual(len(delta), 2)
             for d in delta:
                 self.assertTrue(isinstance(d, float))
-            unaccel = e['unaccel']
+            unaccel = e["unaccel"]
             self.assertTrue(isinstance(unaccel, list))
             self.assertEqual(len(unaccel), 2)
             for d in unaccel:
                 self.assertTrue(isinstance(d, float))
 
     def test_events_libinput_pointer_button(self):
-        keys = ['type', 'time', 'button', 'state', 'seat_count']
-        for e in self.libinput_events('POINTER_BUTTON'):
+        keys = ["type", "time", "button", "state", "seat_count"]
+        for e in self.libinput_events("POINTER_BUTTON"):
             self.dict_key_crosscheck(e, keys)
-            button = e['button']
+            button = e["button"]
             self.assertGreater(button, 0x100)  # BTN_0
             self.assertLess(button, 0x160)  # KEY_OK
-            state = e['state']
-            self.assertIn(state, ['pressed', 'released'])
-            scount = e['seat_count']
+            state = e["state"]
+            self.assertIn(state, ["pressed", "released"])
+            scount = e["seat_count"]
             self.assertGreaterEqual(scount, 0)
 
     def test_events_libinput_pointer_absolute(self):
-        keys = ['type', 'time', 'point', 'transformed']
-        for e in self.libinput_events('POINTER_MOTION_ABSOLUTE'):
+        keys = ["type", "time", "point", "transformed"]
+        for e in self.libinput_events("POINTER_MOTION_ABSOLUTE"):
             self.dict_key_crosscheck(e, keys)
-            point = e['point']
+            point = e["point"]
             self.assertTrue(isinstance(point, list))
             self.assertEqual(len(point), 2)
             for p in point:
@@ -316,7 +334,7 @@ class TestYaml(unittest.TestCase):
                 self.assertGreater(p, 0.0)
                 self.assertLess(p, 300.0)
 
-            transformed = e['transformed']
+            transformed = e["transformed"]
             self.assertTrue(isinstance(transformed, list))
             self.assertEqual(len(transformed), 2)
             for t in transformed:
@@ -325,25 +343,24 @@ class TestYaml(unittest.TestCase):
                 self.assertLess(t, 100.0)
 
     def test_events_libinput_touch(self):
-        keys = ['type', 'time', 'slot', 'seat_slot']
+        keys = ["type", "time", "slot", "seat_slot"]
         for e in self.libinput_events():
-            if (not e['type'].startswith('TOUCH_') or
-                    e['type'] == 'TOUCH_FRAME'):
+            if not e["type"].startswith("TOUCH_") or e["type"] == "TOUCH_FRAME":
                 continue
 
             for k in keys:
                 self.assertIn(k, e.keys())
-            slot = e['slot']
-            seat_slot = e['seat_slot']
+            slot = e["slot"]
+            seat_slot = e["seat_slot"]
 
             self.assertGreaterEqual(slot, 0)
             self.assertGreaterEqual(seat_slot, 0)
 
     def test_events_libinput_touch_down(self):
-        keys = ['type', 'time', 'slot', 'seat_slot', 'point', 'transformed']
-        for e in self.libinput_events('TOUCH_DOWN'):
+        keys = ["type", "time", "slot", "seat_slot", "point", "transformed"]
+        for e in self.libinput_events("TOUCH_DOWN"):
             self.dict_key_crosscheck(e, keys)
-            point = e['point']
+            point = e["point"]
             self.assertTrue(isinstance(point, list))
             self.assertEqual(len(point), 2)
             for p in point:
@@ -351,7 +368,7 @@ class TestYaml(unittest.TestCase):
                 self.assertGreater(p, 0.0)
                 self.assertLess(p, 300.0)
 
-            transformed = e['transformed']
+            transformed = e["transformed"]
             self.assertTrue(isinstance(transformed, list))
             self.assertEqual(len(transformed), 2)
             for t in transformed:
@@ -360,10 +377,10 @@ class TestYaml(unittest.TestCase):
                 self.assertLess(t, 100.0)
 
     def test_events_libinput_touch_motion(self):
-        keys = ['type', 'time', 'slot', 'seat_slot', 'point', 'transformed']
-        for e in self.libinput_events('TOUCH_MOTION'):
+        keys = ["type", "time", "slot", "seat_slot", "point", "transformed"]
+        for e in self.libinput_events("TOUCH_MOTION"):
             self.dict_key_crosscheck(e, keys)
-            point = e['point']
+            point = e["point"]
             self.assertTrue(isinstance(point, list))
             self.assertEqual(len(point), 2)
             for p in point:
@@ -371,7 +388,7 @@ class TestYaml(unittest.TestCase):
                 self.assertGreater(p, 0.0)
                 self.assertLess(p, 300.0)
 
-            transformed = e['transformed']
+            transformed = e["transformed"]
             self.assertTrue(isinstance(transformed, list))
             self.assertEqual(len(transformed), 2)
             for t in transformed:
@@ -380,25 +397,25 @@ class TestYaml(unittest.TestCase):
                 self.assertLess(t, 100.0)
 
     def test_events_libinput_touch_frame(self):
-        devices = self.yaml['devices']
+        devices = self.yaml["devices"]
         for d in devices:
-            events = d['events']
+            events = d["events"]
             if not events:
                 raise unittest.SkipTest()
             for e in events:
                 try:
-                    evdev = e['libinput']
+                    evdev = e["libinput"]
                 except KeyError:
                     continue
 
                 need_frame = False
                 for ev in evdev:
-                    t = ev['type']
-                    if not t.startswith('TOUCH_'):
+                    t = ev["type"]
+                    if not t.startswith("TOUCH_"):
                         self.assertFalse(need_frame)
                         continue
 
-                    if t == 'TOUCH_FRAME':
+                    if t == "TOUCH_FRAME":
                         self.assertTrue(need_frame)
                         need_frame = False
                     else:
@@ -407,177 +424,175 @@ class TestYaml(unittest.TestCase):
                 self.assertFalse(need_frame)
 
     def test_events_libinput_gesture_pinch(self):
-        keys = ['type', 'time', 'nfingers', 'delta',
-                'unaccel', 'angle_delta', 'scale']
-        for e in self.libinput_events(['GESTURE_PINCH_BEGIN',
-                                       'GESTURE_PINCH_UPDATE',
-                                       'GESTURE_PINCH_END']):
+        keys = ["type", "time", "nfingers", "delta", "unaccel", "angle_delta", "scale"]
+        for e in self.libinput_events(
+            ["GESTURE_PINCH_BEGIN", "GESTURE_PINCH_UPDATE", "GESTURE_PINCH_END"]
+        ):
             self.dict_key_crosscheck(e, keys)
-            delta = e['delta']
+            delta = e["delta"]
             self.assertTrue(isinstance(delta, list))
             self.assertEqual(len(delta), 2)
             for d in delta:
                 self.assertTrue(isinstance(d, float))
-            unaccel = e['unaccel']
+            unaccel = e["unaccel"]
             self.assertTrue(isinstance(unaccel, list))
             self.assertEqual(len(unaccel), 2)
             for d in unaccel:
                 self.assertTrue(isinstance(d, float))
 
-            adelta = e['angle_delta']
+            adelta = e["angle_delta"]
             self.assertTrue(isinstance(adelta, list))
             self.assertEqual(len(adelta), 2)
             for d in adelta:
                 self.assertTrue(isinstance(d, float))
 
-            scale = e['scale']
+            scale = e["scale"]
             self.assertTrue(isinstance(scale, list))
             self.assertEqual(len(scale), 2)
             for d in scale:
                 self.assertTrue(isinstance(d, float))
 
     def test_events_libinput_gesture_swipe(self):
-        keys = ['type', 'time', 'nfingers', 'delta',
-                'unaccel']
-        for e in self.libinput_events(['GESTURE_SWIPE_BEGIN',
-                                       'GESTURE_SWIPE_UPDATE',
-                                       'GESTURE_SWIPE_END']):
+        keys = ["type", "time", "nfingers", "delta", "unaccel"]
+        for e in self.libinput_events(
+            ["GESTURE_SWIPE_BEGIN", "GESTURE_SWIPE_UPDATE", "GESTURE_SWIPE_END"]
+        ):
             self.dict_key_crosscheck(e, keys)
-            delta = e['delta']
+            delta = e["delta"]
             self.assertTrue(isinstance(delta, list))
             self.assertEqual(len(delta), 2)
             for d in delta:
                 self.assertTrue(isinstance(d, float))
-            unaccel = e['unaccel']
+            unaccel = e["unaccel"]
             self.assertTrue(isinstance(unaccel, list))
             self.assertEqual(len(unaccel), 2)
             for d in unaccel:
                 self.assertTrue(isinstance(d, float))
 
     def test_events_libinput_tablet_pad_button(self):
-        keys = ['type', 'time', 'button', 'state', 'mode', 'is-toggle']
+        keys = ["type", "time", "button", "state", "mode", "is-toggle"]
 
-        for e in self.libinput_events('TABLET_PAD_BUTTON'):
+        for e in self.libinput_events("TABLET_PAD_BUTTON"):
             self.dict_key_crosscheck(e, keys)
 
-            b = e['button']
+            b = e["button"]
             self.assertTrue(isinstance(b, int))
             self.assertGreaterEqual(b, 0)
             self.assertLessEqual(b, 16)
 
-            state = e['state']
-            self.assertIn(state, ['pressed', 'released'])
+            state = e["state"]
+            self.assertIn(state, ["pressed", "released"])
 
-            m = e['mode']
+            m = e["mode"]
             self.assertTrue(isinstance(m, int))
             self.assertGreaterEqual(m, 0)
             self.assertLessEqual(m, 3)
 
-            t = e['is-toggle']
+            t = e["is-toggle"]
             self.assertTrue(isinstance(t, bool))
 
     def test_events_libinput_tablet_pad_ring(self):
-        keys = ['type', 'time', 'number', 'position', 'source', 'mode']
+        keys = ["type", "time", "number", "position", "source", "mode"]
 
-        for e in self.libinput_events('TABLET_PAD_RING'):
+        for e in self.libinput_events("TABLET_PAD_RING"):
             self.dict_key_crosscheck(e, keys)
 
-            n = e['number']
+            n = e["number"]
             self.assertTrue(isinstance(n, int))
             self.assertGreaterEqual(n, 0)
             self.assertLessEqual(n, 4)
 
-            p = e['position']
+            p = e["position"]
             self.assertTrue(isinstance(p, float))
             if p != -1.0:  # special 'end' case
                 self.assertGreaterEqual(p, 0.0)
                 self.assertLess(p, 360.0)
 
-            m = e['mode']
+            m = e["mode"]
             self.assertTrue(isinstance(m, int))
             self.assertGreaterEqual(m, 0)
             self.assertLessEqual(m, 3)
 
-            s = e['source']
-            self.assertIn(s, ['finger', 'unknown'])
+            s = e["source"]
+            self.assertIn(s, ["finger", "unknown"])
 
     def test_events_libinput_tablet_pad_strip(self):
-        keys = ['type', 'time', 'number', 'position', 'source', 'mode']
+        keys = ["type", "time", "number", "position", "source", "mode"]
 
-        for e in self.libinput_events('TABLET_PAD_STRIP'):
+        for e in self.libinput_events("TABLET_PAD_STRIP"):
             self.dict_key_crosscheck(e, keys)
 
-            n = e['number']
+            n = e["number"]
             self.assertTrue(isinstance(n, int))
             self.assertGreaterEqual(n, 0)
             self.assertLessEqual(n, 4)
 
-            p = e['position']
+            p = e["position"]
             self.assertTrue(isinstance(p, float))
             if p != -1.0:  # special 'end' case
                 self.assertGreaterEqual(p, 0.0)
                 self.assertLessEqual(p, 1.0)
 
-            m = e['mode']
+            m = e["mode"]
             self.assertTrue(isinstance(m, int))
             self.assertGreaterEqual(m, 0)
             self.assertLessEqual(m, 3)
 
-            s = e['source']
-            self.assertIn(s, ['finger', 'unknown'])
+            s = e["source"]
+            self.assertIn(s, ["finger", "unknown"])
 
     def test_events_libinput_tablet_tool_proximity(self):
-        keys = ['type', 'time', 'proximity', 'tool-type', 'serial', 'axes']
+        keys = ["type", "time", "proximity", "tool-type", "serial", "axes"]
 
-        for e in self.libinput_events('TABLET_TOOL_PROXIMITY'):
+        for e in self.libinput_events("TABLET_TOOL_PROXIMITY"):
             for k in keys:
                 self.assertIn(k, e)
 
-            p = e['proximity']
-            self.assertIn(p, ['in', 'out'])
+            p = e["proximity"]
+            self.assertIn(p, ["in", "out"])
 
-            p = e['tool-type']
-            self.assertIn(p, ['pen', 'eraser', 'brush', 'airbrush', 'mouse',
-                              'lens', 'unknown'])
+            p = e["tool-type"]
+            self.assertIn(
+                p, ["pen", "eraser", "brush", "airbrush", "mouse", "lens", "unknown"]
+            )
 
-            s = e['serial']
+            s = e["serial"]
             self.assertTrue(isinstance(s, int))
             self.assertGreaterEqual(s, 0)
 
-            a = e['axes']
-            for ax in e['axes']:
-                self.assertIn(a, 'pdtrsw')
+            a = e["axes"]
+            for ax in e["axes"]:
+                self.assertIn(a, "pdtrsw")
 
     def test_events_libinput_tablet_tool(self):
-        keys = ['type', 'time', 'tip']
+        keys = ["type", "time", "tip"]
 
-        for e in self.libinput_events(['TABLET_TOOL_AXIS',
-                                       'TABLET_TOOL_TIP']):
+        for e in self.libinput_events(["TABLET_TOOL_AXIS", "TABLET_TOOL_TIP"]):
             for k in keys:
                 self.assertIn(k, e)
 
-            t = e['tip']
-            self.assertIn(t, ['down', 'up'])
+            t = e["tip"]
+            self.assertIn(t, ["down", "up"])
 
     def test_events_libinput_tablet_tool_button(self):
-        keys = ['type', 'time', 'button', 'state']
+        keys = ["type", "time", "button", "state"]
 
-        for e in self.libinput_events('TABLET_TOOL_BUTTON'):
+        for e in self.libinput_events("TABLET_TOOL_BUTTON"):
             self.dict_key_crosscheck(e, keys)
 
-            b = e['button']
+            b = e["button"]
             # STYLUS, STYLUS2, STYLUS3
-            self.assertIn(b, [0x14b, 0x14c, 0x139])
+            self.assertIn(b, [0x14B, 0x14C, 0x139])
 
-            s = e['state']
-            self.assertIn(s, ['pressed', 'released'])
+            s = e["state"]
+            self.assertIn(s, ["pressed", "released"])
 
     def test_events_libinput_tablet_tool_axes(self):
-        for e in self.libinput_events(['TABLET_TOOL_PROXIMITY',
-                                       'TABLET_TOOL_AXIS',
-                                       'TABLET_TOOL_TIP']):
+        for e in self.libinput_events(
+            ["TABLET_TOOL_PROXIMITY", "TABLET_TOOL_AXIS", "TABLET_TOOL_TIP"]
+        ):
 
-            point = e['point']
+            point = e["point"]
             self.assertTrue(isinstance(point, list))
             self.assertEqual(len(point), 2)
             for p in point:
@@ -585,7 +600,7 @@ class TestYaml(unittest.TestCase):
                 self.assertGreater(p, 0.0)
 
             try:
-                tilt = e['tilt']
+                tilt = e["tilt"]
                 self.assertTrue(isinstance(tilt, list))
                 self.assertEqual(len(tilt), 2)
                 for t in tilt:
@@ -594,70 +609,75 @@ class TestYaml(unittest.TestCase):
                 pass
 
             try:
-                d = e['distance']
+                d = e["distance"]
                 self.assertTrue(isinstance(d, float))
                 self.assertGreaterEqual(d, 0.0)
-                self.assertNotIn('pressure', e)
+                self.assertNotIn("pressure", e)
             except KeyError:
                 pass
 
             try:
-                p = e['pressure']
+                p = e["pressure"]
                 self.assertTrue(isinstance(p, float))
                 self.assertGreaterEqual(p, 0.0)
-                self.assertNotIn('distance', e)
+                self.assertNotIn("distance", e)
             except KeyError:
                 pass
 
             try:
-                r = e['rotation']
+                r = e["rotation"]
                 self.assertTrue(isinstance(r, float))
                 self.assertGreaterEqual(r, 0.0)
             except KeyError:
                 pass
 
             try:
-                s = e['slider']
+                s = e["slider"]
                 self.assertTrue(isinstance(s, float))
                 self.assertGreaterEqual(s, 0.0)
             except KeyError:
                 pass
 
             try:
-                w = e['wheel']
+                w = e["wheel"]
                 self.assertTrue(isinstance(w, float))
                 self.assertGreaterEqual(w, 0.0)
-                self.assertIn('wheel-discrete', e)
-                wd = e['wheel-discrete']
+                self.assertIn("wheel-discrete", e)
+                wd = e["wheel-discrete"]
                 self.assertTrue(isinstance(wd, 1))
                 self.assertGreaterEqual(wd, 0.0)
 
                 def sign(x):
                     (1, -1)[x < 0]
+
                 self.assertTrue(sign(w), sign(wd))
             except KeyError:
                 pass
 
     def test_events_libinput_switch(self):
-        keys = ['type', 'time', 'switch', 'state']
+        keys = ["type", "time", "switch", "state"]
 
-        for e in self.libinput_events('SWITCH_TOGGLE'):
+        for e in self.libinput_events("SWITCH_TOGGLE"):
             self.dict_key_crosscheck(e, keys)
 
-            s = e['switch']
+            s = e["switch"]
             self.assertTrue(isinstance(s, int))
             self.assertIn(s, [0x00, 0x01])
 
             # yaml converts on/off to true/false
-            state = e['state']
+            state = e["state"]
             self.assertTrue(isinstance(state, bool))
 
 
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser(description='Verify a YAML recording')
-    parser.add_argument('recording', metavar='recorded-file.yaml',
-                        type=str, help='Path to device recording')
-    parser.add_argument('--verbose', action='store_true')
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Verify a YAML recording")
+    parser.add_argument(
+        "recording",
+        metavar="recorded-file.yaml",
+        type=str,
+        help="Path to device recording",
+    )
+    parser.add_argument("--verbose", action="store_true")
     args, remainder = parser.parse_known_args()
     TestYaml.filename = args.recording
     verbosity = 1
index 556cf4810a8a2bd5ca5c6396d4e0e43ce62337e0..b3710c9e627a103b0b82f399cbeafd53304e9d6f 100755 (executable)
@@ -35,9 +35,11 @@ try:
     import yaml
     import pyudev
 except ModuleNotFoundError as e:
-    print('Error: {}'.format(e), file=sys.stderr)
-    print('One or more python modules are missing. Please install those '
-          'modules and re-run this tool.')
+    print("Error: {}".format(e), file=sys.stderr)
+    print(
+        "One or more python modules are missing. Please install those "
+        "modules and re-run this tool."
+    )
     sys.exit(1)
 
 
@@ -53,25 +55,27 @@ class YamlException(Exception):
 
 
 def fetch(yaml, key):
-    '''Helper function to avoid confusing a YAML error with a
-    normal KeyError bug'''
+    """Helper function to avoid confusing a YAML error with a
+    normal KeyError bug"""
     try:
         return yaml[key]
     except KeyError:
-        raise YamlException('Failed to get \'{}\' from recording.'.format(key))
+        raise YamlException("Failed to get '{}' from recording.".format(key))
 
 
 def check_udev_properties(yaml_data, uinput):
-    '''
+    """
     Compare the properties our new uinput device has with the ones from the
     recording and ring the alarm bell if one of them is off.
-    '''
-    yaml_udev_section = fetch(yaml_data, 'udev')
-    yaml_udev_props = fetch(yaml_udev_section, 'properties')
-    yaml_props = {k: v for (k, v) in [prop.split('=', maxsplit=1) for prop in yaml_udev_props]}
+    """
+    yaml_udev_section = fetch(yaml_data, "udev")
+    yaml_udev_props = fetch(yaml_udev_section, "properties")
+    yaml_props = {
+        k: v for (k, v) in [prop.split("=", maxsplit=1) for prop in yaml_udev_props]
+    }
     try:
         # We don't assign this one to virtual devices
-        del yaml_props['LIBINPUT_DEVICE_GROUP']
+        del yaml_props["LIBINPUT_DEVICE_GROUP"]
     except KeyError:
         pass
 
@@ -82,42 +86,52 @@ def check_udev_properties(yaml_data, uinput):
     for name, value in udev_device.properties.items():
         if name in yaml_props:
             if yaml_props[name] != value:
-                error(f'Warning: udev property mismatch: recording has {name}={yaml_props[name]}, device has {name}={value}')
+                error(
+                    f"Warning: udev property mismatch: recording has {name}={yaml_props[name]}, device has {name}={value}"
+                )
             del yaml_props[name]
         else:
             # The list of properties we add to the recording, see libinput-record.c
-            prefixes = ("ID_INPUT", "LIBINPUT", "EVDEV_ABS", "MOUSE_DPI", "POINTINGSTICK_")
+            prefixes = (
+                "ID_INPUT",
+                "LIBINPUT",
+                "EVDEV_ABS",
+                "MOUSE_DPI",
+                "POINTINGSTICK_",
+            )
             for prefix in prefixes:
                 if name.startswith(prefix):
-                    error(f'Warning: unexpected property: {name}={value}')
+                    error(f"Warning: unexpected property: {name}={value}")
 
     # the ones we found above were removed from the dict
     for name, value in yaml_props.items():
-        error(f'Warning: device is missing recorded udev property: {name}={value}')
+        error(f"Warning: device is missing recorded udev property: {name}={value}")
 
 
 def create(device):
-    evdev = fetch(device, 'evdev')
+    evdev = fetch(device, "evdev")
 
     d = libevdev.Device()
-    d.name = fetch(evdev, 'name')
+    d.name = fetch(evdev, "name")
 
-    ids = fetch(evdev, 'id')
+    ids = fetch(evdev, "id")
     if len(ids) != 4:
-        raise YamlException('Invalid ID format: {}'.format(ids))
-    d.id = dict(zip(['bustype', 'vendor', 'product', 'version'], ids))
+        raise YamlException("Invalid ID format: {}".format(ids))
+    d.id = dict(zip(["bustype", "vendor", "product", "version"], ids))
 
-    codes = fetch(evdev, 'codes')
+    codes = fetch(evdev, "codes")
     for evtype, evcodes in codes.items():
         for code in evcodes:
             data = None
             if evtype == libevdev.EV_ABS.value:
-                values = fetch(evdev, 'absinfo')[code]
-                absinfo = libevdev.InputAbsInfo(minimum=values[0],
-                                                maximum=values[1],
-                                                fuzz=values[2],
-                                                flat=values[3],
-                                                resolution=values[4])
+                values = fetch(evdev, "absinfo")[code]
+                absinfo = libevdev.InputAbsInfo(
+                    minimum=values[0],
+                    maximum=values[1],
+                    fuzz=values[2],
+                    flat=values[3],
+                    resolution=values[4],
+                )
                 data = absinfo
             elif evtype == libevdev.EV_REP.value:
                 if code == libevdev.EV_REP.REP_DELAY.value:
@@ -126,7 +140,7 @@ def create(device):
                     data = 20
             d.enable(libevdev.evbit(evtype, code), data=data)
 
-    properties = fetch(evdev, 'properties')
+    properties = fetch(evdev, "properties")
     for prop in properties:
         d.enable(libevdev.propbit(prop))
 
@@ -140,30 +154,39 @@ def create(device):
 def print_events(devnode, indent, evs):
     devnode = os.path.basename(devnode)
     for e in evs:
-        print("{}: {}{:06d}.{:06d} {} / {:<20s} {:4d}".format(
-            devnode, ' ' * (indent * 8), e.sec, e.usec, e.type.name, e.code.name, e.value))
+        print(
+            "{}: {}{:06d}.{:06d} {} / {:<20s} {:4d}".format(
+                devnode,
+                " " * (indent * 8),
+                e.sec,
+                e.usec,
+                e.type.name,
+                e.code.name,
+                e.value,
+            )
+        )
 
 
 def replay(device, verbose):
-    events = fetch(device, 'events')
+    events = fetch(device, "events")
     if events is None:
         return
-    uinput = device['__uinput']
+    uinput = device["__uinput"]
 
     # The first event may have a nonzero offset but we want to replay
     # immediately regardless. When replaying multiple devices, the first
     # offset is the offset from the first event on any device.
-    offset = time.time() - device['__first_event_offset']
+    offset = time.time() - device["__first_event_offset"]
 
     if offset < 0:
-        error('WARNING: event time offset is in the future, refusing to replay')
+        error("WARNING: event time offset is in the future, refusing to replay")
         return
 
     # each 'evdev' set contains one SYN_REPORT so we only need to check for
     # the time offset once per event
     for event in events:
         try:
-            evdev = fetch(event, 'evdev')
+            evdev = fetch(event, "evdev")
         except YamlException:
             continue
 
@@ -174,25 +197,31 @@ def replay(device, verbose):
         if evtime - now > 150 / 1e6:  # 150 µs error margin
             time.sleep(evtime - now - 150 / 1e6)
 
-        evs = [libevdev.InputEvent(libevdev.evbit(e[2], e[3]), value=e[4], sec=e[0], usec=e[1]) for e in evdev]
+        evs = [
+            libevdev.InputEvent(
+                libevdev.evbit(e[2], e[3]), value=e[4], sec=e[0], usec=e[1]
+            )
+            for e in evdev
+        ]
         uinput.send_events(evs)
         if verbose:
-            print_events(uinput.devnode, device['__index'], evs)
+            print_events(uinput.devnode, device["__index"], evs)
 
 
 def first_timestamp(device):
     try:
-        events = fetch(device, 'events')
+        events = fetch(device, "events")
         if events is None:
-            raise YamlException('No events from this device')
+            raise YamlException("No events from this device")
 
-        evdev = fetch(events[0], 'evdev')
+        evdev = fetch(events[0], "evdev")
         (sec, usec, *_) = evdev[0]
 
-        return sec + usec / 1.e6
+        return sec + usec / 1.0e6
 
     except YamlException:
         import math
+
         return math.inf
 
 
@@ -204,7 +233,7 @@ def wrap(func, *args):
 
 
 def loop(args, recording):
-    devices = fetch(recording, 'devices')
+    devices = fetch(recording, "devices")
 
     # All devices need to start replaying at the same time, so let's find
     # the very first event and offset everything by that timestamp.
@@ -212,13 +241,13 @@ def loop(args, recording):
 
     for idx, d in enumerate(devices):
         uinput = create(d)
-        print('{}: {}'.format(uinput.devnode, uinput.name))
-        d['__uinput'] = uinput  # cheaper to hide it in the dict then work around it
-        d['__index'] = idx
-        d['__first_event_offset'] = toffset
+        print("{}: {}".format(uinput.devnode, uinput.name))
+        d["__uinput"] = uinput  # cheaper to hide it in the dict then work around it
+        d["__index"] = idx
+        d["__first_event_offset"] = toffset
 
     while True:
-        input('Hit enter to start replaying')
+        input("Hit enter to start replaying")
 
         processes = []
         for d in devices:
@@ -236,66 +265,83 @@ def loop(args, recording):
 
 def create_device_quirk(device):
     try:
-        quirks = fetch(device, 'quirks')
+        quirks = fetch(device, "quirks")
         if not quirks:
             return None
     except YamlException:
         return None
     # Where the device has a quirk, we match on name, vendor and product.
     # That's the best match we can assemble here from the info we have.
-    evdev = fetch(device, 'evdev')
-    name = fetch(evdev, 'name')
-    id = fetch(evdev, 'id')
-    quirk = ('[libinput-replay {name}]\n'
-             'MatchName={name}\n'
-             'MatchVendor=0x{id[1]:04X}\n'
-             'MatchProduct=0x{id[2]:04X}\n').format(name=name, id=id)
-    quirk += '\n'.join(quirks)
+    evdev = fetch(device, "evdev")
+    name = fetch(evdev, "name")
+    id = fetch(evdev, "id")
+    quirk = (
+        "[libinput-replay {name}]\n"
+        "MatchName={name}\n"
+        "MatchVendor=0x{id[1]:04X}\n"
+        "MatchProduct=0x{id[2]:04X}\n"
+    ).format(name=name, id=id)
+    quirk += "\n".join(quirks)
     return quirk
 
 
 def setup_quirks(recording):
-    devices = fetch(recording, 'devices')
+    devices = fetch(recording, "devices")
     overrides = None
     quirks = []
     for d in devices:
-        if 'quirks' in d:
+        if "quirks" in d:
             quirk = create_device_quirk(d)
             if quirk:
                 quirks.append(quirk)
     if not quirks:
         return None
 
-    overrides = Path('/etc/libinput/local-overrides.quirks')
+    overrides = Path("/etc/libinput/local-overrides.quirks")
     if overrides.exists():
-        print('{} exists, please move it out of the way first'.format(overrides), file=sys.stderr)
+        print(
+            "{} exists, please move it out of the way first".format(overrides),
+            file=sys.stderr,
+        )
         sys.exit(1)
 
     overrides.parent.mkdir(exist_ok=True)
-    with overrides.open('w+') as fd:
-        fd.write('# This file was generated by libinput replay\n')
-        fd.write('# Unless libinput replay is running right now, remove this file.\n')
-        fd.write('\n\n'.join(quirks))
+    with overrides.open("w+") as fd:
+        fd.write("# This file was generated by libinput replay\n")
+        fd.write("# Unless libinput replay is running right now, remove this file.\n")
+        fd.write("\n\n".join(quirks))
 
     return overrides
 
 
 def check_file(recording):
-    version = fetch(recording, 'version')
+    version = fetch(recording, "version")
     if version != SUPPORTED_FILE_VERSION:
-        raise YamlException('Invalid file format: {}, expected {}'.format(version, SUPPORTED_FILE_VERSION))
-
-    ndevices = fetch(recording, 'ndevices')
-    devices = fetch(recording, 'devices')
+        raise YamlException(
+            "Invalid file format: {}, expected {}".format(
+                version, SUPPORTED_FILE_VERSION
+            )
+        )
+
+    ndevices = fetch(recording, "ndevices")
+    devices = fetch(recording, "devices")
     if ndevices != len(devices):
-        error('WARNING: truncated file, expected {} devices, got {}'.format(ndevices, len(devices)))
+        error(
+            "WARNING: truncated file, expected {} devices, got {}".format(
+                ndevices, len(devices)
+            )
+        )
 
 
 def main():
-    parser = argparse.ArgumentParser(description='Replay a device recording')
-    parser.add_argument('recording', metavar='recorded-file.yaml',
-                        type=str, help='Path to device recording')
-    parser.add_argument('--verbose', action='store_true')
+    parser = argparse.ArgumentParser(description="Replay a device recording")
+    parser.add_argument(
+        "recording",
+        metavar="recorded-file.yaml",
+        type=str,
+        help="Path to device recording",
+    )
+    parser.add_argument("--verbose", action="store_true")
     args = parser.parse_args()
 
     quirks_file = None
@@ -309,13 +355,13 @@ def main():
     except KeyboardInterrupt:
         pass
     except (PermissionError, OSError) as e:
-        error('Error: failed to open device: {}'.format(e))
+        error("Error: failed to open device: {}".format(e))
     except YamlException as e:
-        error('Error: failed to parse recording: {}'.format(e))
+        error("Error: failed to parse recording: {}".format(e))
     finally:
         if quirks_file:
             quirks_file.unlink()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
index 373aa8b91d0a99c28b7ee9f9472a740cf6cbae97..5c92b11a49080d66ed92ea870fefea50e7c87101 100755 (executable)
@@ -32,15 +32,15 @@ import logging
 try:
     import pytest
 except ImportError:
-    print('Failed to import pytest. Skipping.', file=sys.stderr)
+    print("Failed to import pytest. Skipping.", file=sys.stderr)
     sys.exit(77)
 
 
-logger = logging.getLogger('test')
+logger = logging.getLogger("test")
 logger.setLevel(logging.DEBUG)
 
-if '@DISABLE_WARNING@' != 'yes':
-    print('This is the source file, run the one in the meson builddir instead')
+if "@DISABLE_WARNING@" != "yes":
+    print("This is the source file, run the one in the meson builddir instead")
     sys.exit(1)
 
 
@@ -49,9 +49,13 @@ def _disable_coredump():
 
 
 def run_command(args):
-    logger.debug('run command: {}'.format(' '.join(args)))
-    with subprocess.Popen(args, preexec_fn=_disable_coredump,
-                          stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
+    logger.debug("run command: {}".format(" ".join(args)))
+    with subprocess.Popen(
+        args,
+        preexec_fn=_disable_coredump,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+    ) as p:
         try:
             p.wait(0.7)
         except subprocess.TimeoutExpired:
@@ -59,11 +63,11 @@ def run_command(args):
         stdout, stderr = p.communicate(timeout=5)
         if p.returncode == -3:
             p.returncode = 0
-        return p.returncode, stdout.decode('UTF-8'), stderr.decode('UTF-8')
+        return p.returncode, stdout.decode("UTF-8"), stderr.decode("UTF-8")
 
 
 class LibinputTool(object):
-    libinput_tool = 'libinput'
+    libinput_tool = "libinput"
     subtool = None
 
     def __init__(self, subtool=None):
@@ -92,43 +96,43 @@ class LibinputTool(object):
     def run_command_unrecognized_option(self, args):
         rc, stdout, stderr = self.run_command(args)
         assert rc == 2, (rc, stdout, stderr)
-        assert stdout.startswith('Usage') or stdout == ''
-        assert 'unrecognized option' in stderr
+        assert stdout.startswith("Usage") or stdout == ""
+        assert "unrecognized option" in stderr
 
     def run_command_missing_arg(self, args):
         rc, stdout, stderr = self.run_command(args)
         assert rc == 2, (rc, stdout, stderr)
-        assert stdout.startswith('Usage') or stdout == ''
-        assert 'requires an argument' in stderr
+        assert stdout.startswith("Usage") or stdout == ""
+        assert "requires an argument" in stderr
 
     def run_command_unrecognized_tool(self, args):
         rc, stdout, stderr = self.run_command(args)
         assert rc == 2, (rc, stdout, stderr)
-        assert stdout.startswith('Usage') or stdout == ''
-        assert 'is not installed' in stderr
+        assert stdout.startswith("Usage") or stdout == ""
+        assert "is not installed" in stderr
 
 
 class LibinputDebugGui(LibinputTool):
-    def __init__(self, subtool='debug-gui'):
-        assert subtool == 'debug-gui'
+    def __init__(self, subtool="debug-gui"):
+        assert subtool == "debug-gui"
         super().__init__(subtool)
 
-        debug_gui_enabled = '@MESON_ENABLED_DEBUG_GUI@' == 'True'
+        debug_gui_enabled = "@MESON_ENABLED_DEBUG_GUI@" == "True"
         if not debug_gui_enabled:
             pytest.skip()
 
-        if not os.getenv('DISPLAY') and not os.getenv('WAYLAND_DISPLAY'):
+        if not os.getenv("DISPLAY") and not os.getenv("WAYLAND_DISPLAY"):
             pytest.skip()
 
         # 77 means gtk_init() failed, which is probably because you can't
         # connect to the display server.
-        rc, _, _ = self.run_command(['--help'])
+        rc, _, _ = self.run_command(["--help"])
         if rc == 77:
             pytest.skip()
 
 
 def get_tool(subtool=None):
-    if subtool == 'debug-gui':
+    if subtool == "debug-gui":
         return LibinputDebugGui()
     else:
         return LibinputTool(subtool)
@@ -139,110 +143,110 @@ def libinput():
     return get_tool()
 
 
-@pytest.fixture(params=['debug-events', 'debug-gui'])
+@pytest.fixture(params=["debug-events", "debug-gui"])
 def libinput_debug_tool(request):
     yield get_tool(request.param)
 
 
 @pytest.fixture
 def libinput_debug_events():
-    return get_tool('debug-events')
+    return get_tool("debug-events")
 
 
 @pytest.fixture
 def libinput_debug_gui():
-    return get_tool('debug-gui')
+    return get_tool("debug-gui")
 
 
 @pytest.fixture
 def libinput_record():
-    return get_tool('record')
+    return get_tool("record")
 
 
 def test_help(libinput):
-    stdout, stderr = libinput.run_command_success(['--help'])
-    assert stdout.startswith('Usage:')
-    assert stderr == ''
+    stdout, stderr = libinput.run_command_success(["--help"])
+    assert stdout.startswith("Usage:")
+    assert stderr == ""
 
 
 def test_version(libinput):
-    stdout, stderr = libinput.run_command_success(['--version'])
-    assert stdout.startswith('1')
-    assert stderr == ''
+    stdout, stderr = libinput.run_command_success(["--version"])
+    assert stdout.startswith("1")
+    assert stderr == ""
 
 
-@pytest.mark.parametrize('argument', ['--banana', '--foo', '--quiet', '--verbose'])
+@pytest.mark.parametrize("argument", ["--banana", "--foo", "--quiet", "--verbose"])
 def test_invalid_arguments(libinput, argument):
     libinput.run_command_unrecognized_option([argument])
 
 
-@pytest.mark.parametrize('tool', [['foo'], ['debug'], ['foo', '--quiet']])
+@pytest.mark.parametrize("tool", [["foo"], ["debug"], ["foo", "--quiet"]])
 def test_invalid_tool(libinput, tool):
     libinput.run_command_unrecognized_tool(tool)
 
 
 def test_udev_seat(libinput_debug_tool):
-    libinput_debug_tool.run_command_missing_arg(['--udev'])
-    libinput_debug_tool.run_command_success(['--udev', 'seat0'])
-    libinput_debug_tool.run_command_success(['--udev', 'seat1'])
+    libinput_debug_tool.run_command_missing_arg(["--udev"])
+    libinput_debug_tool.run_command_success(["--udev", "seat0"])
+    libinput_debug_tool.run_command_success(["--udev", "seat1"])
 
 
-@pytest.mark.skipif(os.environ.get('UDEV_NOT_AVAILABLE'), reason='udev required')
+@pytest.mark.skipif(os.environ.get("UDEV_NOT_AVAILABLE"), reason="udev required")
 def test_device_arg(libinput_debug_tool):
-    libinput_debug_tool.run_command_missing_arg(['--device'])
-    libinput_debug_tool.run_command_success(['--device', '/dev/input/event0'])
-    libinput_debug_tool.run_command_success(['--device', '/dev/input/event1'])
-    libinput_debug_tool.run_command_success(['/dev/input/event0'])
+    libinput_debug_tool.run_command_missing_arg(["--device"])
+    libinput_debug_tool.run_command_success(["--device", "/dev/input/event0"])
+    libinput_debug_tool.run_command_success(["--device", "/dev/input/event1"])
+    libinput_debug_tool.run_command_success(["/dev/input/event0"])
 
 
 options = {
-    'pattern': ['sendevents'],
+    "pattern": ["sendevents"],
     # enable/disable options
-    'enable-disable': [
-        'tap',
-        'drag',
-        'drag-lock',
-        'middlebutton',
-        'natural-scrolling',
-        'left-handed',
-        'dwt'
+    "enable-disable": [
+        "tap",
+        "drag",
+        "drag-lock",
+        "middlebutton",
+        "natural-scrolling",
+        "left-handed",
+        "dwt",
     ],
     # options with distinct values
-    'enums': {
-        'set-click-method': ['none', 'clickfinger', 'buttonareas'],
-        'set-scroll-method': ['none', 'twofinger', 'edge', 'button'],
-        'set-profile': ['adaptive', 'flat'],
-        'set-tap-map': ['lrm', 'lmr'],
+    "enums": {
+        "set-click-method": ["none", "clickfinger", "buttonareas"],
+        "set-scroll-method": ["none", "twofinger", "edge", "button"],
+        "set-profile": ["adaptive", "flat"],
+        "set-tap-map": ["lrm", "lmr"],
     },
     # options with a range
-    'ranges': {
-        'set-speed': (float, -1.0, +1.0),
-    }
+    "ranges": {
+        "set-speed": (float, -1.0, +1.0),
+    },
 }
 
 
 # Options that allow for glob patterns
-@pytest.mark.parametrize('option', options['pattern'])
+@pytest.mark.parametrize("option", options["pattern"])
 def test_options_pattern(libinput_debug_tool, option):
-    libinput_debug_tool.run_command_success(['--disable-{}'.format(option), '*'])
-    libinput_debug_tool.run_command_success(['--disable-{}'.format(option), 'abc*'])
+    libinput_debug_tool.run_command_success(["--disable-{}".format(option), "*"])
+    libinput_debug_tool.run_command_success(["--disable-{}".format(option), "abc*"])
 
 
-@pytest.mark.parametrize('option', options['enable-disable'])
+@pytest.mark.parametrize("option", options["enable-disable"])
 def test_options_enable_disable(libinput_debug_tool, option):
-    libinput_debug_tool.run_command_success(['--enable-{}'.format(option)])
-    libinput_debug_tool.run_command_success(['--disable-{}'.format(option)])
+    libinput_debug_tool.run_command_success(["--enable-{}".format(option)])
+    libinput_debug_tool.run_command_success(["--disable-{}".format(option)])
 
 
-@pytest.mark.parametrize('option', options['enums'].items())
+@pytest.mark.parametrize("option", options["enums"].items())
 def test_options_enums(libinput_debug_tool, option):
     name, values = option
     for v in values:
-        libinput_debug_tool.run_command_success(['--{}'.format(name), v])
-        libinput_debug_tool.run_command_success(['--{}={}'.format(name, v)])
+        libinput_debug_tool.run_command_success(["--{}".format(name), v])
+        libinput_debug_tool.run_command_success(["--{}={}".format(name, v)])
 
 
-@pytest.mark.parametrize('option', options['ranges'].items())
+@pytest.mark.parametrize("option", options["ranges"].items())
 def test_options_ranges(libinput_debug_tool, option):
     name, values = option
     range_type, minimum, maximum = values
@@ -250,114 +254,128 @@ def test_options_ranges(libinput_debug_tool, option):
     step = (maximum - minimum) / 10.0
     value = minimum
     while value < maximum:
-        libinput_debug_tool.run_command_success(['--{}'.format(name), str(value)])
-        libinput_debug_tool.run_command_success(['--{}={}'.format(name, value)])
+        libinput_debug_tool.run_command_success(["--{}".format(name), str(value)])
+        libinput_debug_tool.run_command_success(["--{}={}".format(name, value)])
         value += step
-    libinput_debug_tool.run_command_success(['--{}'.format(name), str(maximum)])
-    libinput_debug_tool.run_command_success(['--{}={}'.format(name, maximum)])
+    libinput_debug_tool.run_command_success(["--{}".format(name), str(maximum)])
+    libinput_debug_tool.run_command_success(["--{}={}".format(name, maximum)])
 
 
 def test_apply_to(libinput_debug_tool):
-    libinput_debug_tool.run_command_missing_arg(['--apply-to'])
-    libinput_debug_tool.run_command_success(['--apply-to', '*foo*'])
-    libinput_debug_tool.run_command_success(['--apply-to', 'foobar'])
-    libinput_debug_tool.run_command_success(['--apply-to', 'any'])
+    libinput_debug_tool.run_command_missing_arg(["--apply-to"])
+    libinput_debug_tool.run_command_success(["--apply-to", "*foo*"])
+    libinput_debug_tool.run_command_success(["--apply-to", "foobar"])
+    libinput_debug_tool.run_command_success(["--apply-to", "any"])
 
 
-@pytest.mark.parametrize('args', [['--verbose'], ['--quiet'],
-                                  ['--verbose', '--quiet'],
-                                  ['--quiet', '--verbose']])
+@pytest.mark.parametrize(
+    "args",
+    [["--verbose"], ["--quiet"], ["--verbose", "--quiet"], ["--quiet", "--verbose"]],
+)
 def test_debug_events_verbose_quiet(libinput_debug_events, args):
     libinput_debug_events.run_command_success(args)
 
 
-@pytest.mark.parametrize('arg', ['--banana', '--foo', '--version'])
+@pytest.mark.parametrize("arg", ["--banana", "--foo", "--version"])
 def test_invalid_args(libinput_debug_tool, arg):
     libinput_debug_tool.run_command_unrecognized_option([arg])
 
 
 def test_libinput_debug_events_multiple_devices(libinput_debug_events):
-    libinput_debug_events.run_command_success(['--device', '/dev/input/event0', '/dev/input/event1'])
+    libinput_debug_events.run_command_success(
+        ["--device", "/dev/input/event0", "/dev/input/event1"]
+    )
     # same event path multiple times? meh, your problem
-    libinput_debug_events.run_command_success(['--device', '/dev/input/event0', '/dev/input/event0'])
-    libinput_debug_events.run_command_success(['/dev/input/event0', '/dev/input/event1'])
+    libinput_debug_events.run_command_success(
+        ["--device", "/dev/input/event0", "/dev/input/event0"]
+    )
+    libinput_debug_events.run_command_success(
+        ["/dev/input/event0", "/dev/input/event1"]
+    )
 
 
 def test_libinput_debug_events_too_many_devices(libinput_debug_events):
     # Too many arguments just bails with the usage message
-    rc, stdout, stderr = libinput_debug_events.run_command(['/dev/input/event0'] * 61)
+    rc, stdout, stderr = libinput_debug_events.run_command(["/dev/input/event0"] * 61)
     assert rc == 2, (stdout, stderr)
 
 
-@pytest.mark.parametrize('arg', ['--quiet'])
+@pytest.mark.parametrize("arg", ["--quiet"])
 def test_libinput_debug_gui_invalid_arg(libinput_debug_gui, arg):
     libinput_debug_gui.run_command_unrecognized_option([arg])
 
 
 def test_libinput_debug_gui_verbose(libinput_debug_gui):
-    libinput_debug_gui.run_command_success(['--verbose'])
+    libinput_debug_gui.run_command_success(["--verbose"])
 
 
-@pytest.mark.parametrize('arg', ['--help', '--show-keycodes', '--with-libinput'])
+@pytest.mark.parametrize("arg", ["--help", "--show-keycodes", "--with-libinput"])
 def test_libinput_record_args(libinput_record, arg):
     libinput_record.run_command_success([arg])
 
 
 def test_libinput_record_multiple_arg(libinput_record):
     # this arg is deprecated and a noop
-    libinput_record.run_command_success(['--multiple'])
+    libinput_record.run_command_success(["--multiple"])
 
 
 @pytest.fixture
 def recording(tmp_path):
-    return str((tmp_path / 'record.out').resolve())
+    return str((tmp_path / "record.out").resolve())
 
 
 def test_libinput_record_all(libinput_record, recording):
-    libinput_record.run_command_success(['--all', '-o', recording])
-    libinput_record.run_command_success(['--all', recording])
+    libinput_record.run_command_success(["--all", "-o", recording])
+    libinput_record.run_command_success(["--all", recording])
 
 
 def test_libinput_record_outfile(libinput_record, recording):
-    libinput_record.run_command_success(['-o', recording])
-    libinput_record.run_command_success(['--output-file', recording])
-    libinput_record.run_command_success(['--output-file={}'.format(recording)])
+    libinput_record.run_command_success(["-o", recording])
+    libinput_record.run_command_success(["--output-file", recording])
+    libinput_record.run_command_success(["--output-file={}".format(recording)])
 
 
 def test_libinput_record_single(libinput_record, recording):
-    libinput_record.run_command_success(['/dev/input/event0'])
-    libinput_record.run_command_success(['-o', recording, '/dev/input/event0'])
-    libinput_record.run_command_success(['/dev/input/event0', recording])
-    libinput_record.run_command_success([recording, '/dev/input/event0'])
+    libinput_record.run_command_success(["/dev/input/event0"])
+    libinput_record.run_command_success(["-o", recording, "/dev/input/event0"])
+    libinput_record.run_command_success(["/dev/input/event0", recording])
+    libinput_record.run_command_success([recording, "/dev/input/event0"])
 
 
 def test_libinput_record_multiple(libinput_record, recording):
-    libinput_record.run_command_success(['-o', recording, '/dev/input/event0', '/dev/input/event1'])
-    libinput_record.run_command_success([recording, '/dev/input/event0', '/dev/input/event1'])
-    libinput_record.run_command_success(['/dev/input/event0', '/dev/input/event1', recording])
+    libinput_record.run_command_success(
+        ["-o", recording, "/dev/input/event0", "/dev/input/event1"]
+    )
+    libinput_record.run_command_success(
+        [recording, "/dev/input/event0", "/dev/input/event1"]
+    )
+    libinput_record.run_command_success(
+        ["/dev/input/event0", "/dev/input/event1", recording]
+    )
 
 
 def test_libinput_record_autorestart(libinput_record, recording):
-    libinput_record.run_command_invalid(['--autorestart'])
-    libinput_record.run_command_invalid(['--autorestart=2'])
-    libinput_record.run_command_success(['-o', recording, '--autorestart=2'])
+    libinput_record.run_command_invalid(["--autorestart"])
+    libinput_record.run_command_invalid(["--autorestart=2"])
+    libinput_record.run_command_success(["-o", recording, "--autorestart=2"])
 
 
 def main():
-    args = ['-m', 'pytest']
+    args = ["-m", "pytest"]
     try:
         import xdist  # noqa
-        args += ['-n', 'auto']
+
+        args += ["-n", "auto"]
     except ImportError:
-        logger.info('python-xdist missing, this test will be slow')
+        logger.info("python-xdist missing, this test will be slow")
         pass
 
-    args += ['@MESON_BUILD_ROOT@']
+    args += ["@MESON_BUILD_ROOT@"]
 
-    os.environ['LIBINPUT_RUNNING_TEST_SUITE'] = '1'
+    os.environ["LIBINPUT_RUNNING_TEST_SUITE"] = "1"
 
     return subprocess.run([sys.executable] + args).returncode
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     raise SystemExit(main())