1a46c0ec8Sopenharmony_ci#!/usr/bin/env python3 2a46c0ec8Sopenharmony_ci# vim: set expandtab shiftwidth=4: 3a46c0ec8Sopenharmony_ci# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ 4a46c0ec8Sopenharmony_ci# 5a46c0ec8Sopenharmony_ci# Copyright © 2020 Red Hat, Inc. 6a46c0ec8Sopenharmony_ci# 7a46c0ec8Sopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a 8a46c0ec8Sopenharmony_ci# copy of this software and associated documentation files (the "Software"), 9a46c0ec8Sopenharmony_ci# to deal in the Software without restriction, including without limitation 10a46c0ec8Sopenharmony_ci# the rights to use, copy, modify, merge, publish, distribute, sublicense, 11a46c0ec8Sopenharmony_ci# and/or sell copies of the Software, and to permit persons to whom the 12a46c0ec8Sopenharmony_ci# Software is furnished to do so, subject to the following conditions: 13a46c0ec8Sopenharmony_ci# 14a46c0ec8Sopenharmony_ci# The above copyright notice and this permission notice (including the next 15a46c0ec8Sopenharmony_ci# paragraph) shall be included in all copies or substantial portions of the 16a46c0ec8Sopenharmony_ci# Software. 17a46c0ec8Sopenharmony_ci# 18a46c0ec8Sopenharmony_ci# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19a46c0ec8Sopenharmony_ci# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20a46c0ec8Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21a46c0ec8Sopenharmony_ci# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22a46c0ec8Sopenharmony_ci# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23a46c0ec8Sopenharmony_ci# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24a46c0ec8Sopenharmony_ci# DEALINGS IN THE SOFTWARE. 25a46c0ec8Sopenharmony_ci 26a46c0ec8Sopenharmony_ci 27a46c0ec8Sopenharmony_ciimport sys 28a46c0ec8Sopenharmony_ciimport argparse 29a46c0ec8Sopenharmony_ci 30a46c0ec8Sopenharmony_citry: 31a46c0ec8Sopenharmony_ci import libevdev 32a46c0ec8Sopenharmony_ci import pyudev 33a46c0ec8Sopenharmony_ciexcept ModuleNotFoundError as e: 34a46c0ec8Sopenharmony_ci print("Error: {}".format(str(e)), file=sys.stderr) 35a46c0ec8Sopenharmony_ci print( 36a46c0ec8Sopenharmony_ci "One or more python modules are missing. Please install those " 37a46c0ec8Sopenharmony_ci "modules and re-run this tool." 38a46c0ec8Sopenharmony_ci ) 39a46c0ec8Sopenharmony_ci sys.exit(1) 40a46c0ec8Sopenharmony_ci 41a46c0ec8Sopenharmony_ci 42a46c0ec8Sopenharmony_ciclass DeviceError(Exception): 43a46c0ec8Sopenharmony_ci pass 44a46c0ec8Sopenharmony_ci 45a46c0ec8Sopenharmony_ci 46a46c0ec8Sopenharmony_ciclass Point: 47a46c0ec8Sopenharmony_ci def __init__(self, x=None, y=None): 48a46c0ec8Sopenharmony_ci self.x = x 49a46c0ec8Sopenharmony_ci self.y = y 50a46c0ec8Sopenharmony_ci 51a46c0ec8Sopenharmony_ci 52a46c0ec8Sopenharmony_ciclass Touchpad(object): 53a46c0ec8Sopenharmony_ci def __init__(self, evdev): 54a46c0ec8Sopenharmony_ci x = evdev.absinfo[libevdev.EV_ABS.ABS_X] 55a46c0ec8Sopenharmony_ci y = evdev.absinfo[libevdev.EV_ABS.ABS_Y] 56a46c0ec8Sopenharmony_ci if not x or not y: 57a46c0ec8Sopenharmony_ci raise DeviceError("Device does not have an x or axis") 58a46c0ec8Sopenharmony_ci 59a46c0ec8Sopenharmony_ci if not x.resolution or not y.resolution: 60a46c0ec8Sopenharmony_ci print("Device does not have resolutions.", file=sys.stderr) 61a46c0ec8Sopenharmony_ci x.resolution = 1 62a46c0ec8Sopenharmony_ci y.resolution = 1 63a46c0ec8Sopenharmony_ci 64a46c0ec8Sopenharmony_ci self.xrange = x.maximum - x.minimum 65a46c0ec8Sopenharmony_ci self.yrange = y.maximum - y.minimum 66a46c0ec8Sopenharmony_ci self.width = self.xrange / x.resolution 67a46c0ec8Sopenharmony_ci self.height = self.yrange / y.resolution 68a46c0ec8Sopenharmony_ci 69a46c0ec8Sopenharmony_ci self._x = x 70a46c0ec8Sopenharmony_ci self._y = y 71a46c0ec8Sopenharmony_ci 72a46c0ec8Sopenharmony_ci # We try to make the touchpad at least look proportional. The 73a46c0ec8Sopenharmony_ci # terminal character space is (guesswork) ca 2.3 times as high as 74a46c0ec8Sopenharmony_ci # wide. 75a46c0ec8Sopenharmony_ci self.columns = 30 76a46c0ec8Sopenharmony_ci self.rows = int( 77a46c0ec8Sopenharmony_ci self.columns 78a46c0ec8Sopenharmony_ci * (self.yrange // y.resolution) 79a46c0ec8Sopenharmony_ci // (self.xrange // x.resolution) 80a46c0ec8Sopenharmony_ci / 2.3 81a46c0ec8Sopenharmony_ci ) 82a46c0ec8Sopenharmony_ci self.pos = Point(0, 0) 83a46c0ec8Sopenharmony_ci self.min = Point() 84a46c0ec8Sopenharmony_ci self.max = Point() 85a46c0ec8Sopenharmony_ci 86a46c0ec8Sopenharmony_ci @property 87a46c0ec8Sopenharmony_ci def x(self): 88a46c0ec8Sopenharmony_ci return self._x 89a46c0ec8Sopenharmony_ci 90a46c0ec8Sopenharmony_ci @property 91a46c0ec8Sopenharmony_ci def y(self): 92a46c0ec8Sopenharmony_ci return self._y 93a46c0ec8Sopenharmony_ci 94a46c0ec8Sopenharmony_ci @x.setter 95a46c0ec8Sopenharmony_ci def x(self, x): 96a46c0ec8Sopenharmony_ci self._x.minimum = min(self.x.minimum, x) 97a46c0ec8Sopenharmony_ci self._x.maximum = max(self.x.maximum, x) 98a46c0ec8Sopenharmony_ci self.min.x = min(x, self.min.x or 0xFFFFFFFF) 99a46c0ec8Sopenharmony_ci self.max.x = max(x, self.max.x or -0xFFFFFFFF) 100a46c0ec8Sopenharmony_ci # we calculate the position based on the original range. 101a46c0ec8Sopenharmony_ci # this means on devices with a narrower range than advertised, not 102a46c0ec8Sopenharmony_ci # all corners may be reachable in the touchpad drawing. 103a46c0ec8Sopenharmony_ci self.pos.x = min(0.99, (x - self._x.minimum) / self.xrange) 104a46c0ec8Sopenharmony_ci 105a46c0ec8Sopenharmony_ci @y.setter 106a46c0ec8Sopenharmony_ci def y(self, y): 107a46c0ec8Sopenharmony_ci self._y.minimum = min(self.y.minimum, y) 108a46c0ec8Sopenharmony_ci self._y.maximum = max(self.y.maximum, y) 109a46c0ec8Sopenharmony_ci self.min.y = min(y, self.min.y or 0xFFFFFFFF) 110a46c0ec8Sopenharmony_ci self.max.y = max(y, self.max.y or -0xFFFFFFFF) 111a46c0ec8Sopenharmony_ci # we calculate the position based on the original range. 112a46c0ec8Sopenharmony_ci # this means on devices with a narrower range than advertised, not 113a46c0ec8Sopenharmony_ci # all corners may be reachable in the touchpad drawing. 114a46c0ec8Sopenharmony_ci self.pos.y = min(0.99, (y - self._y.minimum) / self.yrange) 115a46c0ec8Sopenharmony_ci 116a46c0ec8Sopenharmony_ci def update_from_data(self): 117a46c0ec8Sopenharmony_ci if None in [self.min.x, self.min.y, self.max.x, self.max.y]: 118a46c0ec8Sopenharmony_ci raise DeviceError("Insufficient data to continue") 119a46c0ec8Sopenharmony_ci self._x.minimum = self.min.x 120a46c0ec8Sopenharmony_ci self._x.maximum = self.max.x 121a46c0ec8Sopenharmony_ci self._y.minimum = self.min.y 122a46c0ec8Sopenharmony_ci self._y.maximum = self.max.y 123a46c0ec8Sopenharmony_ci 124a46c0ec8Sopenharmony_ci def draw(self): 125a46c0ec8Sopenharmony_ci print( 126a46c0ec8Sopenharmony_ci "Detected axis range: x [{:4d}..{:4d}], y [{:4d}..{:4d}]".format( 127a46c0ec8Sopenharmony_ci self.min.x if self.min.x is not None else 0, 128a46c0ec8Sopenharmony_ci self.max.x if self.max.x is not None else 0, 129a46c0ec8Sopenharmony_ci self.min.y if self.min.y is not None else 0, 130a46c0ec8Sopenharmony_ci self.max.y if self.max.y is not None else 0, 131a46c0ec8Sopenharmony_ci ) 132a46c0ec8Sopenharmony_ci ) 133a46c0ec8Sopenharmony_ci 134a46c0ec8Sopenharmony_ci print() 135a46c0ec8Sopenharmony_ci print("Move one finger along all edges of the touchpad".center(self.columns)) 136a46c0ec8Sopenharmony_ci print("until the detected axis range stops changing.".center(self.columns)) 137a46c0ec8Sopenharmony_ci 138a46c0ec8Sopenharmony_ci top = int(self.pos.y * self.rows) 139a46c0ec8Sopenharmony_ci 140a46c0ec8Sopenharmony_ci print("+{}+".format("".ljust(self.columns, "-"))) 141a46c0ec8Sopenharmony_ci for row in range(0, top): 142a46c0ec8Sopenharmony_ci print("|{}|".format("".ljust(self.columns))) 143a46c0ec8Sopenharmony_ci 144a46c0ec8Sopenharmony_ci left = int(self.pos.x * self.columns) 145a46c0ec8Sopenharmony_ci right = max(0, self.columns - 1 - left) 146a46c0ec8Sopenharmony_ci print("|{}{}{}|".format("".ljust(left), "O", "".ljust(right))) 147a46c0ec8Sopenharmony_ci 148a46c0ec8Sopenharmony_ci for row in range(top + 1, self.rows): 149a46c0ec8Sopenharmony_ci print("|{}|".format("".ljust(self.columns))) 150a46c0ec8Sopenharmony_ci 151a46c0ec8Sopenharmony_ci print("+{}+".format("".ljust(self.columns, "-"))) 152a46c0ec8Sopenharmony_ci 153a46c0ec8Sopenharmony_ci print("Press Ctrl+C to stop".center(self.columns)) 154a46c0ec8Sopenharmony_ci 155a46c0ec8Sopenharmony_ci print("\033[{}A".format(self.rows + 8), flush=True) 156a46c0ec8Sopenharmony_ci 157a46c0ec8Sopenharmony_ci self.rows_printed = self.rows + 8 158a46c0ec8Sopenharmony_ci 159a46c0ec8Sopenharmony_ci def erase(self): 160a46c0ec8Sopenharmony_ci # Erase all previous lines so we're not left with rubbish 161a46c0ec8Sopenharmony_ci for row in range(self.rows_printed): 162a46c0ec8Sopenharmony_ci print("\033[K") 163a46c0ec8Sopenharmony_ci print("\033[{}A".format(self.rows_printed)) 164a46c0ec8Sopenharmony_ci 165a46c0ec8Sopenharmony_ci 166a46c0ec8Sopenharmony_cidef dimension(string): 167a46c0ec8Sopenharmony_ci try: 168a46c0ec8Sopenharmony_ci ts = string.split("x") 169a46c0ec8Sopenharmony_ci t = tuple([int(x) for x in ts]) 170a46c0ec8Sopenharmony_ci if len(t) == 2: 171a46c0ec8Sopenharmony_ci return t 172a46c0ec8Sopenharmony_ci except: # noqa 173a46c0ec8Sopenharmony_ci pass 174a46c0ec8Sopenharmony_ci 175a46c0ec8Sopenharmony_ci msg = "{} is not in format WxH".format(string) 176a46c0ec8Sopenharmony_ci raise argparse.ArgumentTypeError(msg) 177a46c0ec8Sopenharmony_ci 178a46c0ec8Sopenharmony_ci 179a46c0ec8Sopenharmony_cidef between(v1, v2, deviation): 180a46c0ec8Sopenharmony_ci return v1 - deviation < v2 < v1 + deviation 181a46c0ec8Sopenharmony_ci 182a46c0ec8Sopenharmony_ci 183a46c0ec8Sopenharmony_cidef dmi_modalias_match(modalias): 184a46c0ec8Sopenharmony_ci modalias = modalias.split(":") 185a46c0ec8Sopenharmony_ci dmi = {"svn": None, "pvr": None, "pn": None} 186a46c0ec8Sopenharmony_ci for m in modalias: 187a46c0ec8Sopenharmony_ci for key in dmi: 188a46c0ec8Sopenharmony_ci if m.startswith(key): 189a46c0ec8Sopenharmony_ci dmi[key] = m[len(key) :] 190a46c0ec8Sopenharmony_ci 191a46c0ec8Sopenharmony_ci # Based on the current 60-evdev.hwdb, Lenovo uses pvr and everyone else 192a46c0ec8Sopenharmony_ci # uses pn to provide a human-identifiable match 193a46c0ec8Sopenharmony_ci if dmi["svn"] == "LENOVO": 194a46c0ec8Sopenharmony_ci return "dmi:*svn{}:*pvr{}*".format(dmi["svn"], dmi["pvr"]) 195a46c0ec8Sopenharmony_ci else: 196a46c0ec8Sopenharmony_ci return "dmi:*svn{}:*pn{}*".format(dmi["svn"], dmi["pn"]) 197a46c0ec8Sopenharmony_ci 198a46c0ec8Sopenharmony_ci 199a46c0ec8Sopenharmony_cidef main(args): 200a46c0ec8Sopenharmony_ci parser = argparse.ArgumentParser(description="Measure the touchpad size") 201a46c0ec8Sopenharmony_ci parser.add_argument( 202a46c0ec8Sopenharmony_ci "size", 203a46c0ec8Sopenharmony_ci metavar="WxH", 204a46c0ec8Sopenharmony_ci type=dimension, 205a46c0ec8Sopenharmony_ci help="Touchpad size (width by height) in mm", 206a46c0ec8Sopenharmony_ci ) 207a46c0ec8Sopenharmony_ci parser.add_argument( 208a46c0ec8Sopenharmony_ci "path", 209a46c0ec8Sopenharmony_ci metavar="/dev/input/event0", 210a46c0ec8Sopenharmony_ci nargs="?", 211a46c0ec8Sopenharmony_ci type=str, 212a46c0ec8Sopenharmony_ci help="Path to device (optional)", 213a46c0ec8Sopenharmony_ci ) 214a46c0ec8Sopenharmony_ci context = pyudev.Context() 215a46c0ec8Sopenharmony_ci 216a46c0ec8Sopenharmony_ci args = parser.parse_args() 217a46c0ec8Sopenharmony_ci if not args.path: 218a46c0ec8Sopenharmony_ci for device in context.list_devices(subsystem="input"): 219a46c0ec8Sopenharmony_ci if device.get("ID_INPUT_TOUCHPAD", 0) and ( 220a46c0ec8Sopenharmony_ci device.device_node or "" 221a46c0ec8Sopenharmony_ci ).startswith("/dev/input/event"): 222a46c0ec8Sopenharmony_ci args.path = device.device_node 223a46c0ec8Sopenharmony_ci name = "unknown" 224a46c0ec8Sopenharmony_ci parent = device 225a46c0ec8Sopenharmony_ci while parent is not None: 226a46c0ec8Sopenharmony_ci n = parent.get("NAME", None) 227a46c0ec8Sopenharmony_ci if n: 228a46c0ec8Sopenharmony_ci name = n 229a46c0ec8Sopenharmony_ci break 230a46c0ec8Sopenharmony_ci parent = parent.parent 231a46c0ec8Sopenharmony_ci 232a46c0ec8Sopenharmony_ci print("Using {}: {}".format(name, device.device_node)) 233a46c0ec8Sopenharmony_ci break 234a46c0ec8Sopenharmony_ci else: 235a46c0ec8Sopenharmony_ci print("Unable to find a touchpad device.", file=sys.stderr) 236a46c0ec8Sopenharmony_ci return 1 237a46c0ec8Sopenharmony_ci 238a46c0ec8Sopenharmony_ci dev = pyudev.Devices.from_device_file(context, args.path) 239a46c0ec8Sopenharmony_ci overrides = [p for p in dev.properties if p.startswith("EVDEV_ABS")] 240a46c0ec8Sopenharmony_ci if overrides: 241a46c0ec8Sopenharmony_ci print() 242a46c0ec8Sopenharmony_ci print("********************************************************************") 243a46c0ec8Sopenharmony_ci print("WARNING: axis overrides already in place for this device:") 244a46c0ec8Sopenharmony_ci for prop in overrides: 245a46c0ec8Sopenharmony_ci print(" {}={}".format(prop, dev.properties[prop])) 246a46c0ec8Sopenharmony_ci print("The systemd hwdb already overrides the axis ranges and/or resolution.") 247a46c0ec8Sopenharmony_ci print("This tool is not needed unless you want to verify the axis overrides.") 248a46c0ec8Sopenharmony_ci print("********************************************************************") 249a46c0ec8Sopenharmony_ci print() 250a46c0ec8Sopenharmony_ci 251a46c0ec8Sopenharmony_ci try: 252a46c0ec8Sopenharmony_ci fd = open(args.path, "rb") 253a46c0ec8Sopenharmony_ci evdev = libevdev.Device(fd) 254a46c0ec8Sopenharmony_ci touchpad = Touchpad(evdev) 255a46c0ec8Sopenharmony_ci print( 256a46c0ec8Sopenharmony_ci "Kernel specified touchpad size: {:.1f}x{:.1f}mm".format( 257a46c0ec8Sopenharmony_ci touchpad.width, touchpad.height 258a46c0ec8Sopenharmony_ci ) 259a46c0ec8Sopenharmony_ci ) 260a46c0ec8Sopenharmony_ci print("User specified touchpad size: {:.1f}x{:.1f}mm".format(*args.size)) 261a46c0ec8Sopenharmony_ci 262a46c0ec8Sopenharmony_ci print() 263a46c0ec8Sopenharmony_ci print( 264a46c0ec8Sopenharmony_ci "Kernel axis range: x [{:4d}..{:4d}], y [{:4d}..{:4d}]".format( 265a46c0ec8Sopenharmony_ci touchpad.x.minimum, 266a46c0ec8Sopenharmony_ci touchpad.x.maximum, 267a46c0ec8Sopenharmony_ci touchpad.y.minimum, 268a46c0ec8Sopenharmony_ci touchpad.y.maximum, 269a46c0ec8Sopenharmony_ci ) 270a46c0ec8Sopenharmony_ci ) 271a46c0ec8Sopenharmony_ci 272a46c0ec8Sopenharmony_ci print("Put your finger on the touchpad to start\033[1A") 273a46c0ec8Sopenharmony_ci 274a46c0ec8Sopenharmony_ci try: 275a46c0ec8Sopenharmony_ci touchpad.draw() 276a46c0ec8Sopenharmony_ci while True: 277a46c0ec8Sopenharmony_ci for event in evdev.events(): 278a46c0ec8Sopenharmony_ci if event.matches(libevdev.EV_ABS.ABS_X): 279a46c0ec8Sopenharmony_ci touchpad.x = event.value 280a46c0ec8Sopenharmony_ci elif event.matches(libevdev.EV_ABS.ABS_Y): 281a46c0ec8Sopenharmony_ci touchpad.y = event.value 282a46c0ec8Sopenharmony_ci elif event.matches(libevdev.EV_SYN.SYN_REPORT): 283a46c0ec8Sopenharmony_ci touchpad.draw() 284a46c0ec8Sopenharmony_ci except KeyboardInterrupt: 285a46c0ec8Sopenharmony_ci touchpad.erase() 286a46c0ec8Sopenharmony_ci touchpad.update_from_data() 287a46c0ec8Sopenharmony_ci 288a46c0ec8Sopenharmony_ci print( 289a46c0ec8Sopenharmony_ci "Detected axis range: x [{:4d}..{:4d}], y [{:4d}..{:4d}]".format( 290a46c0ec8Sopenharmony_ci touchpad.x.minimum, 291a46c0ec8Sopenharmony_ci touchpad.x.maximum, 292a46c0ec8Sopenharmony_ci touchpad.y.minimum, 293a46c0ec8Sopenharmony_ci touchpad.y.maximum, 294a46c0ec8Sopenharmony_ci ) 295a46c0ec8Sopenharmony_ci ) 296a46c0ec8Sopenharmony_ci 297a46c0ec8Sopenharmony_ci touchpad.x.resolution = round( 298a46c0ec8Sopenharmony_ci (touchpad.x.maximum - touchpad.x.minimum) / args.size[0] 299a46c0ec8Sopenharmony_ci ) 300a46c0ec8Sopenharmony_ci touchpad.y.resolution = round( 301a46c0ec8Sopenharmony_ci (touchpad.y.maximum - touchpad.y.minimum) / args.size[1] 302a46c0ec8Sopenharmony_ci ) 303a46c0ec8Sopenharmony_ci 304a46c0ec8Sopenharmony_ci print( 305a46c0ec8Sopenharmony_ci "Resolutions calculated based on user-specified size: x {}, y {} units/mm".format( 306a46c0ec8Sopenharmony_ci touchpad.x.resolution, touchpad.y.resolution 307a46c0ec8Sopenharmony_ci ) 308a46c0ec8Sopenharmony_ci ) 309a46c0ec8Sopenharmony_ci 310a46c0ec8Sopenharmony_ci # If both x/y are within some acceptable deviation, we skip the axis 311a46c0ec8Sopenharmony_ci # overrides and only override the resolution 312a46c0ec8Sopenharmony_ci xorig = evdev.absinfo[libevdev.EV_ABS.ABS_X] 313a46c0ec8Sopenharmony_ci yorig = evdev.absinfo[libevdev.EV_ABS.ABS_Y] 314a46c0ec8Sopenharmony_ci deviation = 1.5 * touchpad.x.resolution # 1.5 mm rounding on each side 315a46c0ec8Sopenharmony_ci skip = between(xorig.minimum, touchpad.x.minimum, deviation) 316a46c0ec8Sopenharmony_ci skip = skip and between(xorig.maximum, touchpad.x.maximum, deviation) 317a46c0ec8Sopenharmony_ci deviation = 1.5 * touchpad.y.resolution # 1.5 mm rounding on each side 318a46c0ec8Sopenharmony_ci skip = skip and between(yorig.minimum, touchpad.y.minimum, deviation) 319a46c0ec8Sopenharmony_ci skip = skip and between(yorig.maximum, touchpad.y.maximum, deviation) 320a46c0ec8Sopenharmony_ci 321a46c0ec8Sopenharmony_ci if skip: 322a46c0ec8Sopenharmony_ci print() 323a46c0ec8Sopenharmony_ci print( 324a46c0ec8Sopenharmony_ci "Note: Axis ranges within acceptable deviation, skipping min/max override" 325a46c0ec8Sopenharmony_ci ) 326a46c0ec8Sopenharmony_ci print() 327a46c0ec8Sopenharmony_ci 328a46c0ec8Sopenharmony_ci print() 329a46c0ec8Sopenharmony_ci print("Suggested hwdb entry:") 330a46c0ec8Sopenharmony_ci 331a46c0ec8Sopenharmony_ci use_dmi = evdev.id["bustype"] not in [0x03, 0x05] # USB, Bluetooth 332a46c0ec8Sopenharmony_ci if use_dmi: 333a46c0ec8Sopenharmony_ci modalias = open("/sys/class/dmi/id/modalias").read().strip() 334a46c0ec8Sopenharmony_ci print( 335a46c0ec8Sopenharmony_ci "Note: the dmi modalias match is a guess based on your machine's modalias:" 336a46c0ec8Sopenharmony_ci ) 337a46c0ec8Sopenharmony_ci print(" ", modalias) 338a46c0ec8Sopenharmony_ci print( 339a46c0ec8Sopenharmony_ci "Please verify that this is the most sensible match and adjust if necessary." 340a46c0ec8Sopenharmony_ci ) 341a46c0ec8Sopenharmony_ci 342a46c0ec8Sopenharmony_ci print("-8<--------------------------") 343a46c0ec8Sopenharmony_ci print("# Laptop model description (e.g. Lenovo X1 Carbon 5th)") 344a46c0ec8Sopenharmony_ci if use_dmi: 345a46c0ec8Sopenharmony_ci print("evdev:name:{}:{}*".format(evdev.name, dmi_modalias_match(modalias))) 346a46c0ec8Sopenharmony_ci else: 347a46c0ec8Sopenharmony_ci print( 348a46c0ec8Sopenharmony_ci "evdev:input:b{:04X}v{:04X}p{:04X}*".format( 349a46c0ec8Sopenharmony_ci evdev.id["bustype"], evdev.id["vendor"], evdev.id["product"] 350a46c0ec8Sopenharmony_ci ) 351a46c0ec8Sopenharmony_ci ) 352a46c0ec8Sopenharmony_ci print( 353a46c0ec8Sopenharmony_ci " EVDEV_ABS_00={}:{}:{}".format( 354a46c0ec8Sopenharmony_ci touchpad.x.minimum if not skip else "", 355a46c0ec8Sopenharmony_ci touchpad.x.maximum if not skip else "", 356a46c0ec8Sopenharmony_ci touchpad.x.resolution, 357a46c0ec8Sopenharmony_ci ) 358a46c0ec8Sopenharmony_ci ) 359a46c0ec8Sopenharmony_ci print( 360a46c0ec8Sopenharmony_ci " EVDEV_ABS_01={}:{}:{}".format( 361a46c0ec8Sopenharmony_ci touchpad.y.minimum if not skip else "", 362a46c0ec8Sopenharmony_ci touchpad.y.maximum if not skip else "", 363a46c0ec8Sopenharmony_ci touchpad.y.resolution, 364a46c0ec8Sopenharmony_ci ) 365a46c0ec8Sopenharmony_ci ) 366a46c0ec8Sopenharmony_ci if evdev.absinfo[libevdev.EV_ABS.ABS_MT_POSITION_X]: 367a46c0ec8Sopenharmony_ci print( 368a46c0ec8Sopenharmony_ci " EVDEV_ABS_35={}:{}:{}".format( 369a46c0ec8Sopenharmony_ci touchpad.x.minimum if not skip else "", 370a46c0ec8Sopenharmony_ci touchpad.x.maximum if not skip else "", 371a46c0ec8Sopenharmony_ci touchpad.x.resolution, 372a46c0ec8Sopenharmony_ci ) 373a46c0ec8Sopenharmony_ci ) 374a46c0ec8Sopenharmony_ci print( 375a46c0ec8Sopenharmony_ci " EVDEV_ABS_36={}:{}:{}".format( 376a46c0ec8Sopenharmony_ci touchpad.y.minimum if not skip else "", 377a46c0ec8Sopenharmony_ci touchpad.y.maximum if not skip else "", 378a46c0ec8Sopenharmony_ci touchpad.y.resolution, 379a46c0ec8Sopenharmony_ci ) 380a46c0ec8Sopenharmony_ci ) 381a46c0ec8Sopenharmony_ci print("-8<--------------------------") 382a46c0ec8Sopenharmony_ci print( 383a46c0ec8Sopenharmony_ci "Instructions on what to do with this snippet are in /usr/lib/udev/hwdb.d/60-evdev.hwdb" 384a46c0ec8Sopenharmony_ci ) 385a46c0ec8Sopenharmony_ci except DeviceError as e: 386a46c0ec8Sopenharmony_ci print("Error: {}".format(e), file=sys.stderr) 387a46c0ec8Sopenharmony_ci return 1 388a46c0ec8Sopenharmony_ci except PermissionError: 389a46c0ec8Sopenharmony_ci print("Unable to open device. Please run me as root", file=sys.stderr) 390a46c0ec8Sopenharmony_ci return 1 391a46c0ec8Sopenharmony_ci 392a46c0ec8Sopenharmony_ci return 0 393a46c0ec8Sopenharmony_ci 394a46c0ec8Sopenharmony_ci 395a46c0ec8Sopenharmony_ciif __name__ == "__main__": 396a46c0ec8Sopenharmony_ci sys.exit(main(sys.argv)) 397