Trackpoints can send very different ranges between the various pressures. Collect the data and print it out to get an idea of what ranges are realistic.
Signed-off-by: Peter Hutterer <[email protected]> --- meson.build | 9 ++ tools/libinput-measure-trackpoint-range | 192 ++++++++++++++++++++++++++++ tools/libinput-measure-trackpoint-range.man | 31 +++++ tools/libinput-measure.man | 3 + tools/libinput.man | 3 + 5 files changed, 238 insertions(+) create mode 100755 tools/libinput-measure-trackpoint-range create mode 100644 tools/libinput-measure-trackpoint-range.man diff --git a/meson.build b/meson.build index abc8d765..ac1ceb17 100644 --- a/meson.build +++ b/meson.build @@ -432,6 +432,15 @@ configure_file(input : 'tools/libinput-measure-touchpad-pressure.man', install_dir : join_paths(get_option('mandir'), 'man1') ) +install_data('tools/libinput-measure-trackpoint-range', + install_dir : libinput_tool_path) +configure_file(input : 'tools/libinput-measure-trackpoint-range.man', + output : 'libinput-measure-trackpoint-range.1', + configuration : man_config, + install : true, + install_dir : join_paths(get_option('mandir'), 'man1') + ) + if get_option('debug-gui') dep_gtk = dependency('gtk+-3.0') dep_cairo = dependency('cairo') diff --git a/tools/libinput-measure-trackpoint-range b/tools/libinput-measure-trackpoint-range new file mode 100755 index 00000000..53dc67ec --- /dev/null +++ b/tools/libinput-measure-trackpoint-range @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +# vim: set expandtab shiftwidth=4: +# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ +# +# Copyright © 2017 Red Hat, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +import sys +import argparse +import evdev +import evdev.ecodes +import pyudev + +MINIMUM_EVENT_COUNT = 1000 + + +class InvalidDeviceError(Exception): + pass + + +class Delta(object): + def __init__(self, x=0, y=0): + self.x = x + self.y = y + + def __bool_(self): + return self.x != 0 or self.y != 0 + + +class Device(object): + def __init__(self, path): + if path is None: + path = self._find_trackpoint_device() + self.path = path + + self.device = evdev.InputDevice(self.path) + + self.deltas = [] + self.nxdeltas = 0 + self.nydeltas = 0 + + self.current_delta = Delta() + self.max_delta = Delta(0, 0) + + def _find_trackpoint_device(self): + context = pyudev.Context() + for device in context.list_devices(subsystem='input'): + if not device.get('ID_INPUT_POINTINGSTICK', 0): + continue + + if not device.device_node or \ + not device.device_node.startswith('/dev/input/event'): + continue + + return device.device_node + + raise InvalidDeviceError("Unable to find a trackpoint device") + + def handle_rel(self, event): + if event.code == evdev.ecodes.REL_X: + self.current_delta.x = event.value + if self.max_delta.x < abs(event.value): + self.max_delta.x = abs(event.value) + elif event.code == evdev.ecodes.REL_Y: + self.current_delta.y = event.value + if self.max_delta.y < abs(event.value): + self.max_delta.y = abs(event.value) + + def handle_syn(self, event): + self.deltas.append(self.current_delta) + if self.current_delta.x != 0: + self.nxdeltas += 1 + if self.current_delta.y != 0: + self.nydeltas += 1 + + self.current_delta = Delta() + + print("\rTrackpoint sends: max x:{:3d}, max y:{:3} samples [{}, {}]" + .format( + self.max_delta.x, self.max_delta.y, + self.nxdeltas, self.nydeltas, + ), end="") + + def read_events(self): + for event in self.device.read_loop(): + if event.type == evdev.ecodes.EV_REL: + self.handle_rel(event) + elif event.type == evdev.ecodes.EV_SYN: + self.handle_syn(event) + + def print_summary(self): + print("\n") # undo the \r from the status line + if not self.deltas: + return + + if len(self.deltas) < MINIMUM_EVENT_COUNT: + print("WARNING: *******************************************\n" + "WARNING: Insufficient samples, data is not reliable\n" + "WARNING: *******************************************\n") + + print("Histogram for x axis deltas, in counts of 5") + xs = [d.x for d in self.deltas] + minx = min(xs) + maxx = max(xs) + for i in range(minx, maxx + 1): + xc = len([x for x in xs if x == i]) + xc = int(xc/5) # counts of 5 is enough + print("{:4}: {}".format(i, "+" * xc, end="")) + + print("Histogram for y axis deltas, in counts of 5") + ys = [d.y for d in self.deltas] + miny = min(ys) + maxy = max(ys) + for i in range(miny, maxy + 1): + yc = len([y for y in ys if y == i]) + yc = int(yc/5) # counts of 5 is enough + print("{:4}: {}".format(i, "+" * yc, end="")) + + axs = sorted([abs(x) for x in xs]) + ays = sorted([abs(y) for y in ys]) + + avgx = int(sum(axs)/len(axs)) + avgy = int(sum(ays)/len(ays)) + + medx = axs[int(len(axs)/2)] + medy = ays[int(len(ays)/2)] + + pc95x = axs[int(len(axs) * 0.95)] + pc95y = ays[int(len(ays) * 0.95)] + + print("Average for abs deltas: x: {:3} y: {:3}".format(avgx, avgy)) + print("Median for abs deltas: x: {:3} y: {:3}".format(medx, medy)) + print("95% percentile for abs deltas: x: {:3} y: {:3}" + .format(pc95x, pc95y) + ) + + +def main(args): + parser = argparse.ArgumentParser( + description="Measure the trackpoint delta coordinate range" + ) + parser.add_argument('path', metavar='/dev/input/event0', + nargs='?', type=str, help='Path to device (optional)') + + args = parser.parse_args() + + try: + device = Device(args.path) + + print( + "This tool measures the commonly used pressure range of the\n" + "trackpoint. Push the trackpoint:\n" + "- Four times around the screen edges\n" + "- From the top left to the bottom right and back, twice\n" + "- From the top right to the bottom left and back, twice\n" + "A minimum of {} events for each axis is required\n" + "\n" + "Movements should emulate fast pointer movement on the screen\n" + "but not use excessive pressure that would not be used\n" + "during day-to-day movement. For best results, run this tool \n" + "several times to get an idea of the common range.\n" + "\n".format(MINIMUM_EVENT_COUNT)) + device.read_events() + except KeyboardInterrupt: + device.print_summary() + except (PermissionError, OSError): + print("Error: failed to open device") + except InvalidDeviceError as e: + print("Error: {}".format(e)) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/tools/libinput-measure-trackpoint-range.man b/tools/libinput-measure-trackpoint-range.man new file mode 100644 index 00000000..94f20404 --- /dev/null +++ b/tools/libinput-measure-trackpoint-range.man @@ -0,0 +1,31 @@ +.TH LIBINPUT-MEASURE-TRACKPOINT-RANGE "1" "" "libinput @LIBINPUT_VERSION@" "libinput Manual" +.SH NAME +libinput\-measure\-trackpoint\-range \- measure the delta range of a trackpoint +.SH SYNOPSIS +.B libinput measure trackpoint\-range [\-\-help] [/dev/input/event0] +.SH DESCRIPTION +.PP +The +.B "libinput measure trackpoint\-range" +tool measures the delta range of a trackpoint. This is +an interactive tool. When executed, the tool will prompt the user to +interact with the trackpoint. On termination, the tool prints a summary of +the trackpoint deltas seen. This data should be attached to any bug report +relating to the trackpoint's speed. +.PP +This is a debugging tool only, its output may change at any time. Do not +rely on the output. +.PP +This tool usually needs to be run as root to have access to the +/dev/input/eventX nodes. +.SH OPTIONS +If a device node is given, this tool opens that device node. Otherwise, this +tool searches for the first node that looks like a trackpoint and uses that +node. +.TP 8 +.B \-\-help +Print help +.SH LIBINPUT +Part of the +.B libinput(1) +suite diff --git a/tools/libinput-measure.man b/tools/libinput-measure.man index d91afdd0..3bcfa158 100644 --- a/tools/libinput-measure.man +++ b/tools/libinput-measure.man @@ -27,6 +27,9 @@ Measure tap-to-click time .TP 8 .B libinput\-measure\-touchpad\-pressure(1) Measure touch pressure +.TP 8 +.B libinput\-measure\-trackpoint\-range(1) +Measure the delta range of a trackpoint. .SH LIBINPUT Part of the .B libinput(1) diff --git a/tools/libinput.man b/tools/libinput.man index fef7cdca..ca8a71fd 100644 --- a/tools/libinput.man +++ b/tools/libinput.man @@ -50,6 +50,9 @@ Measure tap-to-click time .TP 8 .B libinput\-measure\-touchpad\-pressure(1) Measure touch pressure +.TP 8 +.B libinput-measure-trackpoint-range(1) +Measure the delta range of a trackpoint .SH LIBINPUT Part of the .B libinput(1) -- 2.13.0 _______________________________________________ wayland-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/wayland-devel
