[PATCH libinput 1/4] tools: add a command to analyze trackpoints
Peter Hutterer
peter.hutterer at who-t.net
Wed Jul 12 06:06:15 UTC 2017
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 <peter.hutterer at who-t.net>
---
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
More information about the wayland-devel
mailing list