Let's enforce a consistent (and verifiable) style everywhere.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
/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)
#
/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 %}
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)
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
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]
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)
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:
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
# 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
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:
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:
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)
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)
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):
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]
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
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):
# 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
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
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
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
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
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)
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)
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:
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
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))
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
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):
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):
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
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():
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)
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:
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))
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)
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()
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)
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
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))
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
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
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):
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):
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))
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())
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)
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()
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))
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)
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
# 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()
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.
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.
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)
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()
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
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
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
@up.setter
def up(self, up):
- assert(up > self.down)
+ assert up > self.down
self._up = up
@property
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))
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:
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):
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):
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
# 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)
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():
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))
class TestYaml(unittest.TestCase):
- filename = ''
+ filename = ""
@classmethod
def setUpClass(cls):
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
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
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
# 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:
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:
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:
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:
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:
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:
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:
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:
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:
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
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)
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
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:
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))
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
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
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.
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:
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
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()
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)
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:
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):
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)
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
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())