Commit ef5204fd authored by Peter Hutterer's avatar Peter Hutterer

tools: revamp the touchpad-pressure measuring tool

Let's hope this one is more obvious to use for users.
Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent 9351f54d
...@@ -38,27 +38,43 @@ statistics, including whether a touch is/was considered logically down. ...@@ -38,27 +38,43 @@ statistics, including whether a touch is/was considered logically down.
Example output of the tool is below: :: Example output of the tool is below: ::
$ sudo libinput measure touchpad-pressure $ sudo libinput measure touchpad-pressure
Ready for recording data. Using Synaptics TM2668-002: /dev/input/event21
Pressure range used: 8:10
Palm pressure range used: 65535 This is an interactive tool
Place a single finger on the touchpad to measure pressure values.
Ctrl+C to exit Place a single finger on the touchpad to measure pressure values.
&nbsp; Check that:
Sequence 1190 pressure: min: 39 max: 48 avg: 43 median: 44 tags: down - touches subjectively perceived as down are tagged as down
Sequence 1191 pressure: min: 49 max: 65 avg: 62 median: 64 tags: down - touches with a thumb are tagged as thumb
Sequence 1192 pressure: min: 40 max: 78 avg: 64 median: 66 tags: down - touches with a palm are tagged as palm
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 If the touch states do not match the interaction, re-run
Touchpad pressure: 47 min: 47 max: 86 tags: down 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. The example output shows five completed touch sequences and one ongoing one.
For each, the respective minimum and maximum pressure values are printed as For each, the respective minimum and maximum pressure values are printed as
well as some statistics. The ``tags`` show that sequence was considered well as some statistics. The ``down`` column show that each sequence was
logically down at some point. This is an interactive tool and its output may considered logically down at some point, two of the sequences were considered
change frequently. Refer to the **libinput-measure-touchpad-pressure(1)** man thumbs. This is an interactive tool and its output may change frequently. Refer
page for more details. 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 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' narrow down on the best values for your device, specify the 'logically down'
......
...@@ -37,6 +37,51 @@ except ModuleNotFoundError as e: ...@@ -37,6 +37,51 @@ except ModuleNotFoundError as e:
sys.exit(1) 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 Range(object):
"""Class to keep a min/max of a value around""" """Class to keep a min/max of a value around"""
def __init__(self): def __init__(self):
...@@ -112,36 +157,19 @@ class TouchSequence(object): ...@@ -112,36 +157,19 @@ class TouchSequence(object):
def _str_summary(self): def _str_summary(self):
if not self.points: if not self.points:
return "{:78s}".format("Sequence: no pressure values recorded") return fmt.values([self.tracking_id, False, False, False, False,
'No pressure values recorded'])
s = "Sequence {} pressure: "\
"min: {:3d} max: {:3d} avg: {:3d} median: {:3d} tags:" \ s = fmt.values([self.tracking_id, self.was_down, True, self.was_palm,
.format( self.was_thumb, self.prange.min, self.prange.max, 0,
self.tracking_id, self.avg(), self.median()])
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 s return s
def _str_state(self): def _str_state(self):
s = "Touchpad pressure: {:3d} min: {:3d} max: {:3d} tags: {} {} {}" \ s = fmt.values([self.tracking_id, self.is_down, not self.is_down,
.format( self.is_palm, self.is_thumb, self.prange.min,
self.points[-1].pressure, self.prange.max, 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 " "
)
return s return s
...@@ -227,8 +255,8 @@ def handle_key(device, event): ...@@ -227,8 +255,8 @@ def handle_key(device, event):
libevdev.EV_KEY.BTN_TOOL_QUINTTAP libevdev.EV_KEY.BTN_TOOL_QUINTTAP
] ]
if event.code in tapcodes and event.value > 0: if event.code in tapcodes and event.value > 0:
print("\rThis tool cannot handle multiple fingers, " print('\r\033[2KThis tool cannot handle multiple fingers, '
"output will be invalid", file=sys.stderr) 'output will be invalid')
def handle_abs(device, event): def handle_abs(device, event):
...@@ -239,7 +267,7 @@ def handle_abs(device, event): ...@@ -239,7 +267,7 @@ def handle_abs(device, event):
try: try:
s = device.current_sequence() s = device.current_sequence()
s.finalize() s.finalize()
print("\r{}".format(s)) print("\r\033[2K{}".format(s))
except IndexError: except IndexError:
# If the finger was down at startup # If the finger was down at startup
pass pass
...@@ -248,7 +276,7 @@ def handle_abs(device, event): ...@@ -248,7 +276,7 @@ def handle_abs(device, event):
try: try:
s = device.current_sequence() s = device.current_sequence()
s.append(Touch(pressure=event.value)) s.append(Touch(pressure=event.value))
print("\r{}".format(s), end="") print("\r\033[2K{}".format(s), end="")
except IndexError: except IndexError:
# If the finger was down at startup # If the finger was down at startup
pass pass
...@@ -262,12 +290,27 @@ def handle_event(device, event): ...@@ -262,12 +290,27 @@ def handle_event(device, event):
def loop(device): def loop(device):
print("Ready for recording data.") print('This is an interactive tool')
print("Pressure range used: {}:{}".format(device.down, device.up)) print()
print("Palm pressure range used: {}".format(device.palm)) print("Place a single finger on the touchpad to measure pressure values.")
print("Thumb pressure range used: {}".format(device.thumb)) print('Check that:')
print("Place a single finger on the touchpad to measure pressure values.\n" print('- touches subjectively perceived as down are tagged as down')
"Ctrl+C to exit\n") 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: while True:
for event in device.events(): for event in device.events():
...@@ -316,7 +359,9 @@ def main(args): ...@@ -316,7 +359,9 @@ def main(args):
loop(device) loop(device)
except KeyboardInterrupt: except KeyboardInterrupt:
pass print('\r\033[2K{}'.format(fmt.separator()))
print()
except (PermissionError, OSError): except (PermissionError, OSError):
print("Error: failed to open device") print("Error: failed to open device")
except InvalidDeviceError as e: except InvalidDeviceError as e:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment