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 © 2017 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 subprocess 29a46c0ec8Sopenharmony_ciimport argparse 30a46c0ec8Sopenharmony_ci 31a46c0ec8Sopenharmony_citry: 32a46c0ec8Sopenharmony_ci import libevdev 33a46c0ec8Sopenharmony_ci import pyudev 34a46c0ec8Sopenharmony_ciexcept ModuleNotFoundError as e: 35a46c0ec8Sopenharmony_ci print("Error: {}".format(str(e)), file=sys.stderr) 36a46c0ec8Sopenharmony_ci print( 37a46c0ec8Sopenharmony_ci "One or more python modules are missing. Please install those " 38a46c0ec8Sopenharmony_ci "modules and re-run this tool." 39a46c0ec8Sopenharmony_ci ) 40a46c0ec8Sopenharmony_ci sys.exit(1) 41a46c0ec8Sopenharmony_ci 42a46c0ec8Sopenharmony_ci 43a46c0ec8Sopenharmony_ciclass Range(object): 44a46c0ec8Sopenharmony_ci """Class to keep a min/max of a value around""" 45a46c0ec8Sopenharmony_ci 46a46c0ec8Sopenharmony_ci def __init__(self): 47a46c0ec8Sopenharmony_ci self.min = float("inf") 48a46c0ec8Sopenharmony_ci self.max = float("-inf") 49a46c0ec8Sopenharmony_ci 50a46c0ec8Sopenharmony_ci def update(self, value): 51a46c0ec8Sopenharmony_ci self.min = min(self.min, value) 52a46c0ec8Sopenharmony_ci self.max = max(self.max, value) 53a46c0ec8Sopenharmony_ci 54a46c0ec8Sopenharmony_ci 55a46c0ec8Sopenharmony_ciclass Touch(object): 56a46c0ec8Sopenharmony_ci """A single data point of a sequence (i.e. one event frame)""" 57a46c0ec8Sopenharmony_ci 58a46c0ec8Sopenharmony_ci def __init__(self, major=None, minor=None, orientation=None): 59a46c0ec8Sopenharmony_ci self._major = major 60a46c0ec8Sopenharmony_ci self._minor = minor 61a46c0ec8Sopenharmony_ci self._orientation = orientation 62a46c0ec8Sopenharmony_ci self.dirty = False 63a46c0ec8Sopenharmony_ci 64a46c0ec8Sopenharmony_ci @property 65a46c0ec8Sopenharmony_ci def major(self): 66a46c0ec8Sopenharmony_ci return self._major 67a46c0ec8Sopenharmony_ci 68a46c0ec8Sopenharmony_ci @major.setter 69a46c0ec8Sopenharmony_ci def major(self, major): 70a46c0ec8Sopenharmony_ci self._major = major 71a46c0ec8Sopenharmony_ci self.dirty = True 72a46c0ec8Sopenharmony_ci 73a46c0ec8Sopenharmony_ci @property 74a46c0ec8Sopenharmony_ci def minor(self): 75a46c0ec8Sopenharmony_ci return self._minor 76a46c0ec8Sopenharmony_ci 77a46c0ec8Sopenharmony_ci @minor.setter 78a46c0ec8Sopenharmony_ci def minor(self, minor): 79a46c0ec8Sopenharmony_ci self._minor = minor 80a46c0ec8Sopenharmony_ci self.dirty = True 81a46c0ec8Sopenharmony_ci 82a46c0ec8Sopenharmony_ci @property 83a46c0ec8Sopenharmony_ci def orientation(self): 84a46c0ec8Sopenharmony_ci return self._orientation 85a46c0ec8Sopenharmony_ci 86a46c0ec8Sopenharmony_ci @orientation.setter 87a46c0ec8Sopenharmony_ci def orientation(self, orientation): 88a46c0ec8Sopenharmony_ci self._orientation = orientation 89a46c0ec8Sopenharmony_ci self.dirty = True 90a46c0ec8Sopenharmony_ci 91a46c0ec8Sopenharmony_ci def __str__(self): 92a46c0ec8Sopenharmony_ci s = "Touch: major {:3d}".format(self.major) 93a46c0ec8Sopenharmony_ci if self.minor is not None: 94a46c0ec8Sopenharmony_ci s += ", minor {:3d}".format(self.minor) 95a46c0ec8Sopenharmony_ci if self.orientation is not None: 96a46c0ec8Sopenharmony_ci s += ", orientation {:+3d}".format(self.orientation) 97a46c0ec8Sopenharmony_ci return s 98a46c0ec8Sopenharmony_ci 99a46c0ec8Sopenharmony_ci 100a46c0ec8Sopenharmony_ciclass TouchSequence(object): 101a46c0ec8Sopenharmony_ci """A touch sequence from beginning to end""" 102a46c0ec8Sopenharmony_ci 103a46c0ec8Sopenharmony_ci def __init__(self, device, tracking_id): 104a46c0ec8Sopenharmony_ci self.device = device 105a46c0ec8Sopenharmony_ci self.tracking_id = tracking_id 106a46c0ec8Sopenharmony_ci self.points = [] 107a46c0ec8Sopenharmony_ci 108a46c0ec8Sopenharmony_ci self.is_active = True 109a46c0ec8Sopenharmony_ci 110a46c0ec8Sopenharmony_ci self.is_down = False 111a46c0ec8Sopenharmony_ci self.was_down = False 112a46c0ec8Sopenharmony_ci self.is_palm = False 113a46c0ec8Sopenharmony_ci self.was_palm = False 114a46c0ec8Sopenharmony_ci self.is_thumb = False 115a46c0ec8Sopenharmony_ci self.was_thumb = False 116a46c0ec8Sopenharmony_ci 117a46c0ec8Sopenharmony_ci self.major_range = Range() 118a46c0ec8Sopenharmony_ci self.minor_range = Range() 119a46c0ec8Sopenharmony_ci 120a46c0ec8Sopenharmony_ci def append(self, touch): 121a46c0ec8Sopenharmony_ci """Add a Touch to the sequence""" 122a46c0ec8Sopenharmony_ci self.points.append(touch) 123a46c0ec8Sopenharmony_ci self.major_range.update(touch.major) 124a46c0ec8Sopenharmony_ci self.minor_range.update(touch.minor) 125a46c0ec8Sopenharmony_ci 126a46c0ec8Sopenharmony_ci if touch.major < self.device.up or touch.minor < self.device.up: 127a46c0ec8Sopenharmony_ci self.is_down = False 128a46c0ec8Sopenharmony_ci elif touch.major > self.device.down or touch.minor > self.device.down: 129a46c0ec8Sopenharmony_ci self.is_down = True 130a46c0ec8Sopenharmony_ci self.was_down = True 131a46c0ec8Sopenharmony_ci 132a46c0ec8Sopenharmony_ci self.is_palm = touch.major > self.device.palm 133a46c0ec8Sopenharmony_ci if self.is_palm: 134a46c0ec8Sopenharmony_ci self.was_palm = True 135a46c0ec8Sopenharmony_ci 136a46c0ec8Sopenharmony_ci self.is_thumb = self.device.thumb != 0 and touch.major > self.device.thumb 137a46c0ec8Sopenharmony_ci if self.is_thumb: 138a46c0ec8Sopenharmony_ci self.was_thumb = True 139a46c0ec8Sopenharmony_ci 140a46c0ec8Sopenharmony_ci def finalize(self): 141a46c0ec8Sopenharmony_ci """Mark the TouchSequence as complete (finger is up)""" 142a46c0ec8Sopenharmony_ci self.is_active = False 143a46c0ec8Sopenharmony_ci 144a46c0ec8Sopenharmony_ci def __str__(self): 145a46c0ec8Sopenharmony_ci return self._str_state() if self.is_active else self._str_summary() 146a46c0ec8Sopenharmony_ci 147a46c0ec8Sopenharmony_ci def _str_summary(self): 148a46c0ec8Sopenharmony_ci if not self.points: 149a46c0ec8Sopenharmony_ci return "{:78s}".format("Sequence: no major/minor values recorded") 150a46c0ec8Sopenharmony_ci 151a46c0ec8Sopenharmony_ci s = "Sequence: major: [{:3d}..{:3d}] ".format( 152a46c0ec8Sopenharmony_ci self.major_range.min, self.major_range.max 153a46c0ec8Sopenharmony_ci ) 154a46c0ec8Sopenharmony_ci if self.device.has_minor: 155a46c0ec8Sopenharmony_ci s += "minor: [{:3d}..{:3d}] ".format( 156a46c0ec8Sopenharmony_ci self.minor_range.min, self.minor_range.max 157a46c0ec8Sopenharmony_ci ) 158a46c0ec8Sopenharmony_ci if self.was_down: 159a46c0ec8Sopenharmony_ci s += " down" 160a46c0ec8Sopenharmony_ci if self.was_palm: 161a46c0ec8Sopenharmony_ci s += " palm" 162a46c0ec8Sopenharmony_ci if self.was_thumb: 163a46c0ec8Sopenharmony_ci s += " thumb" 164a46c0ec8Sopenharmony_ci 165a46c0ec8Sopenharmony_ci return s 166a46c0ec8Sopenharmony_ci 167a46c0ec8Sopenharmony_ci def _str_state(self): 168a46c0ec8Sopenharmony_ci touch = self.points[-1] 169a46c0ec8Sopenharmony_ci s = "{}, tags: {} {} {}".format( 170a46c0ec8Sopenharmony_ci touch, 171a46c0ec8Sopenharmony_ci "down" if self.is_down else " ", 172a46c0ec8Sopenharmony_ci "palm" if self.is_palm else " ", 173a46c0ec8Sopenharmony_ci "thumb" if self.is_thumb else " ", 174a46c0ec8Sopenharmony_ci ) 175a46c0ec8Sopenharmony_ci return s 176a46c0ec8Sopenharmony_ci 177a46c0ec8Sopenharmony_ci 178a46c0ec8Sopenharmony_ciclass InvalidDeviceError(Exception): 179a46c0ec8Sopenharmony_ci pass 180a46c0ec8Sopenharmony_ci 181a46c0ec8Sopenharmony_ci 182a46c0ec8Sopenharmony_ciclass Device(libevdev.Device): 183a46c0ec8Sopenharmony_ci def __init__(self, path): 184a46c0ec8Sopenharmony_ci if path is None: 185a46c0ec8Sopenharmony_ci self.path = self.find_touch_device() 186a46c0ec8Sopenharmony_ci else: 187a46c0ec8Sopenharmony_ci self.path = path 188a46c0ec8Sopenharmony_ci 189a46c0ec8Sopenharmony_ci fd = open(self.path, "rb") 190a46c0ec8Sopenharmony_ci super().__init__(fd) 191a46c0ec8Sopenharmony_ci 192a46c0ec8Sopenharmony_ci print("Using {}: {}\n".format(self.name, self.path)) 193a46c0ec8Sopenharmony_ci 194a46c0ec8Sopenharmony_ci if not self.has(libevdev.EV_ABS.ABS_MT_TOUCH_MAJOR): 195a46c0ec8Sopenharmony_ci raise InvalidDeviceError("Device does not have ABS_MT_TOUCH_MAJOR") 196a46c0ec8Sopenharmony_ci 197a46c0ec8Sopenharmony_ci self.has_minor = self.has(libevdev.EV_ABS.ABS_MT_TOUCH_MINOR) 198a46c0ec8Sopenharmony_ci self.has_orientation = self.has(libevdev.EV_ABS.ABS_MT_ORIENTATION) 199a46c0ec8Sopenharmony_ci 200a46c0ec8Sopenharmony_ci self.up = 0 201a46c0ec8Sopenharmony_ci self.down = 0 202a46c0ec8Sopenharmony_ci self.palm = 0 203a46c0ec8Sopenharmony_ci self.thumb = 0 204a46c0ec8Sopenharmony_ci 205a46c0ec8Sopenharmony_ci self._init_thresholds_from_quirks() 206a46c0ec8Sopenharmony_ci self.sequences = [] 207a46c0ec8Sopenharmony_ci self.touch = Touch(0, 0) 208a46c0ec8Sopenharmony_ci 209a46c0ec8Sopenharmony_ci def find_touch_device(self): 210a46c0ec8Sopenharmony_ci context = pyudev.Context() 211a46c0ec8Sopenharmony_ci for device in context.list_devices(subsystem="input"): 212a46c0ec8Sopenharmony_ci if not device.get("ID_INPUT_TOUCHPAD", 0) and not device.get( 213a46c0ec8Sopenharmony_ci "ID_INPUT_TOUCHSCREEN", 0 214a46c0ec8Sopenharmony_ci ): 215a46c0ec8Sopenharmony_ci continue 216a46c0ec8Sopenharmony_ci 217a46c0ec8Sopenharmony_ci if not device.device_node or not device.device_node.startswith( 218a46c0ec8Sopenharmony_ci "/dev/input/event" 219a46c0ec8Sopenharmony_ci ): 220a46c0ec8Sopenharmony_ci continue 221a46c0ec8Sopenharmony_ci 222a46c0ec8Sopenharmony_ci return device.device_node 223a46c0ec8Sopenharmony_ci 224a46c0ec8Sopenharmony_ci print("Unable to find a touch device.", file=sys.stderr) 225a46c0ec8Sopenharmony_ci sys.exit(1) 226a46c0ec8Sopenharmony_ci 227a46c0ec8Sopenharmony_ci def _init_thresholds_from_quirks(self): 228a46c0ec8Sopenharmony_ci command = ["libinput", "quirks", "list", self.path] 229a46c0ec8Sopenharmony_ci cmd = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 230a46c0ec8Sopenharmony_ci if cmd.returncode != 0: 231a46c0ec8Sopenharmony_ci print( 232a46c0ec8Sopenharmony_ci "Error querying quirks: {}".format(cmd.stderr.decode("utf-8")), 233a46c0ec8Sopenharmony_ci file=sys.stderr, 234a46c0ec8Sopenharmony_ci ) 235a46c0ec8Sopenharmony_ci return 236a46c0ec8Sopenharmony_ci 237a46c0ec8Sopenharmony_ci stdout = cmd.stdout.decode("utf-8") 238a46c0ec8Sopenharmony_ci quirks = [q.split("=") for q in stdout.split("\n")] 239a46c0ec8Sopenharmony_ci 240a46c0ec8Sopenharmony_ci for q in quirks: 241a46c0ec8Sopenharmony_ci if q[0] == "AttrPalmSizeThreshold": 242a46c0ec8Sopenharmony_ci self.palm = int(q[1]) 243a46c0ec8Sopenharmony_ci elif q[0] == "AttrTouchSizeRange": 244a46c0ec8Sopenharmony_ci self.down, self.up = colon_tuple(q[1]) 245a46c0ec8Sopenharmony_ci elif q[0] == "AttrThumbSizeThreshold": 246a46c0ec8Sopenharmony_ci self.thumb = int(q[1]) 247a46c0ec8Sopenharmony_ci 248a46c0ec8Sopenharmony_ci def start_new_sequence(self, tracking_id): 249a46c0ec8Sopenharmony_ci self.sequences.append(TouchSequence(self, tracking_id)) 250a46c0ec8Sopenharmony_ci 251a46c0ec8Sopenharmony_ci def current_sequence(self): 252a46c0ec8Sopenharmony_ci return self.sequences[-1] 253a46c0ec8Sopenharmony_ci 254a46c0ec8Sopenharmony_ci def handle_key(self, event): 255a46c0ec8Sopenharmony_ci tapcodes = [ 256a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_DOUBLETAP, 257a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_TRIPLETAP, 258a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_QUADTAP, 259a46c0ec8Sopenharmony_ci libevdev.EV_KEY.BTN_TOOL_QUINTTAP, 260a46c0ec8Sopenharmony_ci ] 261a46c0ec8Sopenharmony_ci if event.code in tapcodes and event.value > 0: 262a46c0ec8Sopenharmony_ci print( 263a46c0ec8Sopenharmony_ci "\rThis tool cannot handle multiple fingers, " "output will be invalid", 264a46c0ec8Sopenharmony_ci file=sys.stderr, 265a46c0ec8Sopenharmony_ci ) 266a46c0ec8Sopenharmony_ci 267a46c0ec8Sopenharmony_ci def handle_abs(self, event): 268a46c0ec8Sopenharmony_ci if event.matches(libevdev.EV_ABS.ABS_MT_TRACKING_ID): 269a46c0ec8Sopenharmony_ci if event.value > -1: 270a46c0ec8Sopenharmony_ci self.start_new_sequence(event.value) 271a46c0ec8Sopenharmony_ci else: 272a46c0ec8Sopenharmony_ci try: 273a46c0ec8Sopenharmony_ci s = self.current_sequence() 274a46c0ec8Sopenharmony_ci s.finalize() 275a46c0ec8Sopenharmony_ci print("\r{}".format(s)) 276a46c0ec8Sopenharmony_ci except IndexError: 277a46c0ec8Sopenharmony_ci # If the finger was down during start 278a46c0ec8Sopenharmony_ci pass 279a46c0ec8Sopenharmony_ci elif event.matches(libevdev.EV_ABS.ABS_MT_TOUCH_MAJOR): 280a46c0ec8Sopenharmony_ci self.touch.major = event.value 281a46c0ec8Sopenharmony_ci elif event.matches(libevdev.EV_ABS.ABS_MT_TOUCH_MINOR): 282a46c0ec8Sopenharmony_ci self.touch.minor = event.value 283a46c0ec8Sopenharmony_ci elif event.matches(libevdev.EV_ABS.ABS_MT_ORIENTATION): 284a46c0ec8Sopenharmony_ci self.touch.orientation = event.value 285a46c0ec8Sopenharmony_ci 286a46c0ec8Sopenharmony_ci def handle_syn(self, event): 287a46c0ec8Sopenharmony_ci if self.touch.dirty: 288a46c0ec8Sopenharmony_ci try: 289a46c0ec8Sopenharmony_ci self.current_sequence().append(self.touch) 290a46c0ec8Sopenharmony_ci print("\r{}".format(self.current_sequence()), end="") 291a46c0ec8Sopenharmony_ci self.touch = Touch( 292a46c0ec8Sopenharmony_ci major=self.touch.major, 293a46c0ec8Sopenharmony_ci minor=self.touch.minor, 294a46c0ec8Sopenharmony_ci orientation=self.touch.orientation, 295a46c0ec8Sopenharmony_ci ) 296a46c0ec8Sopenharmony_ci except IndexError: 297a46c0ec8Sopenharmony_ci pass 298a46c0ec8Sopenharmony_ci 299a46c0ec8Sopenharmony_ci def handle_event(self, event): 300a46c0ec8Sopenharmony_ci if event.matches(libevdev.EV_ABS): 301a46c0ec8Sopenharmony_ci self.handle_abs(event) 302a46c0ec8Sopenharmony_ci elif event.matches(libevdev.EV_KEY): 303a46c0ec8Sopenharmony_ci self.handle_key(event) 304a46c0ec8Sopenharmony_ci elif event.matches(libevdev.EV_SYN): 305a46c0ec8Sopenharmony_ci self.handle_syn(event) 306a46c0ec8Sopenharmony_ci 307a46c0ec8Sopenharmony_ci def read_events(self): 308a46c0ec8Sopenharmony_ci print("Ready for recording data.") 309a46c0ec8Sopenharmony_ci print("Touch sizes used: {}:{}".format(self.down, self.up)) 310a46c0ec8Sopenharmony_ci print("Palm size used: {}".format(self.palm)) 311a46c0ec8Sopenharmony_ci print("Thumb size used: {}".format(self.thumb)) 312a46c0ec8Sopenharmony_ci print( 313a46c0ec8Sopenharmony_ci "Place a single finger on the device to measure touch size.\n" 314a46c0ec8Sopenharmony_ci "Ctrl+C to exit\n" 315a46c0ec8Sopenharmony_ci ) 316a46c0ec8Sopenharmony_ci 317a46c0ec8Sopenharmony_ci while True: 318a46c0ec8Sopenharmony_ci for event in self.events(): 319a46c0ec8Sopenharmony_ci self.handle_event(event) 320a46c0ec8Sopenharmony_ci 321a46c0ec8Sopenharmony_ci 322a46c0ec8Sopenharmony_cidef colon_tuple(string): 323a46c0ec8Sopenharmony_ci try: 324a46c0ec8Sopenharmony_ci ts = string.split(":") 325a46c0ec8Sopenharmony_ci t = tuple([int(x) for x in ts]) 326a46c0ec8Sopenharmony_ci if len(t) == 2 and t[0] >= t[1]: 327a46c0ec8Sopenharmony_ci return t 328a46c0ec8Sopenharmony_ci except: # noqa 329a46c0ec8Sopenharmony_ci pass 330a46c0ec8Sopenharmony_ci 331a46c0ec8Sopenharmony_ci msg = "{} is not in format N:M (N >= M)".format(string) 332a46c0ec8Sopenharmony_ci raise argparse.ArgumentTypeError(msg) 333a46c0ec8Sopenharmony_ci 334a46c0ec8Sopenharmony_ci 335a46c0ec8Sopenharmony_cidef main(args): 336a46c0ec8Sopenharmony_ci parser = argparse.ArgumentParser(description="Measure touch size and orientation") 337a46c0ec8Sopenharmony_ci parser.add_argument( 338a46c0ec8Sopenharmony_ci "path", 339a46c0ec8Sopenharmony_ci metavar="/dev/input/event0", 340a46c0ec8Sopenharmony_ci nargs="?", 341a46c0ec8Sopenharmony_ci type=str, 342a46c0ec8Sopenharmony_ci help="Path to device (optional)", 343a46c0ec8Sopenharmony_ci ) 344a46c0ec8Sopenharmony_ci parser.add_argument( 345a46c0ec8Sopenharmony_ci "--touch-thresholds", 346a46c0ec8Sopenharmony_ci metavar="down:up", 347a46c0ec8Sopenharmony_ci type=colon_tuple, 348a46c0ec8Sopenharmony_ci help="Thresholds when a touch is logically down or up", 349a46c0ec8Sopenharmony_ci ) 350a46c0ec8Sopenharmony_ci parser.add_argument( 351a46c0ec8Sopenharmony_ci "--palm-threshold", 352a46c0ec8Sopenharmony_ci metavar="t", 353a46c0ec8Sopenharmony_ci type=int, 354a46c0ec8Sopenharmony_ci help="Threshold when a touch is a palm", 355a46c0ec8Sopenharmony_ci ) 356a46c0ec8Sopenharmony_ci args = parser.parse_args() 357a46c0ec8Sopenharmony_ci 358a46c0ec8Sopenharmony_ci try: 359a46c0ec8Sopenharmony_ci device = Device(args.path) 360a46c0ec8Sopenharmony_ci 361a46c0ec8Sopenharmony_ci if args.touch_thresholds is not None: 362a46c0ec8Sopenharmony_ci device.down, device.up = args.touch_thresholds 363a46c0ec8Sopenharmony_ci 364a46c0ec8Sopenharmony_ci if args.palm_threshold is not None: 365a46c0ec8Sopenharmony_ci device.palm = args.palm_threshold 366a46c0ec8Sopenharmony_ci 367a46c0ec8Sopenharmony_ci device.read_events() 368a46c0ec8Sopenharmony_ci except KeyboardInterrupt: 369a46c0ec8Sopenharmony_ci pass 370a46c0ec8Sopenharmony_ci except (PermissionError, OSError): 371a46c0ec8Sopenharmony_ci print("Error: failed to open device") 372a46c0ec8Sopenharmony_ci except InvalidDeviceError as e: 373a46c0ec8Sopenharmony_ci print( 374a46c0ec8Sopenharmony_ci "This device does not have the capabilities for size-based touch detection." 375a46c0ec8Sopenharmony_ci ) 376a46c0ec8Sopenharmony_ci print("Details: {}".format(e)) 377a46c0ec8Sopenharmony_ci 378a46c0ec8Sopenharmony_ci 379a46c0ec8Sopenharmony_ciif __name__ == "__main__": 380a46c0ec8Sopenharmony_ci main(sys.argv) 381