Merge branch 'wip/trackpoint-accel-v6'

This commit is contained in:
Peter Hutterer 2017-07-20 13:33:43 +10:00
commit 27ebea9a76
13 changed files with 576 additions and 83 deletions

View file

@ -109,9 +109,16 @@ The image above shows the touchpad acceleration profile in comparison to the
@section ptraccel-trackpoint Pointer acceleration on trackpoints
Trackpoint hardware is quite varied in how it reacts to user pressure and
unlike other devices it cannot easily be normalized for physical properties.
Measuring pressure objectively across a variety of hardware is nontrivial.
The main difference between trackpoint hardware and mice or touchpads is
that trackpoint speed is a function of pressure rather than moving speed.
But trackpoint hardware is quite varied in how it reacts to user pressure
and unlike other devices it cannot easily be normalized for physical
properties. Measuring pressure objectively across a variety of hardware is
nontrivial.
libinput's pointer acceleration is a function of the total available
pressure range on a device.
libinput relies on some sytem-wide configured properties, specifically the
@ref udev_config. The two properties that influence trackpoint acceleration
````POINTINGSTICK_CONST_ACCEL```` which is a constant factor applied to the
@ -125,9 +132,6 @@ builtin is expected to apply this to the device, i.e. libinput does not
handle this property. Once applied, the sensitivity adjusts the deltas
coming out of the hardware.
Trackpoint pointer acceleration uses the @ref ptraccel-low-dpi profile, with a
constant acceleration factor taking the place of the DPI settings.
@image html ptraccel-trackpoint.svg "Pointer acceleration curves for trackpoints"
The image above shows the trackpoint acceleration profile in comparison to the

View file

@ -440,6 +440,15 @@ configure_file(input : 'tools/libinput-measure-touch-size.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')

View file

@ -1990,7 +1990,7 @@ evdev_init_accel(struct evdev_device *device,
if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT)
filter = create_pointer_accelerator_filter_flat(device->dpi);
else if (device->tags & EVDEV_TAG_TRACKPOINT)
filter = create_pointer_accelerator_filter_trackpoint(device->dpi);
filter = create_pointer_accelerator_filter_trackpoint(device->trackpoint_range);
else if (device->dpi < DEFAULT_MOUSE_DPI)
filter = create_pointer_accelerator_filter_linear_low_dpi(device->dpi);
else
@ -2208,26 +2208,48 @@ evdev_read_wheel_tilt_props(struct evdev_device *device)
}
static inline int
evdev_get_trackpoint_dpi(struct evdev_device *device)
evdev_get_trackpoint_range(struct evdev_device *device)
{
const char *trackpoint_accel;
double accel = DEFAULT_TRACKPOINT_ACCEL;
const char *prop;
int range = DEFAULT_TRACKPOINT_RANGE;
trackpoint_accel = udev_device_get_property_value(
device->udev_device, "POINTINGSTICK_CONST_ACCEL");
if (trackpoint_accel) {
accel = parse_trackpoint_accel_property(trackpoint_accel);
if (accel == 0.0) {
if (!(device->tags & EVDEV_TAG_TRACKPOINT))
return DEFAULT_TRACKPOINT_RANGE;
prop = udev_device_get_property_value(device->udev_device,
"LIBINPUT_ATTR_TRACKPOINT_RANGE");
if (prop) {
if (!safe_atoi(prop, &range) ||
(range < 0.0 || range > 100)) {
evdev_log_error(device,
"trackpoint accel property is present but invalid, "
"using %.2f instead\n",
DEFAULT_TRACKPOINT_ACCEL);
accel = DEFAULT_TRACKPOINT_ACCEL;
"trackpoint range property is present but invalid, "
"using %d instead\n",
DEFAULT_TRACKPOINT_RANGE);
range = DEFAULT_TRACKPOINT_RANGE;
}
evdev_log_info(device, "set to const accel %.2f\n", accel);
goto out;
}
return DEFAULT_MOUSE_DPI / accel;
prop = udev_device_get_property_value(device->udev_device,
"POINTINGSTICK_SENSITIVITY");
if (prop) {
int sensitivity;
if (!safe_atoi(prop, &sensitivity) ||
(sensitivity < 0.0 || sensitivity > 255)) {
evdev_log_error(device,
"trackpoint sensitivity property is present but invalid, "
"using %d instead\n",
DEFAULT_TRACKPOINT_SENSITIVITY);
sensitivity = DEFAULT_TRACKPOINT_SENSITIVITY;
}
range = 1.0 * DEFAULT_TRACKPOINT_RANGE *
sensitivity/DEFAULT_TRACKPOINT_SENSITIVITY;
}
out:
evdev_log_info(device, "trackpoint device set to range %d\n", range);
return range;
}
static inline int
@ -2236,13 +2258,8 @@ evdev_read_dpi_prop(struct evdev_device *device)
const char *mouse_dpi;
int dpi = DEFAULT_MOUSE_DPI;
/*
* Trackpoints do not have dpi, instead hwdb may contain a
* POINTINGSTICK_CONST_ACCEL value to compensate for sensitivity
* differences between models, we translate this to a fake dpi.
*/
if (device->tags & EVDEV_TAG_TRACKPOINT)
return evdev_get_trackpoint_dpi(device);
return DEFAULT_MOUSE_DPI;
mouse_dpi = udev_device_get_property_value(device->udev_device,
"MOUSE_DPI");
@ -2660,6 +2677,7 @@ evdev_configure_device(struct evdev_device *device)
evdev_tag_external_mouse(device, device->udev_device);
evdev_tag_trackpoint(device, device->udev_device);
device->dpi = evdev_read_dpi_prop(device);
device->trackpoint_range = evdev_get_trackpoint_range(device);
device->seat_caps |= EVDEV_DEVICE_POINTER;

View file

@ -158,6 +158,7 @@ struct evdev_device {
bool is_mt;
bool is_suspended;
int dpi; /* HW resolution */
int trackpoint_range; /* trackpoint max delta */
struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */
struct ratelimit nonpointer_rel_limit; /* ratelimit for REL_* events from non-pointer devices */
uint32_t model_flags;

View file

@ -147,6 +147,12 @@ filter_get_type(struct motion_filter *filter)
#define X230_MAGIC_SLOWDOWN 0.4 /* unitless */
#define X230_TP_MAGIC_LOW_RES_FACTOR 4.0 /* unitless */
/* Trackpoint acceleration */
#define TRACKPOINT_DEFAULT_MAX_ACCEL 2.0 /* in units/us */
#define TRACKPOINT_DEFAULT_MAX_DELTA 60
/* As measured on a Lenovo T440 at kernel-default sensitivity 128 */
#define TRACKPOINT_DEFAULT_RANGE 20 /* max value */
/*
* Pointer acceleration filter constants
*/
@ -195,6 +201,20 @@ struct tablet_accelerator_flat {
yres_scale; /* 1000dpi : tablet res */
};
struct trackpoint_accelerator {
struct motion_filter base;
struct device_float_coords history[4];
size_t history_size;
double scale_factor;
double max_accel;
double max_delta;
double incline; /* incline of the function */
double offset; /* offset of the function */
};
static void
feed_trackers(struct pointer_accelerator *accel,
const struct device_float_coords *delta,
@ -904,38 +924,6 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
return factor * X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR;
}
double
trackpoint_accel_profile(struct motion_filter *filter,
void *data,
double speed_in, /* device units/µs */
uint64_t time)
{
struct pointer_accelerator *accel_filter =
(struct pointer_accelerator *)filter;
double max_accel = accel_filter->accel; /* unitless factor */
double threshold = accel_filter->threshold; /* units/ms */
const double incline = accel_filter->incline;
double dpi_factor = accel_filter->dpi/(double)DEFAULT_MOUSE_DPI;
double factor;
/* dpi_factor is always < 1.0, increase max_accel, reduce
the threshold so it kicks in earlier */
max_accel /= dpi_factor;
threshold *= dpi_factor;
/* see pointer_accel_profile_linear for a long description */
if (v_us2ms(speed_in) < 0.07)
factor = 10 * v_us2ms(speed_in) + 0.3;
else if (speed_in < threshold)
factor = 1;
else
factor = incline * v_us2ms(speed_in - threshold) + 1;
factor = min(max_accel, factor);
return factor;
}
struct motion_filter_interface accelerator_interface = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
.filter = accelerator_filter_pre_normalized,
@ -1063,30 +1051,245 @@ create_pointer_accelerator_filter_lenovo_x230(int dpi)
return &filter->base;
}
double
trackpoint_accel_profile(struct motion_filter *filter,
void *data,
double delta)
{
struct trackpoint_accelerator *accel_filter =
(struct trackpoint_accelerator *)filter;
const double max_accel = accel_filter->max_accel;
double factor;
delta = fabs(delta);
/* This is almost the equivalent of the xserver acceleration
at sensitivity 128 and speed 0.0 */
factor = delta * accel_filter->incline + accel_filter->offset;
factor = min(factor, max_accel);
return factor;
}
/**
* Average the deltas, they are messy and can provide sequences like 7, 7,
* 9, 8, 14, 7, 9, 8 ... The outliers cause unpredictable jumps, so average
* them out.
*/
static inline struct device_float_coords
trackpoint_average_delta(struct trackpoint_accelerator *filter,
const struct device_float_coords *unaccelerated)
{
size_t i;
struct device_float_coords avg = {0};
memmove(&filter->history[1],
&filter->history[0],
sizeof(*filter->history) * (filter->history_size - 1));
filter->history[0] = *unaccelerated;
for (i = 0; i < filter->history_size; i++) {
avg.x += filter->history[i].x;
avg.y += filter->history[i].y;
}
avg.x /= filter->history_size;
avg.y /= filter->history_size;
return avg;
}
/**
* Undo any system-wide magic scaling, so we're behaving the same regardless
* of the trackpoint hardware. This way we can apply our profile independent
* of any other configuration that messes with things.
*/
static inline struct device_float_coords
trackpoint_normalize_deltas(const struct trackpoint_accelerator *accel_filter,
const struct device_float_coords *delta)
{
struct device_float_coords scaled = *delta;
scaled.x *= accel_filter->scale_factor;
scaled.y *= accel_filter->scale_factor;
return scaled;
}
/**
* We set a max delta per event, to avoid extreme jumps once we exceed the
* expected pressure. Trackpoint hardware is inconsistent once the pressure
* gets high, so we can expect sequences like 30, 40, 35, 55, etc. This may
* be caused by difficulty keeping up high consistent pressures or just
* measuring errors in the hardware. Either way, we cap to a max delta so
* once we hit the high pressures, movement is capped and consistent.
*/
static inline struct normalized_coords
trackpoint_clip_to_max_delta(const struct trackpoint_accelerator *accel_filter,
struct normalized_coords coords)
{
const double max_delta = accel_filter->max_delta;
if (abs(coords.x) > max_delta)
coords.x = copysign(max_delta, coords.x);
if (abs(coords.y) > max_delta)
coords.y = copysign(max_delta, coords.y);
return coords;
}
static struct normalized_coords
trackpoint_accelerator_filter(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data, uint64_t time)
{
struct trackpoint_accelerator *accel_filter =
(struct trackpoint_accelerator *)filter;
struct device_float_coords scaled;
struct device_float_coords avg;
struct normalized_coords coords;
double f;
double delta;
scaled = trackpoint_normalize_deltas(accel_filter, unaccelerated);
avg = trackpoint_average_delta(accel_filter, &scaled);
delta = hypot(avg.x, avg.y);
f = trackpoint_accel_profile(filter, data, delta);
coords.x = avg.x * f;
coords.y = avg.y * f;
coords = trackpoint_clip_to_max_delta(accel_filter, coords);
return coords;
}
static struct normalized_coords
trackpoint_accelerator_filter_noop(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data, uint64_t time)
{
struct trackpoint_accelerator *accel_filter =
(struct trackpoint_accelerator *)filter;
struct device_float_coords scaled;
struct device_float_coords avg;
struct normalized_coords coords;
scaled = trackpoint_normalize_deltas(accel_filter, unaccelerated);
avg = trackpoint_average_delta(accel_filter, &scaled);
coords.x = avg.x;
coords.y = avg.y;
coords = trackpoint_clip_to_max_delta(accel_filter, coords);
return coords;
}
static bool
trackpoint_accelerator_set_speed(struct motion_filter *filter,
double speed_adjustment)
{
struct trackpoint_accelerator *accel_filter =
(struct trackpoint_accelerator*)filter;
double incline, offset, max;
assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
/* Helloooo, magic numbers.
These numbers were obtained by finding an acceleration curve that
provides precision at slow speeds but still provides a good
acceleration at higher pressure - and a quick ramp-up to that
acceleration.
Trackpoints have built-in acceleration curves already, so we
don't put a new function on top, we merely scale the output from
those curves (re-calculating the pressure values from the
firmware-defined curve and applying a new curve is unreliable).
For that basic scaling, we assume a constant factor f based on
the speed setting together with a maximum factor m (for this
speed setting). Delta acceleration is thus:
factor = max(m, f)
accelerated_delta = delta * factor;
Trial and error showed a couple of pairs that work well for the
various speed settings (Lenovo T440, sensitivity 128):
-1.0: f = 0.3, m = 1
-0.5: f = 0.6, m = 2
0.0: f = 1.0, m = 6
0.5: f = 1.4, m = 8
1.0: f = 1.9, m = 15
Note: if f >= 2.0, some pixels are unaddressable
Those pairs were fed into the linear/exponential regression tool
at http://www.xuru.org/rt/LR.asp and show two functions that map
speed settings to the respective f and m.
Given a speed setting s in [-1.0, 1.0]
f(s) = 0.8 * s + 1.04
m(s) = 4.6 * e**(1.2 * s)
These are close enough to the tested pairs.
*/
max = 4.6 * pow(M_E, 1.2 * speed_adjustment);
incline = 0.8 * speed_adjustment + 1.04;
offset = 0;
accel_filter->max_accel = max;
accel_filter->incline = incline;
accel_filter->offset = offset;
filter->speed_adjustment = speed_adjustment;
return true;
}
static void
trackpoint_accelerator_destroy(struct motion_filter *filter)
{
struct trackpoint_accelerator *accel_filter =
(struct trackpoint_accelerator *)filter;
free(accel_filter);
}
struct motion_filter_interface accelerator_interface_trackpoint = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
.filter = accelerator_filter_unnormalized,
.filter_constant = accelerator_filter_noop,
.restart = accelerator_restart,
.destroy = accelerator_destroy,
.set_speed = accelerator_set_speed,
.filter = trackpoint_accelerator_filter,
.filter_constant = trackpoint_accelerator_filter_noop,
.restart = NULL,
.destroy = trackpoint_accelerator_destroy,
.set_speed = trackpoint_accelerator_set_speed,
};
struct motion_filter *
create_pointer_accelerator_filter_trackpoint(int dpi)
create_pointer_accelerator_filter_trackpoint(int max_hw_delta)
{
struct pointer_accelerator *filter;
struct trackpoint_accelerator *filter;
filter = create_default_filter(dpi);
/* Trackpoints are special. They don't have a movement speed like a
* mouse or a finger, instead they send a constant stream of events
* based on the pressure applied.
*
* Physical ranges on a trackpoint are the max values for relative
* deltas, but these are highly device-specific.
*
*/
filter = zalloc(sizeof *filter);
if (!filter)
return NULL;
filter->history_size = ARRAY_LENGTH(filter->history);
filter->scale_factor = 1.0 * TRACKPOINT_DEFAULT_RANGE / max_hw_delta;
filter->max_accel = TRACKPOINT_DEFAULT_MAX_ACCEL;
filter->max_delta = TRACKPOINT_DEFAULT_MAX_DELTA;
filter->base.interface = &accelerator_interface_trackpoint;
filter->profile = trackpoint_accel_profile;
filter->threshold = DEFAULT_THRESHOLD;
filter->accel = DEFAULT_ACCELERATION;
filter->incline = DEFAULT_INCLINE;
filter->dpi = dpi;
return &filter->base;
}

View file

@ -120,7 +120,7 @@ struct motion_filter *
create_pointer_accelerator_filter_lenovo_x230(int dpi);
struct motion_filter *
create_pointer_accelerator_filter_trackpoint(int dpi);
create_pointer_accelerator_filter_trackpoint(int max_delta);
struct motion_filter *
create_pointer_accelerator_filter_tablet(int xres, int yres);
@ -152,6 +152,5 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
double
trackpoint_accel_profile(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
double delta);
#endif /* FILTER_H */

View file

@ -53,6 +53,8 @@
/* The HW DPI rate we normalize to before calculating pointer acceleration */
#define DEFAULT_MOUSE_DPI 1000
#define DEFAULT_TRACKPOINT_RANGE 20
#define DEFAULT_TRACKPOINT_SENSITIVITY 128
#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
#define ANSI_RED "\x1B[0;31m"

View file

@ -2076,9 +2076,9 @@ litest_setup_tests_pointer(void)
struct range axis_range = {ABS_X, ABS_Y + 1};
struct range compass = {0, 7}; /* cardinal directions */
litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_POINTINGSTICK);
litest_add_for_device("pointer:motion", pointer_motion_relative_zero, LITEST_MOUSE);
litest_add_ranged("pointer:motion", pointer_motion_relative_min_decel, LITEST_RELATIVE, LITEST_ANY, &compass);
litest_add_ranged("pointer:motion", pointer_motion_relative_min_decel, LITEST_RELATIVE, LITEST_POINTINGSTICK, &compass);
litest_add("pointer:motion", pointer_motion_absolute, LITEST_ABSOLUTE, LITEST_ANY);
litest_add("pointer:motion", pointer_motion_unaccel, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:button", pointer_button, LITEST_BUTTON, LITEST_CLICKPAD);
@ -2111,7 +2111,7 @@ litest_setup_tests_pointer(void)
litest_add("pointer:accel", pointer_accel_invalid, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_RELATIVE);
litest_add("pointer:accel", pointer_accel_defaults_absolute_relative, LITEST_ABSOLUTE|LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_direction_change, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_direction_change, LITEST_RELATIVE, LITEST_POINTINGSTICK);
litest_add("pointer:accel", pointer_accel_profile_defaults, LITEST_RELATIVE, LITEST_TOUCHPAD);
litest_add("pointer:accel", pointer_accel_profile_defaults_noprofile, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_profile_invalid, LITEST_RELATIVE, LITEST_ANY);

View file

@ -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)

View file

@ -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

View file

@ -30,6 +30,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)

View file

@ -53,6 +53,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)

View file

@ -169,6 +169,24 @@ print_accel_func(struct motion_filter *filter,
}
}
static void
print_accel_func_trackpoint(struct motion_filter *filter,
int max)
{
printf("# gnuplot:\n");
printf("# set xlabel \"deltas (units)\"\n");
printf("# set ylabel \"raw accel factor\"\n");
printf("# set style data lines\n");
printf("# plot \"gnuplot.data\" using 1:2 title 'accel factor'\n");
printf("#\n");
printf("# data: delta(units) factor\n");
for (double delta = 0; delta < max; delta += 0.2) {
double factor = trackpoint_accel_profile(filter, NULL, delta);
printf("%.2f %f\n", delta, factor);
}
}
static void
usage(void)
{
@ -184,6 +202,7 @@ usage(void)
"--steps=<double> ... in motion and delta modes only. Increase dx by step each round\n"
"--speed=<double> ... accel speed [-1, 1], default 0\n"
"--dpi=<int> ... device resolution in DPI (default: 1000)\n"
"--trackpoint_range=<int> ... range of the trackpoint deltas (default: 30)\n"
"--filter=<linear|low-dpi|touchpad|x230|trackpoint> \n"
" linear ... the default motion filter\n"
" low-dpi ... low-dpi filter, use --dpi with this argument\n"
@ -219,6 +238,7 @@ main(int argc, char **argv)
int dpi = 1000;
const char *filter_type = "linear";
accel_profile_func_t profile = NULL;
int tp_range_max = 20;
enum {
OPT_HELP = 1,
@ -229,6 +249,7 @@ main(int argc, char **argv)
OPT_SPEED,
OPT_DPI,
OPT_FILTER,
OPT_TRACKPOINT_RANGE,
};
while (1) {
@ -243,6 +264,7 @@ main(int argc, char **argv)
{"speed", 1, 0, OPT_SPEED },
{"dpi", 1, 0, OPT_DPI },
{"filter", 1, 0, OPT_FILTER },
{"trackpoint-range", 1, 0, OPT_TRACKPOINT_RANGE },
{0, 0, 0, 0}
};
@ -300,6 +322,9 @@ main(int argc, char **argv)
case OPT_FILTER:
filter_type = optarg;
break;
case OPT_TRACKPOINT_RANGE:
tp_range_max = strtod(optarg, NULL);
break;
default:
usage();
exit(1);
@ -320,8 +345,8 @@ main(int argc, char **argv)
filter = create_pointer_accelerator_filter_lenovo_x230(dpi);
profile = touchpad_lenovo_x230_accel_profile;
} else if (streq(filter_type, "trackpoint")) {
filter = create_pointer_accelerator_filter_trackpoint(dpi);
profile = trackpoint_accel_profile;
filter = create_pointer_accelerator_filter_trackpoint(tp_range_max);
profile = NULL; /* trackpoint is special */
} else {
fprintf(stderr, "Invalid filter type %s\n", filter_type);
return 1;
@ -349,9 +374,12 @@ main(int argc, char **argv)
custom_deltas[nevents++] = strtod(argv[optind++], NULL);
}
if (print_accel)
print_accel_func(filter, profile, dpi);
else if (print_delta)
if (print_accel) {
if (!profile) /* trackpoint */
print_accel_func_trackpoint(filter, tp_range_max);
else
print_accel_func(filter, profile, dpi);
} else if (print_delta)
print_ptraccel_deltas(filter, step);
else if (print_motion)
print_ptraccel_movement(filter, nevents, max_dx, step);