From ef5204fda7a0b2a3205b063ea70ad1859e42a339 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Sun, 29 Mar 2020 12:08:30 +1000 Subject: [PATCH] tools: revamp the touchpad-pressure measuring tool Let's hope this one is more obvious to use for users. Signed-off-by: Peter Hutterer --- doc/user/touchpad-pressure-debugging.rst | 50 +++++--- tools/libinput-measure-touchpad-pressure.py | 119 ++++++++++++++------ 2 files changed, 115 insertions(+), 54 deletions(-) diff --git a/doc/user/touchpad-pressure-debugging.rst b/doc/user/touchpad-pressure-debugging.rst index 1ca3fe0e..58837d3a 100644 --- a/doc/user/touchpad-pressure-debugging.rst +++ b/doc/user/touchpad-pressure-debugging.rst @@ -38,27 +38,43 @@ statistics, including whether a touch is/was considered logically down. Example output of the tool is below: :: - $ sudo libinput measure touchpad-pressure - Ready for recording data. - Pressure range used: 8:10 - Palm pressure range used: 65535 - Place a single finger on the touchpad to measure pressure values. - Ctrl+C to exit -   - Sequence 1190 pressure: min: 39 max: 48 avg: 43 median: 44 tags: down - Sequence 1191 pressure: min: 49 max: 65 avg: 62 median: 64 tags: down - Sequence 1192 pressure: min: 40 max: 78 avg: 64 median: 66 tags: down - Sequence 1193 pressure: min: 36 max: 83 avg: 70 median: 73 tags: down - Sequence 1194 pressure: min: 43 max: 76 avg: 72 median: 74 tags: down - Touchpad pressure: 47 min: 47 max: 86 tags: down + $ sudo libinput measure touchpad-pressure + Using Synaptics TM2668-002: /dev/input/event21 + + This is an interactive tool + + Place a single finger on the touchpad to measure pressure values. + Check that: + - touches subjectively perceived as down are tagged as down + - touches with a thumb are tagged as thumb + - touches with a palm are tagged as palm + + If the touch states do not match the interaction, re-run + with --touch-thresholds=down:up using observed pressure values. + See --help for more options. + + Press Ctrl+C to exit + + +-------------------------------------------------------------------------------+ + | Thresh | 70 | 60 | 130 | 100 | | + +-------------------------------------------------------------------------------+ + | Touch | down | up | palm | thumb | min | max | p | avg | median | + +-------------------------------------------------------------------------------+ + | 178 | x | x | | | 75 | 75 | 0 | 75 | 75 | + | 179 | x | x | | | 35 | 88 | 0 | 77 | 81 | + | 180 | x | x | | x | 65 | 113 | 0 | 98 | 98 | + | 181 | x | x | | x | 50 | 101 | 0 | 86 | 90 | + | 182 | x | x | | | 40 | 80 | 0 | 66 | 70 | + | 183 | x | | | | 43 | 78 | 78 | | + ... The example output shows five completed touch sequences and one ongoing one. For each, the respective minimum and maximum pressure values are printed as -well as some statistics. The ``tags`` show that sequence was considered -logically down at some point. This is an interactive tool and its output may -change frequently. Refer to the **libinput-measure-touchpad-pressure(1)** man -page for more details. +well as some statistics. The ``down`` column show that each sequence was +considered logically down at some point, two of the sequences were considered +thumbs. This is an interactive tool and its output may change frequently. Refer +to the **libinput-measure-touchpad-pressure(1)** man page for more details. By default, this tool uses the :ref:`device-quirks` for the pressure range. To narrow down on the best values for your device, specify the 'logically down' diff --git a/tools/libinput-measure-touchpad-pressure.py b/tools/libinput-measure-touchpad-pressure.py index 812074b2..a6cabe02 100755 --- a/tools/libinput-measure-touchpad-pressure.py +++ b/tools/libinput-measure-touchpad-pressure.py @@ -37,6 +37,51 @@ except ModuleNotFoundError as e: sys.exit(1) +class TableFormatter(object): + ALIGNMENT = 3 + + def __init__(self): + self.colwidths = [] + + @property + def width(self): + return sum(self.colwidths) + 1 + + def headers(self, args): + 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) + + return s + + def values(self, args): + 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) + elif type(arg) == bool: + s += '{:^{width}s}|'.format('x' if arg else ' ', width=w) + else: + s += '{:^{width}d}|'.format(arg, width=w) + + if len(args) < len(self.colwidths): + s += '|'.rjust(self.width - len(s), ' ') + return s + + def separator(self): + return '+' + '-' * (self.width - 2) + '+' + + +fmt = TableFormatter() + + class Range(object): """Class to keep a min/max of a value around""" def __init__(self): @@ -112,36 +157,19 @@ class TouchSequence(object): def _str_summary(self): if not self.points: - return "{:78s}".format("Sequence: no pressure values recorded") - - s = "Sequence {} pressure: "\ - "min: {:3d} max: {:3d} avg: {:3d} median: {:3d} tags:" \ - .format( - self.tracking_id, - self.prange.min, - self.prange.max, - self.avg(), - self.median() - ) - if self.was_down: - s += " down" - if self.was_palm: - s += " palm" - if self.was_thumb: - s += " thumb" + 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 = "Touchpad pressure: {:3d} min: {:3d} max: {:3d} tags: {} {} {}" \ - .format( - self.points[-1].pressure, - self.prange.min, - self.prange.max, - "down" if self.is_down else " ", - "palm" if self.is_palm else " ", - "thumb" if self.is_thumb else " " - ) + 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 @@ -227,8 +255,8 @@ def handle_key(device, event): 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('\r\033[2KThis tool cannot handle multiple fingers, ' + 'output will be invalid') def handle_abs(device, event): @@ -239,7 +267,7 @@ def handle_abs(device, event): try: s = device.current_sequence() s.finalize() - print("\r{}".format(s)) + print("\r\033[2K{}".format(s)) except IndexError: # If the finger was down at startup pass @@ -248,7 +276,7 @@ def handle_abs(device, event): try: s = device.current_sequence() s.append(Touch(pressure=event.value)) - print("\r{}".format(s), end="") + print("\r\033[2K{}".format(s), end="") except IndexError: # If the finger was down at startup pass @@ -262,12 +290,27 @@ def handle_event(device, event): def loop(device): - print("Ready for recording data.") - print("Pressure range used: {}:{}".format(device.down, device.up)) - print("Palm pressure range used: {}".format(device.palm)) - print("Thumb pressure range used: {}".format(device.thumb)) - print("Place a single finger on the touchpad to measure pressure values.\n" - "Ctrl+C to exit\n") + 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() + 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']) + print(fmt.separator()) + print(fmt.values(['Thresh', device.down, device.up, device.palm, device.thumb])) + print(fmt.separator()) + print(headers) + print(fmt.separator()) while True: for event in device.events(): @@ -316,7 +359,9 @@ def main(args): loop(device) except KeyboardInterrupt: - pass + print('\r\033[2K{}'.format(fmt.separator())) + print() + except (PermissionError, OSError): print("Error: failed to open device") except InvalidDeviceError as e: -- GitLab