Merge branch 'master' into tablet-support

This commit is contained in:
Peter Hutterer 2015-08-26 14:24:16 +10:00
commit 8fe7f08e85
42 changed files with 15962 additions and 378 deletions

View file

@ -1,7 +1,7 @@
AC_PREREQ([2.64])
m4_define([libinput_major_version], [0])
m4_define([libinput_minor_version], [21])
m4_define([libinput_major_version], [1])
m4_define([libinput_minor_version], [0])
m4_define([libinput_micro_version], [0])
m4_define([libinput_version],
[libinput_major_version.libinput_minor_version.libinput_micro_version])
@ -31,7 +31,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
# b) If interfaces have been changed or added, but binary compatibility has
# been preserved, change to C+1:0:A+1
# c) If the interface is the same as the previous version, change to C:R+1:A
LIBINPUT_LT_VERSION=15:0:5
LIBINPUT_LT_VERSION=15:2:5
AC_SUBST(LIBINPUT_LT_VERSION)
AM_SILENT_RULES([yes])
@ -60,13 +60,22 @@ PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(MTDEV, [mtdev >= 1.1.0])
PKG_CHECK_MODULES(LIBUDEV, [libudev])
PKG_CHECK_MODULES(LIBEVDEV, [libevdev >= 0.4])
PKG_CHECK_MODULES(LIBUNWIND,
AC_ARG_WITH(libunwind,
AS_HELP_STRING([--without-libunwind],[Do not use libunwind]))
AS_IF([test "x$with_libunwind" != "xno"],
[PKG_CHECK_MODULES(LIBUNWIND,
[libunwind],
[HAVE_LIBUNWIND=yes],
[HAVE_LIBUNWIND=no])
if test "x$HAVE_LIBUNWIND" = "xyes"; then
AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support])
fi
[HAVE_LIBUNWIND=no])],
[HAVE_LIBUNWIND=no])
AS_IF([test "x$HAVE_LIBUNWIND" = "xyes"],
[AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support])],
[AS_IF([test "x$with_libunwind" = "xyes"],
[AC_MSG_ERROR([libunwind requested but not found])])])
AM_CONDITIONAL(HAVE_LIBUNWIND, [test "x$HAVE_LIBUNWIND" = xyes])
AC_PATH_PROG(ADDR2LINE, [addr2line])
if test "x$ADDR2LINE" != "x"; then

View file

@ -18,6 +18,7 @@ header_files = \
$(srcdir)/normalization-of-relative-motion.dox \
$(srcdir)/palm-detection.dox \
$(srcdir)/page-hierarchy.dox \
$(srcdir)/pointer-acceleration.dox \
$(srcdir)/reporting-bugs.dox \
$(srcdir)/scrolling.dox \
$(srcdir)/seats.dox \
@ -34,12 +35,17 @@ diagram_files = \
$(srcdir)/dot/libinput-stack-wayland.gv \
$(srcdir)/dot/libinput-stack-xorg.gv \
$(srcdir)/dot/libinput-stack-gnome.gv \
$(srcdir)/dot/evemu.gv \
$(srcdir)/svg/software-buttons.svg \
$(srcdir)/svg/clickfinger.svg \
$(srcdir)/svg/button-scrolling.svg \
$(srcdir)/svg/edge-scrolling.svg \
$(srcdir)/svg/palm-detection.svg \
$(srcdir)/svg/pinch-gestures.svg \
$(srcdir)/svg/ptraccel-linear.svg \
$(srcdir)/svg/ptraccel-low-dpi.svg \
$(srcdir)/svg/ptraccel-touchpad.svg \
$(srcdir)/svg/ptraccel-trackpoint.svg \
$(srcdir)/svg/swipe-gestures.svg \
$(srcdir)/svg/tap-n-drag.svg \
$(srcdir)/svg/thumb-detection.svg \

View file

@ -67,7 +67,7 @@ or height. Note that rotation applies to the device's origin, rotation
usually requires an offset to move the coordinates back into the original
range.
The most comon matrices are:
The most common matrices are:
- 90 degree clockwise:
@f$

19
doc/dot/evemu.gv Normal file
View file

@ -0,0 +1,19 @@
digraph stack
{
compound=true;
rankdir="LR";
node [
shape="box";
]
kernel [label="Kernel"];
libinput;
xserver [label="X Server"];
kernel -> libinput
libinput -> xserver
kernel -> evemu
evemu -> stdout
}

View file

@ -30,7 +30,7 @@ scroll_sources.
@section faq_gpl Is libinput GPL-licensed?
No, libinput is MIT licensed. The Linux kernel header file linux/input.h in
libinput's tree is provded to ensure the same behavior regardless of which
libinput's tree is provided to ensure the same behavior regardless of which
kernel version libinput is built on. It does not make libinput GPL-licensed.
@section faq_config_options Where is the configuration stored?

View file

@ -82,7 +82,7 @@ screen as well as the context of those virtual objects:
@image html touchscreen-gestures.svg "Context-sensitivity of touchscreen gestures"
In this example, the finger movements are identical but in the left case
both fingers are located within the same window, thus suggesting an attemp
both fingers are located within the same window, thus suggesting an attempt
to zoom. In the right case both fingers are located on a window border,
thus suggesting a window movement. libinput only has knowledge of the finger
coordinates (and even then only in device coordinates, not in screen

View file

@ -25,16 +25,16 @@ movement speed increases, acceleration is applied - at high speeds a low-dpi
device will roughly feel the same as a higher-dpi mouse.
This normalization only applies to accelerated coordinates, unaccelerated
coordiantes are left in device-units. It is up to the caller to interpret
coordinates are left in device-units. It is up to the caller to interpret
those coordinates correctly.
@section Normalization of touchpad coordinates
Touchpads may have a different resolution for the horizontal and vertical
axis. Interpreting coordinates from the touchpad without taking resolutino
axis. Interpreting coordinates from the touchpad without taking resolution
into account results in uneven motion.
libinput scales unaccelerated touchpad motion do the resolution of the
libinput scales unaccelerated touchpad motion to the resolution of the
touchpad's x axis, i.e. the unaccelerated value for the y axis is:
y = (x / resolution_x) * resolution_y

View file

@ -31,5 +31,6 @@
- @subpage test-suite
- @subpage tools
- @subpage pointer-acceleration
*/

View file

@ -46,10 +46,10 @@ will generate a button event for this touch.
If a device provides a <a
href="http://en.wikipedia.org/wiki/Pointing_stick">trackpoint</a>, it is
usually located above the touchpad. This increases the likelyhood of
usually located above the touchpad. This increases the likelihood of
accidental touches whenever the trackpoint is used.
libinput disables the touchpad whenver it detects trackpoint activity for a
libinput disables the touchpad whenever it detects trackpoint activity for a
certain timeout until after trackpoint activity stops. Touches generated
during this timeout will not move the pointer, and touches started during
this timeout will likewise not move the pointer (allowing for a user to rest

View file

@ -0,0 +1,110 @@
/**
@page pointer-acceleration Pointer acceleration
libinput uses device-specific pointer acceleration methods, with the default
being the @ref ptraccel-linear. The methods share common properties, such as
@ref ptraccel-velocity.
This page explains the high-level concepts used in the code. It aims to
provide an overview for developers and is not necessarily useful for
users.
@section ptraccel-velocity Velocity calculation
The device's speed of movement is measured across multiple input events
through so-called "trackers". Each event prepends a the tracker item, each
subsequent tracker contains the delta of that item to the current position,
the timestamp of the event that created it and the cardinal direction of the
movement at the time. If a device moves into the same direction, the
velocity is calculated across multiple trackers. For example, if a device
moves steadily for 10 events to the left, the velocity is calculated across
all 10 events.
Whenever the movement changes direction or significantly changes speed, the
velocity is calculated from the direction/speed change only. For example, if
a device moves steadily for 8 events to the left and then 2 events to the
right, the velocity is only that of the last 2 events.
An extra time limit prevents events that are too old to factor into the
velocity calculation. For example, if a device moves steadily for 5 events
to the left, then pauses, then moves again for 5 events to the left, only
the last 5 events are used for velocity calculation.
The velocity is then used to calculate the acceleration factor
@section ptraccel-factor Acceleration factor
The acceleration factor is the final outcome of the pointer acceleration
calculations. It is a unitless factor that is applied to the current delta,
a factor of 2 doubles the delta (i.e. speeds up the movement), a factor of
less than 1 reduces the delta (i.e. slows the movement).
Any factor less than 1 requires the user to move the device further to move
the visible pointer. This is called deceleration and enables high precision
target selection through subpixel movements. libinput's current maximum
deceleration factor is 0.3 (i.e. slow down to 30% of the pointer speed).
A factor higher than 1 moves the pointer further than the physical device
moves. This is acceleration and allows a user to cross the screen quickly
but effectively skips pixels. libinput's current maximum acceleration factor
is 3.5.
@section ptraccel-linear Linear pointer acceleration
The linear pointer acceleration method is the default for most pointer
devices. It provides deceleration at very slow movements, a 1:1 mapping for
regular movements and a linear increase to the maximum acceleration factor
for fast movements.
Linear pointer acceleration applies to devices with above 1000dpi resolution
and after @ref motion_normalization is applied.
@image html ptraccel-linear.svg "Linear pointer acceleration"
The image above shows the linear pointer acceleration settings at various
speeds. The line for 0.0 is the default acceleration curve, speed settings
above 0.0 accelerate sooner, faster and to a higher maximum acceleration.
Speed settings below 0 delay when acceleration kicks in, how soon the
maximum acceleration is reached and the maximum acceleration factor.
Extremely low speed settings provide no acceleration and additionally
decelerate all movement by a constant factor.
@section ptraccel-low-dpi Pointer acceleration for low-dpi devices
Low-dpi devices are those with a physical resolution of less than 1000 dots
per inch (dpi). The pointer acceleration is adjusted to provide roughly the
same feel for all devices at normal to high speeds. At slow speeds, the
pointer acceleration works on device-units rather than normalized
coordinates (see @ref motion_normalization).
@image html ptraccel-low-dpi.svg "Pointer acceleration for low-dpi devices"
The image above shows the default pointer acceleration curve for a speed of
0.0 at different DPI settings. A device with low DPI has the acceleration
applied sooner and with a stronger acceleration factor.
@section ptraccel-touchpad Pointer acceleration on touchpads
Touchpad pointer acceleration uses the @ref ptraccel-linear profile, with a
constant deceleration factor applied. The user expectation of how much a
pointer should move in response to finger movement is different to that of a
mouse device, hence the constant deceleration factor.
@image html ptraccel-touchpad.svg "Pointer acceleration curve for touchpads"
The image above shows the touchpad acceleration profile in comparison to the
@ref ptraccel-linear. The shape of the curve is identical but vertically squashed.
@section ptraccel-trackpoint Pointer acceleration on trackpoints
Trackpoint pointer acceleration uses the @ref ptraccel-low-dpi profile, with a
constant deceleration 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
@ref ptraccel-linear. The constant acceleration factor, usually applied by
udev, shapes the acceleration profile.
*/

View file

@ -8,7 +8,7 @@ When reporting bugs against libinput, please follow the instructions below
and provide the required data. This will speed up triage, resulting in a
quicker bugfix.
First, try to identify the bugi by reproducing it reliably. The more
First, try to identify the bug by reproducing it reliably. The more
specific a bug description is, the easier it is to fix. The @ref
libinput-debug-events helper tool can help identify whether the bug is in
libinput at all. This tool is a direct hook to libinput without a desktop
@ -85,4 +85,10 @@ $ sudo evemu-play /dev/input/event4 < scroll.evemu
If the bug is triggered by replaying on your device, attach the recording to
the bug report.
@note libinput does not affect the evemu recording. libinput and evemu talk
directly to the kernel's device nodes. An evemu recording is not influenced
by the libinput version or whether a libinput context is currently active.
@dotfile evemu.gv
*/

View file

@ -5,7 +5,7 @@ Each device in libinput is assigned to one seat.
A seat has two identifiers, the physical name and the logical name. The
physical name is summarized as the list of devices a process on the same
physical seat has access to. The logical seat name is the seat name for a
logical group of devices. A compositor may use that to create additonal
logical group of devices. A compositor may use that to create additional
seats as independent device sets. Alternatively, a compositor may limit
itself to a single logical seat, leaving a second compositor to manage
devices on the other logical seats.
@ -32,7 +32,7 @@ libinput_seat, all other devices reference their own respective seats.
@section seats_and_features The effect of seat assignment
A logical set is interprested as a group of devices that usually belong to a
A logical set is interpreted as a group of devices that usually belong to a
single user that interacts with a computer. Thus, the devices are
semantically related. This means for devices within the same logical seat:

5486
doc/svg/ptraccel-linear.svg Normal file

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 550 KiB

3748
doc/svg/ptraccel-low-dpi.svg Normal file

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 377 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 171 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 371 KiB

View file

@ -40,7 +40,7 @@ disabled with libinput_device_config_tap_set_drag_lock_enabled().
The above diagram explains the process, a tap (a) followed by a finger held
down (b) starts the drag process and logically holds the left mouse button
down. A movement of the finger (c) will drag the selected item until the
finger is relased (e). If needed and drag lock is enabled, the finger's
finger is released (e). If needed and drag lock is enabled, the finger's
position can be reset by lifting and quickly setting it down again on the
touchpad (d). This will be interpreted as continuing move and is especially
useful on small touchpads or with slow pointer acceleration.

View file

@ -8,7 +8,7 @@ tools are usually installed, others are @ref developer_tools only.
libinput ships with two tools to gather information about devices:
@ref libinput-list-devices and @ref libinput-debug-events. Both tools must
be run as root to have acess to the kernel's @c /dev/input/event* device
be run as root to have access to the kernel's @c /dev/input/event* device
files.
@subsection libinput-list-devices
@ -97,6 +97,6 @@ $ sudo ./tools/event-gui
See the @c --help output for information about the available options.
@note The @c --grab flag puts an exclusive @c EVIOCGRAB on the device to
avoid interference with the desktiop while testing.
avoid interference with the desktop while testing.
*/

View file

@ -71,7 +71,7 @@ touches, others support 5 touches like the Synaptics touchpads when using
SMBus.
These touchpads usually also provide extra information. Apple touchpads
provide an ellipsis and the orientation of the ellipsis for each touch point.
provide an ellipse and the orientation of the ellipse for each touch point.
Other touchpads provide a pressure value for each touch point (see @ref
touchpads_pressure_handling).
@ -130,7 +130,7 @@ located underneath the keyboard, accidental palm touches are a non-issue.
Pressure is usually directly related to contact area. Human fingers flatten
out as the pressure on the pad increases, resulting in a bigger contact area
and the firmware then calculates that back into a ressure reading.
and the firmware then calculates that back into a pressure reading.
libinput uses pressure to detect accidental palm contact and thumbs, though
pressure data is often device-specific and unreliable.
@ -138,7 +138,7 @@ pressure data is often device-specific and unreliable.
@subsection touchpads_circular Circular touchpads
Only listed for completeness, circular touchpads have not been used in
laptops for a number of years. These touchpad shaped in an ellipsis or
laptops for a number of years. These touchpad shaped in an ellipse or
straight.
@subsection touchpads_tablets Graphics tablets

View file

@ -640,9 +640,7 @@ evdev_middlebutton_handle_timeout(uint64_t now, void *data)
{
struct evdev_device *device = (struct evdev_device*)data;
evdev_middlebutton_handle_event(device,
libinput_now(device->base.seat->libinput),
MIDDLEBUTTON_EVENT_TIMEOUT);
evdev_middlebutton_handle_event(device, now, MIDDLEBUTTON_EVENT_TIMEOUT);
}
static int

View file

@ -342,7 +342,7 @@ tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
int
tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
{
struct libinput_device *device = &tp->device->base;
struct evdev_device *device = tp->device;
struct tp_touch *t;
enum libinput_pointer_axis axis;
double *delta;
@ -369,7 +369,7 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
case EDGE_NONE:
if (t->scroll.direction != -1) {
/* Send stop scroll event */
pointer_notify_axis(device, time,
evdev_notify_axis(device, time,
AS_MASK(t->scroll.direction),
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
&zero,
@ -390,12 +390,13 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
}
normalized = tp_get_delta(t);
normalized = tp_filter_motion(tp, &normalized, time);
/* scroll is not accelerated */
normalized = tp_filter_motion_unaccelerated(tp, &normalized, time);
switch (t->scroll.edge_state) {
case EDGE_SCROLL_TOUCH_STATE_NONE:
case EDGE_SCROLL_TOUCH_STATE_AREA:
log_bug_libinput(device->seat->libinput,
log_bug_libinput(tp_libinput_context(tp),
"unexpected scroll state %d\n",
t->scroll.edge_state);
break;
@ -416,11 +417,11 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
if (*delta == 0.0)
continue;
pointer_notify_axis(device, time,
AS_MASK(axis),
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
&normalized,
&zero_discrete);
evdev_notify_axis(device, time,
AS_MASK(axis),
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
&normalized,
&zero_discrete);
t->scroll.direction = axis;
tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_POSTED);
@ -432,14 +433,14 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
void
tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time)
{
struct libinput_device *device = &tp->device->base;
struct evdev_device *device = tp->device;
struct tp_touch *t;
const struct normalized_coords zero = { 0.0, 0.0 };
const struct discrete_coords zero_discrete = { 0.0, 0.0 };
tp_for_each_touch(tp, t) {
if (t->scroll.direction != -1) {
pointer_notify_axis(device, time,
evdev_notify_axis(device, time,
AS_MASK(t->scroll.direction),
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
&zero,

View file

@ -328,7 +328,8 @@ tp_gesture_twofinger_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
delta = tp_get_average_touches_delta(tp);
}
delta = tp_filter_motion(tp, &delta, time);
/* scroll is not accelerated */
delta = tp_filter_motion_unaccelerated(tp, &delta, time);
if (normalized_is_zero(delta))
return GESTURE_2FG_STATE_SCROLL;

View file

@ -71,6 +71,18 @@ tp_filter_motion(struct tp_dispatch *tp,
unaccelerated, tp, time);
}
struct normalized_coords
tp_filter_motion_unaccelerated(struct tp_dispatch *tp,
const struct normalized_coords *unaccelerated,
uint64_t time)
{
if (normalized_is_zero(*unaccelerated))
return *unaccelerated;
return filter_dispatch_constant(tp->device->pointer.filter,
unaccelerated, tp, time);
}
static inline void
tp_motion_history_push(struct tp_touch *t)
{
@ -1511,7 +1523,7 @@ static int
tp_init_accel(struct tp_dispatch *tp, double diagonal)
{
int res_x, res_y;
accel_profile_func_t profile;
struct motion_filter *filter;
res_x = tp->device->abs.absinfo_x->resolution;
res_y = tp->device->abs.absinfo_y->resolution;
@ -1527,14 +1539,14 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
if (tp->device->model_flags & EVDEV_MODEL_LENOVO_X230)
profile = touchpad_lenovo_x230_accel_profile;
filter = create_pointer_accelerator_filter_lenovo_x230(tp->device->dpi);
else
profile = touchpad_accel_profile_linear;
filter = create_pointer_accelerator_filter_touchpad(tp->device->dpi);
if (evdev_device_init_pointer_acceleration(tp->device, profile) == -1)
if (!filter)
return -1;
return 0;
return evdev_device_init_pointer_acceleration(tp->device, filter);
}
static uint32_t
@ -1629,8 +1641,9 @@ tp_init_scroll(struct tp_dispatch *tp, struct evdev_device *device)
tp->scroll.method = tp_scroll_get_default_method(tp);
tp->device->base.config.scroll_method = &tp->scroll.config_method;
/* In mm for touchpads with valid resolution, see tp_init_accel() */
tp->device->scroll.threshold = 5.0;
/* In mm for touchpads with valid resolution, see tp_init_accel() */
tp->device->scroll.threshold = 0.0;
tp->device->scroll.direction_lock_threshold = 5.0;
return 0;
}

View file

@ -395,6 +395,10 @@ struct normalized_coords
tp_filter_motion(struct tp_dispatch *tp,
const struct normalized_coords *unaccelerated,
uint64_t time);
struct normalized_coords
tp_filter_motion_unaccelerated(struct tp_dispatch *tp,
const struct normalized_coords *unaccelerated,
uint64_t time);
int
tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t);

View file

@ -612,7 +612,7 @@ evdev_process_absolute_motion(struct evdev_device *device,
}
}
static void
void
evdev_notify_axis(struct evdev_device *device,
uint64_t time,
uint32_t axes,
@ -638,6 +638,25 @@ evdev_notify_axis(struct evdev_device *device,
&discrete);
}
static inline bool
evdev_reject_relative(struct evdev_device *device,
const struct input_event *e,
uint64_t time)
{
struct libinput *libinput = device->base.seat->libinput;
if ((e->code == REL_X || e->code == REL_Y) &&
(device->seat_caps & EVDEV_DEVICE_POINTER) == 0) {
log_bug_libinput_ratelimit(libinput,
&device->nonpointer_rel_limit,
"REL_X/Y from device '%s', but this device is not a pointer\n",
device->devname);
return true;
}
return false;
}
static inline void
evdev_process_relative(struct evdev_device *device,
struct input_event *e, uint64_t time)
@ -645,6 +664,9 @@ evdev_process_relative(struct evdev_device *device,
struct normalized_coords wheel_degrees = { 0.0, 0.0 };
struct discrete_coords discrete = { 0.0, 0.0 };
if (evdev_reject_relative(device, e, time))
return;
switch (e->code) {
case REL_X:
if (device->pending_event != EVDEV_RELATIVE_MOTION)
@ -1339,20 +1361,10 @@ evdev_device_dispatch(void *data)
rc = libevdev_next_event(device->evdev,
LIBEVDEV_READ_FLAG_NORMAL, &ev);
if (rc == LIBEVDEV_READ_STATUS_SYNC) {
switch (ratelimit_test(&device->syn_drop_limit)) {
case RATELIMIT_PASS:
log_info(libinput, "SYN_DROPPED event from "
"\"%s\" - some input events have "
"been lost.\n", device->devname);
break;
case RATELIMIT_THRESHOLD:
log_info(libinput, "SYN_DROPPED flood "
"from \"%s\"\n",
device->devname);
break;
case RATELIMIT_EXCEEDED:
break;
}
log_info_ratelimit(libinput,
&device->syn_drop_limit,
"SYN_DROPPED event from \"%s\" - some input events have been lost.\n",
device->devname);
/* send one more sync event so we handle all
currently pending events before we sync up
@ -1409,12 +1421,9 @@ evdev_accel_config_get_default_speed(struct libinput_device *device)
int
evdev_device_init_pointer_acceleration(struct evdev_device *device,
accel_profile_func_t profile)
struct motion_filter *filter)
{
device->pointer.filter = create_pointer_accelerator_filter(profile,
device->dpi);
if (!device->pointer.filter)
return -1;
device->pointer.filter = filter;
device->pointer.config.available = evdev_accel_config_available;
device->pointer.config.set_speed = evdev_accel_config_set_speed;
@ -1863,14 +1872,19 @@ evdev_configure_mt_device(struct evdev_device *device)
static inline int
evdev_init_accel(struct evdev_device *device)
{
accel_profile_func_t profile;
struct motion_filter *filter;
if (device->dpi < DEFAULT_MOUSE_DPI)
profile = pointer_accel_profile_linear_low_dpi;
if (device->tags & EVDEV_TAG_TRACKPOINT)
filter = create_pointer_accelerator_filter_trackpoint(device->dpi);
else if (device->dpi < DEFAULT_MOUSE_DPI)
filter = create_pointer_accelerator_filter_linear_low_dpi(device->dpi);
else
profile = pointer_accel_profile_linear;
filter = create_pointer_accelerator_filter_linear(device->dpi);
return evdev_device_init_pointer_acceleration(device, profile);
if (!filter)
return -1;
return evdev_device_init_pointer_acceleration(device, filter);
}
static int
@ -2167,6 +2181,7 @@ evdev_device_create(struct libinput_seat *seat,
device->pending_event = EVDEV_NONE;
device->devname = libevdev_get_name(device->evdev);
device->scroll.threshold = 5.0; /* Default may be overridden */
device->scroll.direction_lock_threshold = 5.0; /* Default may be overridden */
device->scroll.direction = 0;
device->scroll.wheel_click_angle =
evdev_read_wheel_click_prop(device);
@ -2175,6 +2190,8 @@ evdev_device_create(struct libinput_seat *seat,
/* at most 5 SYN_DROPPED log-messages per 30s */
ratelimit_init(&device->syn_drop_limit, s2us(30), 5);
/* at most 5 log-messages per 5s */
ratelimit_init(&device->nonpointer_rel_limit, s2us(5), 5);
matrix_init_identity(&device->abs.calibration);
matrix_init_identity(&device->abs.usermatrix);
@ -2437,12 +2454,12 @@ evdev_post_scroll(struct evdev_device *device,
trigger speed to start scrolling in the other direction */
} else if (!evdev_is_scrolling(device,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
if (fabs(delta->y) >= device->scroll.threshold)
if (fabs(delta->y) >= device->scroll.direction_lock_threshold)
evdev_start_scrolling(device,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
} else if (!evdev_is_scrolling(device,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
if (fabs(delta->x) >= device->scroll.threshold)
if (fabs(delta->x) >= device->scroll.direction_lock_threshold)
evdev_start_scrolling(device,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
}

View file

@ -167,6 +167,7 @@ struct evdev_device {
void (*change_scroll_method)(struct evdev_device *device);
bool button_scroll_active;
double threshold;
double direction_lock_threshold;
uint32_t direction;
struct normalized_coords buildup;
@ -223,6 +224,7 @@ struct evdev_device {
int dpi; /* HW resolution */
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;
};
@ -286,7 +288,7 @@ evdev_device_create(struct libinput_seat *seat,
int
evdev_device_init_pointer_acceleration(struct evdev_device *device,
accel_profile_func_t profile);
struct motion_filter *filter);
struct evdev_dispatch *
evdev_touchpad_create(struct evdev_device *device);
@ -388,6 +390,13 @@ evdev_pointer_notify_physical_button(struct evdev_device *device,
void
evdev_init_natural_scroll(struct evdev_device *device);
void
evdev_notify_axis(struct evdev_device *device,
uint64_t time,
uint32_t axes,
enum libinput_pointer_axis_source source,
const struct normalized_coords *delta_in,
const struct discrete_coords *discrete_in);
void
evdev_post_scroll(struct evdev_device *device,
uint64_t time,

View file

@ -33,16 +33,20 @@ struct motion_filter_interface {
struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time);
struct normalized_coords (*filter_constant)(
struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time);
void (*restart)(struct motion_filter *filter,
void *data,
uint64_t time);
void (*destroy)(struct motion_filter *filter);
bool (*set_speed)(struct motion_filter *filter,
double speed);
double speed_adjustment);
};
struct motion_filter {
double speed; /* normalized [-1, 1] */
double speed_adjustment; /* normalized [-1, 1] */
struct motion_filter_interface *interface;
};

View file

@ -36,6 +36,26 @@
#include "libinput-util.h"
#include "filter-private.h"
/* Once normalized, touchpads see the same acceleration as mice. that is
* technically correct but subjectively wrong, we expect a touchpad to be a
* lot slower than a mouse. Apply a magic factor to slow down all movements
*/
#define TP_MAGIC_SLOWDOWN 0.4 /* unitless factor */
/* Convert speed/velocity from units/us to units/ms */
static inline double
v_us2ms(double units_per_us)
{
return units_per_us * 1000.0;
}
/* Convert speed/velocity from units/ms to units/us */
static inline double
v_ms2us(double units_per_ms)
{
return units_per_ms/1000.0;
}
struct normalized_coords
filter_dispatch(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
@ -44,6 +64,14 @@ filter_dispatch(struct motion_filter *filter,
return filter->interface->filter(filter, unaccelerated, data, time);
}
struct normalized_coords
filter_dispatch_constant(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time)
{
return filter->interface->filter_constant(filter, unaccelerated, data, time);
}
void
filter_restart(struct motion_filter *filter,
void *data, uint64_t time)
@ -62,30 +90,36 @@ filter_destroy(struct motion_filter *filter)
bool
filter_set_speed(struct motion_filter *filter,
double speed)
double speed_adjustment)
{
return filter->interface->set_speed(filter, speed);
return filter->interface->set_speed(filter, speed_adjustment);
}
double
filter_get_speed(struct motion_filter *filter)
{
return filter->speed;
return filter->speed_adjustment;
}
/*
* Default parameters for pointer acceleration profiles.
*/
#define DEFAULT_THRESHOLD 0.4 /* in units/ms */
#define DEFAULT_THRESHOLD v_ms2us(0.4) /* in units/us */
#define MINIMUM_THRESHOLD v_ms2us(0.2) /* in units/us */
#define DEFAULT_ACCELERATION 2.0 /* unitless factor */
#define DEFAULT_INCLINE 1.1 /* unitless factor */
/* for the Lenovo x230 custom accel. do not touch */
#define X230_THRESHOLD v_ms2us(0.4) /* in units/us */
#define X230_ACCELERATION 2.0 /* unitless factor */
#define X230_INCLINE 1.1 /* unitless factor */
/*
* Pointer acceleration filter constants
*/
#define MAX_VELOCITY_DIFF 1 /* units/ms */
#define MAX_VELOCITY_DIFF v_ms2us(1) /* units/us */
#define MOTION_TIMEOUT ms2us(1000)
#define NUM_POINTER_TRACKERS 16
@ -95,20 +129,18 @@ struct pointer_tracker {
int dir;
};
struct pointer_accelerator;
struct pointer_accelerator {
struct motion_filter base;
accel_profile_func_t profile;
double velocity; /* units/ms */
double last_velocity; /* units/ms */
struct normalized_coords last;
double velocity; /* units/us */
double last_velocity; /* units/us */
struct pointer_tracker *trackers;
int cur_tracker;
double threshold; /* units/ms */
double threshold; /* units/us */
double accel; /* unitless factor */
double incline; /* incline of the function */
@ -150,7 +182,7 @@ static double
calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time)
{
double tdelta = time - tracker->time + 1;
return normalized_length(tracker->delta) / tdelta * 1000.0; /* units/ms */
return normalized_length(tracker->delta) / tdelta; /* units/us */
}
static inline double
@ -220,7 +252,7 @@ calculate_velocity(struct pointer_accelerator *accel, uint64_t time)
}
}
return result; /* units/ms */
return result; /* units/us */
}
static double
@ -253,14 +285,63 @@ calculate_acceleration(struct pointer_accelerator *accel,
return factor; /* unitless factor */
}
static inline double
calculate_acceleration_factor(struct pointer_accelerator *accel,
const struct normalized_coords *unaccelerated,
void *data,
uint64_t time)
{
double velocity; /* units/us */
double accel_factor;
feed_trackers(accel, unaccelerated, time);
velocity = calculate_velocity(accel, time);
accel_factor = calculate_acceleration(accel,
data,
velocity,
accel->last_velocity,
time);
accel->last_velocity = velocity;
return accel_factor;
}
static struct normalized_coords
accelerator_filter(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time /* in us */)
void *data, uint64_t time)
{
struct pointer_accelerator *accel =
(struct pointer_accelerator *) filter;
double accel_value; /* unitless factor */
struct normalized_coords accelerated;
accel_value = calculate_acceleration_factor(accel,
unaccelerated,
data,
time);
accelerated.x = accel_value * unaccelerated->x;
accelerated.y = accel_value * unaccelerated->y;
return accelerated;
}
static struct normalized_coords
accelerator_filter_noop(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time)
{
return *unaccelerated;
}
static struct normalized_coords
accelerator_filter_low_dpi(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time)
{
struct pointer_accelerator *accel =
(struct pointer_accelerator *) filter;
double velocity; /* units/ms */
double accel_value; /* unitless factor */
struct normalized_coords accelerated;
struct normalized_coords unnormalized;
@ -272,24 +353,86 @@ accelerator_filter(struct motion_filter *filter,
unnormalized.x = unaccelerated->x * dpi_factor;
unnormalized.y = unaccelerated->y * dpi_factor;
feed_trackers(accel, &unnormalized, time);
velocity = calculate_velocity(accel, time);
accel_value = calculate_acceleration(accel,
data,
velocity,
accel->last_velocity,
time);
accel_value = calculate_acceleration_factor(accel,
&unnormalized,
data,
time);
accelerated.x = accel_value * unnormalized.x;
accelerated.y = accel_value * unnormalized.y;
accel->last = unnormalized;
return accelerated;
}
accel->last_velocity = velocity;
static struct normalized_coords
accelerator_filter_trackpoint(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time)
{
struct pointer_accelerator *accel =
(struct pointer_accelerator *) filter;
double accel_value; /* unitless factor */
struct normalized_coords accelerated;
struct normalized_coords unnormalized;
double dpi_factor = accel->dpi_factor;
/* trackpoints with a dpi factor have a const accel set, remove that
* and restore device units. The accel profile takes const accel
* into account */
dpi_factor = min(1.0, dpi_factor);
unnormalized.x = unaccelerated->x * dpi_factor;
unnormalized.y = unaccelerated->y * dpi_factor;
accel_value = calculate_acceleration_factor(accel,
&unnormalized,
data,
time);
accelerated.x = accel_value * unnormalized.x;
accelerated.y = accel_value * unnormalized.y;
return accelerated;
}
static struct normalized_coords
accelerator_filter_x230(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time)
{
struct pointer_accelerator *accel =
(struct pointer_accelerator *) filter;
double accel_factor; /* unitless factor */
struct normalized_coords accelerated;
double velocity; /* units/us */
feed_trackers(accel, unaccelerated, time);
velocity = calculate_velocity(accel, time);
accel_factor = calculate_acceleration(accel,
data,
velocity,
accel->last_velocity,
time);
accel->last_velocity = velocity;
accelerated.x = accel_factor * unaccelerated->x;
accelerated.y = accel_factor * unaccelerated->y;
return accelerated;
}
static struct normalized_coords
touchpad_constant_filter(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time)
{
struct normalized_coords normalized;
normalized.x = TP_MAGIC_SLOWDOWN * unaccelerated->x;
normalized.y = TP_MAGIC_SLOWDOWN * unaccelerated->y;
return normalized;
}
static void
accelerator_restart(struct motion_filter *filter,
void *data,
@ -325,65 +468,32 @@ accelerator_destroy(struct motion_filter *filter)
static bool
accelerator_set_speed(struct motion_filter *filter,
double speed)
double speed_adjustment)
{
struct pointer_accelerator *accel_filter =
(struct pointer_accelerator *)filter;
assert(speed >= -1.0 && speed <= 1.0);
assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
/* Note: the numbers below are nothing but trial-and-error magic,
don't read more into them other than "they mostly worked ok" */
/* delay when accel kicks in */
accel_filter->threshold = DEFAULT_THRESHOLD - speed / 4000.0;
if (accel_filter->threshold < 0.0002)
accel_filter->threshold = 0.0002;
accel_filter->threshold = DEFAULT_THRESHOLD -
v_ms2us(0.25) * speed_adjustment;
if (accel_filter->threshold < MINIMUM_THRESHOLD)
accel_filter->threshold = MINIMUM_THRESHOLD;
/* adjust max accel factor */
accel_filter->accel = DEFAULT_ACCELERATION + speed * 1.5;
accel_filter->accel = DEFAULT_ACCELERATION + speed_adjustment * 1.5;
/* higher speed -> faster to reach max */
accel_filter->incline = DEFAULT_INCLINE + speed * 0.75;
accel_filter->incline = DEFAULT_INCLINE + speed_adjustment * 0.75;
filter->speed = speed;
filter->speed_adjustment = speed_adjustment;
return true;
}
struct motion_filter_interface accelerator_interface = {
accelerator_filter,
accelerator_restart,
accelerator_destroy,
accelerator_set_speed,
};
struct motion_filter *
create_pointer_accelerator_filter(accel_profile_func_t profile,
int dpi)
{
struct pointer_accelerator *filter;
filter = zalloc(sizeof *filter);
if (filter == NULL)
return NULL;
filter->base.interface = &accelerator_interface;
filter->profile = profile;
filter->last_velocity = 0.0;
filter->last.x = 0;
filter->last.y = 0;
filter->trackers =
calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers);
filter->cur_tracker = 0;
filter->threshold = DEFAULT_THRESHOLD;
filter->accel = DEFAULT_ACCELERATION;
filter->incline = DEFAULT_INCLINE;
filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI;
return &filter->base;
}
/**
* Custom acceleration function for mice < 1000dpi.
* At slow motion, a single device unit causes a one-pixel movement.
@ -397,25 +507,32 @@ create_pointer_accelerator_filter(accel_profile_func_t profile,
double
pointer_accel_profile_linear_low_dpi(struct motion_filter *filter,
void *data,
double speed_in, /* in device units (units/ms) */
uint64_t time /* in us */)
double speed_in, /* in device units (units/us) */
uint64_t time)
{
struct pointer_accelerator *accel_filter =
(struct pointer_accelerator *)filter;
double s1, s2;
double max_accel = accel_filter->accel; /* unitless factor */
const double threshold = accel_filter->threshold; /* units/ms */
double threshold = accel_filter->threshold; /* units/us */
const double incline = accel_filter->incline;
double factor;
double factor; /* unitless */
double dpi_factor = accel_filter->dpi_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;
s1 = min(1, 0.3 + speed_in * 10.0);
s2 = 1 + (speed_in - threshold * dpi_factor) * incline;
/* 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, s2 > 1 ? s2 : s1);
factor = min(max_accel, factor);
return factor;
}
@ -424,54 +541,98 @@ double
pointer_accel_profile_linear(struct motion_filter *filter,
void *data,
double speed_in, /* 1000-dpi normalized */
uint64_t time /* in us */)
uint64_t time)
{
struct pointer_accelerator *accel_filter =
(struct pointer_accelerator *)filter;
double s1, s2;
const double max_accel = accel_filter->accel; /* unitless factor */
const double threshold = accel_filter->threshold; /* units/ms */
const double threshold = accel_filter->threshold; /* units/us */
const double incline = accel_filter->incline;
double factor;
double factor; /* unitless */
s1 = min(1, 0.3 + speed_in * 10);
s2 = 1 + (speed_in - threshold) * incline;
/*
Our acceleration function calculates a factor to accelerate input
deltas with. The function is a double incline with a plateau,
with a rough shape like this:
factor = min(max_accel, s2 > 1 ? s2 : s1);
accel
factor
^
| /
| _____/
| /
|/
+-------------> speed in
The two inclines are linear functions in the form
y = ax + b
where y is speed_out
x is speed_in
a is the incline of acceleration
b is minimum acceleration factor
for speeds up to 0.07 u/ms, we decelerate, down to 30% of input
speed.
hence 1 = a * 0.07 + 0.3
0.3 = a * 0.00 + 0.3 => a := 10
deceleration function is thus:
y = 10x + 0.3
Note:
* 0.07u/ms as threshold is a result of trial-and-error and
has no other intrinsic meaning.
* 0.3 is chosen simply because it is above the Nyquist frequency
for subpixel motion within a pixel.
*/
if (v_us2ms(speed_in) < 0.07) {
factor = 10 * v_us2ms(speed_in) + 0.3;
/* up to the threshold, we keep factor 1, i.e. 1:1 movement */
} else if (speed_in < threshold) {
factor = 1;
} else {
/* Acceleration function above the threshold:
y = ax' + b
where T is threshold
x is speed_in
x' is speed
and
y(T) == 1
hence 1 = ax' + 1
=> x' := (x - T)
*/
factor = incline * v_us2ms(speed_in - threshold) + 1;
}
/* Cap at the maximum acceleration factor */
factor = min(max_accel, factor);
return factor;
}
double
touchpad_accel_profile_linear(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time /* in us */)
void *data,
double speed_in, /* units/us */
uint64_t time)
{
/* Once normalized, touchpads see the same
acceleration as mice. that is technically correct but
subjectively wrong, we expect a touchpad to be a lot
slower than a mouse. Apply a magic factor here and proceed
as normal. */
const double TP_MAGIC_SLOWDOWN = 0.4;
double speed_out;
double factor; /* unitless */
speed_in *= TP_MAGIC_SLOWDOWN;
speed_out = pointer_accel_profile_linear(filter, data, speed_in, time);
factor = pointer_accel_profile_linear(filter, data, speed_in, time);
return speed_out * TP_MAGIC_SLOWDOWN;
return factor * TP_MAGIC_SLOWDOWN;
}
double
touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time /* in us */)
uint64_t time)
{
/* Keep the magic factor from touchpad_accel_profile_linear. */
const double TP_MAGIC_SLOWDOWN = 0.4;
const double X230_MAGIC_SLOWDOWN = 0.4; /* unitless */
/* Those touchpads presents an actual lower resolution that what is
* advertised. We see some jumps from the cursor due to the big steps
@ -479,24 +640,219 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
* Apply a factor to minimize those jumps at low speed, and try
* keeping the same feeling as regular touchpads at high speed.
* It still feels slower but it is usable at least */
const double TP_MAGIC_LOW_RES_FACTOR = 4.0;
double speed_out;
const double TP_MAGIC_LOW_RES_FACTOR = 4.0; /* unitless */
double factor; /* unitless */
struct pointer_accelerator *accel_filter =
(struct pointer_accelerator *)filter;
double s1, s2;
double f1, f2; /* unitless */
const double max_accel = accel_filter->accel *
TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */
const double threshold = accel_filter->threshold /
TP_MAGIC_LOW_RES_FACTOR; /* units/ms */
TP_MAGIC_LOW_RES_FACTOR; /* units/us */
const double incline = accel_filter->incline * TP_MAGIC_LOW_RES_FACTOR;
speed_in *= TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR;
/* Note: the magic values in this function are obtained by
* trial-and-error. No other meaning should be interpreted.
* The calculation is a compressed form of
* pointer_accel_profile_linear(), look at the git history of that
* function for an explaination of what the min/max/etc. does.
*/
speed_in *= X230_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR;
s1 = min(1, speed_in * 5);
s2 = 1 + (speed_in - threshold) * incline;
f1 = min(1, v_us2ms(speed_in) * 5);
f2 = 1 + (v_us2ms(speed_in) - v_us2ms(threshold)) * incline;
speed_out = min(max_accel, s2 > 1 ? s2 : s1);
factor = min(max_accel, f2 > 1 ? f2 : f1);
return speed_out * TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR;
return factor * X230_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR;
}
double
trackpoint_accel_profile(struct motion_filter *filter,
void *data,
double speed_in, /* 1000-dpi normalized */
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 factor;
double dpi_factor = accel_filter->dpi_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 = {
.filter = accelerator_filter,
.filter_constant = accelerator_filter_noop,
.restart = accelerator_restart,
.destroy = accelerator_destroy,
.set_speed = accelerator_set_speed,
};
static struct pointer_accelerator *
create_default_filter(int dpi)
{
struct pointer_accelerator *filter;
filter = zalloc(sizeof *filter);
if (filter == NULL)
return NULL;
filter->last_velocity = 0.0;
filter->trackers =
calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers);
filter->cur_tracker = 0;
filter->threshold = DEFAULT_THRESHOLD;
filter->accel = DEFAULT_ACCELERATION;
filter->incline = DEFAULT_INCLINE;
filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI;
return filter;
}
struct motion_filter *
create_pointer_accelerator_filter_linear(int dpi)
{
struct pointer_accelerator *filter;
filter = create_default_filter(dpi);
if (!filter)
return NULL;
filter->base.interface = &accelerator_interface;
filter->profile = pointer_accel_profile_linear;
return &filter->base;
}
struct motion_filter_interface accelerator_interface_low_dpi = {
.filter = accelerator_filter_low_dpi,
.filter_constant = accelerator_filter_noop,
.restart = accelerator_restart,
.destroy = accelerator_destroy,
.set_speed = accelerator_set_speed,
};
struct motion_filter *
create_pointer_accelerator_filter_linear_low_dpi(int dpi)
{
struct pointer_accelerator *filter;
filter = create_default_filter(dpi);
if (!filter)
return NULL;
filter->base.interface = &accelerator_interface_low_dpi;
filter->profile = pointer_accel_profile_linear_low_dpi;
return &filter->base;
}
struct motion_filter_interface accelerator_interface_touchpad = {
.filter = accelerator_filter,
.filter_constant = touchpad_constant_filter,
.restart = accelerator_restart,
.destroy = accelerator_destroy,
.set_speed = accelerator_set_speed,
};
struct motion_filter *
create_pointer_accelerator_filter_touchpad(int dpi)
{
struct pointer_accelerator *filter;
filter = create_default_filter(dpi);
if (!filter)
return NULL;
filter->base.interface = &accelerator_interface_touchpad;
filter->profile = touchpad_accel_profile_linear;
return &filter->base;
}
struct motion_filter_interface accelerator_interface_x230 = {
.filter = accelerator_filter_x230,
.filter_constant = accelerator_filter_noop,
.restart = accelerator_restart,
.destroy = accelerator_destroy,
.set_speed = accelerator_set_speed,
};
/* The Lenovo x230 has a bad touchpad. This accel method has been
* trial-and-error'd, any changes to it will require re-testing everything.
* Don't touch this.
*/
struct motion_filter *
create_pointer_accelerator_filter_lenovo_x230(int dpi)
{
struct pointer_accelerator *filter;
filter = zalloc(sizeof *filter);
if (filter == NULL)
return NULL;
filter->base.interface = &accelerator_interface_x230;
filter->profile = touchpad_lenovo_x230_accel_profile;
filter->last_velocity = 0.0;
filter->trackers =
calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers);
filter->cur_tracker = 0;
filter->threshold = X230_THRESHOLD;
filter->accel = X230_ACCELERATION; /* unitless factor */
filter->incline = X230_INCLINE; /* incline of the acceleration function */
filter->dpi_factor = 1; /* unused for this accel method */
return &filter->base;
}
struct motion_filter_interface accelerator_interface_trackpoint = {
.filter = accelerator_filter_trackpoint,
.filter_constant = accelerator_filter_noop,
.restart = accelerator_restart,
.destroy = accelerator_destroy,
.set_speed = accelerator_set_speed,
};
struct motion_filter *
create_pointer_accelerator_filter_trackpoint(int dpi)
{
struct pointer_accelerator *filter;
filter = create_default_filter(dpi);
if (!filter)
return NULL;
filter->base.interface = &accelerator_interface_trackpoint;
filter->profile = trackpoint_accel_profile;
filter->threshold = DEFAULT_THRESHOLD;
filter->accel = DEFAULT_ACCELERATION;
filter->incline = DEFAULT_INCLINE;
return &filter->base;
}

View file

@ -34,11 +34,33 @@
struct motion_filter;
/**
* Accelerate the given coordinates.
* Takes a set of unaccelerated deltas and accelerates them based on the
* current and previous motion.
*
* This is a superset of filter_dispatch_constant()
*
* @see filter_dispatch_constant
*/
struct normalized_coords
filter_dispatch(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time);
/**
* Apply constant motion filters, but no acceleration.
*
* Takes a set of unaccelerated deltas and applies any constant filters to
* it but does not accelerate the delta in the conventional sense.
*
* @see filter_dispatch
*/
struct normalized_coords
filter_dispatch_constant(struct motion_filter *filter,
const struct normalized_coords *unaccelerated,
void *data, uint64_t time);
void
filter_restart(struct motion_filter *filter,
void *data, uint64_t time);
@ -57,9 +79,22 @@ typedef double (*accel_profile_func_t)(struct motion_filter *filter,
double velocity,
uint64_t time);
/* Pointer acceleration types */
struct motion_filter *
create_pointer_accelerator_filter(accel_profile_func_t filter,
int dpi);
create_pointer_accelerator_filter_linear(int dpi);
struct motion_filter *
create_pointer_accelerator_filter_linear_low_dpi(int dpi);
struct motion_filter *
create_pointer_accelerator_filter_touchpad(int dpi);
struct motion_filter *
create_pointer_accelerator_filter_lenovo_x230(int dpi);
struct motion_filter *
create_pointer_accelerator_filter_trackpoint(int dpi);
/*
* Pointer acceleration profiles.
@ -85,4 +120,9 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
double
trackpoint_accel_profile(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
#endif /* FILTER_H */

View file

@ -271,6 +271,20 @@ typedef void (*libinput_source_dispatch_t)(void *data);
#define log_bug_libinput(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__)
#define log_bug_client(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
#define log_debug_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_DEBUG, __VA_ARGS__)
#define log_info_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__)
#define log_error_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, __VA_ARGS__)
#define log_bug_kernel_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
#define log_bug_libinput_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__)
#define log_bug_client_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
void
log_msg_ratelimit(struct libinput *libinput,
struct ratelimit *ratelimit,
enum libinput_log_priority priority,
const char *format, ...)
LIBINPUT_ATTRIBUTE_PRINTF(4, 5);
void
log_msg(struct libinput *libinput,
enum libinput_log_priority priority,

View file

@ -45,12 +45,6 @@
#define CASE_RETURN_STRING(a) case a: return #a;
void
set_logging_enabled(int enabled);
void
log_info(const char *format, ...);
/*
* This list data structure is a verbatim copy from wayland-util.h from the
* Wayland project; except that wl_ prefix has been removed.

View file

@ -180,6 +180,31 @@ log_msg(struct libinput *libinput,
va_end(args);
}
void
log_msg_ratelimit(struct libinput *libinput,
struct ratelimit *ratelimit,
enum libinput_log_priority priority,
const char *format, ...)
{
va_list args;
enum ratelimit_state state;
state = ratelimit_test(ratelimit);
if (state == RATELIMIT_EXCEEDED)
return;
va_start(args, format);
log_msg_va(libinput, priority, format, args);
va_end(args);
if (state == RATELIMIT_THRESHOLD)
log_msg(libinput,
priority,
"WARNING: log rate limit exceeded (%d msgs per %dms). Discarding future messages.\n",
ratelimit->burst,
us2ms(ratelimit->interval));
}
LIBINPUT_EXPORT void
libinput_log_set_priority(struct libinput *libinput,
enum libinput_log_priority priority)
@ -2046,8 +2071,9 @@ libinput_post_event(struct libinput *libinput,
events_len *= 2;
events = realloc(events, events_len * sizeof *events);
if (!events) {
fprintf(stderr, "Failed to reallocate event ring "
"buffer");
log_error(libinput,
"Failed to reallocate event ring buffer. "
"Events may be discarded\n");
return;
}

View file

@ -1031,7 +1031,8 @@ libinput_event_touch_get_seat_slot(struct libinput_event_touch *event);
* LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
*
* @note It is an application bug to call this function for events of type
* @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
* other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
* LIBINPUT_EVENT_TOUCH_MOTION.
*
* @param event The libinput touch event
* @return The current absolute x coordinate
@ -1050,7 +1051,8 @@ libinput_event_touch_get_x(struct libinput_event_touch *event);
* LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
*
* @note It is an application bug to call this function for events of type
* @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
* other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
* LIBINPUT_EVENT_TOUCH_MOTION.
*
* @param event The libinput touch event
* @return The current absolute y coordinate
@ -1068,7 +1070,8 @@ libinput_event_touch_get_y(struct libinput_event_touch *event);
* LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
*
* @note It is an application bug to call this function for events of type
* @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
* other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
* LIBINPUT_EVENT_TOUCH_MOTION.
*
* @param event The libinput touch event
* @param width The current output screen width
@ -1088,7 +1091,8 @@ libinput_event_touch_get_x_transformed(struct libinput_event_touch *event,
* LIBINPUT_EVENT_TOUCH_MOTION, this function returns 0.
*
* @note It is an application bug to call this function for events of type
* @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref LIBINPUT_EVENT_TOUCH_MOTION.
* other than @ref LIBINPUT_EVENT_TOUCH_DOWN or @ref
* LIBINPUT_EVENT_TOUCH_MOTION.
*
* @param event The libinput touch event
* @param height The current output screen height

View file

@ -1046,6 +1046,156 @@ START_TEST(device_udev_tag_wacom_tablet)
}
END_TEST
START_TEST(device_nonpointer_rel)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct libinput_device *device;
int i;
uinput = litest_create_uinput_device("test device",
NULL,
EV_KEY, KEY_A,
EV_KEY, KEY_B,
EV_REL, REL_X,
EV_REL, REL_Y,
-1);
li = litest_create_context();
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
ck_assert(device != NULL);
litest_disable_log_handler(li);
for (i = 0; i < 100; i++) {
libevdev_uinput_write_event(uinput, EV_REL, REL_X, 1);
libevdev_uinput_write_event(uinput, EV_REL, REL_Y, -1);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
}
litest_restore_log_handler(li);
libinput_unref(li);
libevdev_uinput_destroy(uinput);
}
END_TEST
START_TEST(device_touchpad_rel)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct libinput_device *device;
const struct input_absinfo abs[] = {
{ ABS_X, 0, 10, 0, 0, 10 },
{ ABS_Y, 0, 10, 0, 0, 10 },
{ ABS_MT_SLOT, 0, 2, 0, 0, 0 },
{ ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 10, 0, 0, 10 },
{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 10 },
{ -1, -1, -1, -1, -1, -1 }
};
int i;
uinput = litest_create_uinput_abs_device("test device",
NULL, abs,
EV_KEY, BTN_TOOL_FINGER,
EV_KEY, BTN_TOUCH,
EV_REL, REL_X,
EV_REL, REL_Y,
-1);
li = litest_create_context();
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
ck_assert(device != NULL);
for (i = 0; i < 100; i++) {
libevdev_uinput_write_event(uinput, EV_REL, REL_X, 1);
libevdev_uinput_write_event(uinput, EV_REL, REL_Y, -1);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
}
libinput_unref(li);
libevdev_uinput_destroy(uinput);
}
END_TEST
START_TEST(device_touch_rel)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct libinput_device *device;
const struct input_absinfo abs[] = {
{ ABS_X, 0, 10, 0, 0, 10 },
{ ABS_Y, 0, 10, 0, 0, 10 },
{ ABS_MT_SLOT, 0, 2, 0, 0, 0 },
{ ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 10, 0, 0, 10 },
{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 10 },
{ -1, -1, -1, -1, -1, -1 }
};
int i;
uinput = litest_create_uinput_abs_device("test device",
NULL, abs,
EV_KEY, BTN_TOUCH,
EV_REL, REL_X,
EV_REL, REL_Y,
-1);
li = litest_create_context();
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
ck_assert(device != NULL);
litest_disable_log_handler(li);
for (i = 0; i < 100; i++) {
libevdev_uinput_write_event(uinput, EV_REL, REL_X, 1);
libevdev_uinput_write_event(uinput, EV_REL, REL_Y, -1);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
}
litest_restore_log_handler(li);
libinput_unref(li);
libevdev_uinput_destroy(uinput);
}
END_TEST
START_TEST(device_abs_rel)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct libinput_device *device;
const struct input_absinfo abs[] = {
{ ABS_X, 0, 10, 0, 0, 10 },
{ ABS_Y, 0, 10, 0, 0, 10 },
{ -1, -1, -1, -1, -1, -1 }
};
int i;
uinput = litest_create_uinput_abs_device("test device",
NULL, abs,
EV_KEY, BTN_TOUCH,
EV_KEY, BTN_LEFT,
EV_REL, REL_X,
EV_REL, REL_Y,
-1);
li = litest_create_context();
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
ck_assert(device != NULL);
for (i = 0; i < 100; i++) {
libevdev_uinput_write_event(uinput, EV_REL, REL_X, 1);
libevdev_uinput_write_event(uinput, EV_REL, REL_Y, -1);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
}
libinput_unref(li);
libevdev_uinput_destroy(uinput);
}
END_TEST
void
litest_setup_tests(void)
{
@ -1094,4 +1244,9 @@ litest_setup_tests(void)
litest_add("device:udev tags", device_udev_tag_apple, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_synaptics_serial, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_wacom_tablet, LITEST_TABLET, LITEST_ANY);
litest_add_no_device("device:invalid rel events", device_nonpointer_rel);
litest_add_no_device("device:invalid rel events", device_touchpad_rel);
litest_add_no_device("device:invalid rel events", device_touch_rel);
litest_add_no_device("device:invalid rel events", device_abs_rel);
}

View file

@ -105,8 +105,8 @@ struct litest_device_interface {
struct input_event *tablet_proximity_out_events;
struct input_event *tablet_motion_events;
int min[2];
int max[2];
int min[2]; /* x/y axis minimum */
int max[2]; /* x/y axis maximum */
};
void litest_set_current_device(struct litest_device *device);

View file

@ -44,6 +44,8 @@
#include <sys/sendfile.h>
#include <sys/timerfd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <libudev.h>
#include "litest.h"
@ -788,9 +790,11 @@ litest_log_handler(struct libinput *libinput,
fprintf(stderr, "litest %s: ", priority);
vfprintf(stderr, format, args);
#if 0
if (strstr(format, "client bug: ") ||
strstr(format, "libinput bug: "))
litest_abort_msg("libinput bug triggered, aborting.\n");
#endif
}
static int
@ -1927,12 +1931,17 @@ litest_print_event(struct libinput_event *event)
break;
case LIBINPUT_EVENT_POINTER_AXIS:
p = libinput_event_get_pointer_event(event);
fprintf(stderr,
"vert %.f horiz %.2f",
libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL));
x = 0.0;
y = 0.0;
if (libinput_event_pointer_has_axis(p,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
y = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
if (libinput_event_pointer_has_axis(p,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
x = libinput_event_pointer_get_axis_value(p,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
fprintf(stderr, "vert %.f horiz %.2f", y, x);
break;
default:
break;

View file

@ -150,20 +150,17 @@ START_TEST(touchpad_2fg_scroll_slow_distance)
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
const struct input_absinfo *y;
double y_move;
double width, height;
double y_move = 100;
if (!litest_has_2fg_scroll(dev))
return;
/* We want to move > 5 mm. */
y = libevdev_get_abs_info(dev->evdev, ABS_Y);
if (y->resolution) {
y_move = 7.0 * y->resolution /
(y->maximum - y->minimum) * 100;
} else {
y_move = 20.0;
}
ck_assert_int_eq(libinput_device_get_size(dev->libinput_device,
&width,
&height), 0);
y_move = 100.0/height * 7;
litest_enable_2fg_scroll(dev);
litest_drain_events(li);
@ -350,6 +347,35 @@ START_TEST(touchpad_scroll_natural_2fg)
}
END_TEST
START_TEST(touchpad_scroll_natural_edge)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
litest_enable_edge_scroll(dev);
litest_drain_events(li);
libinput_device_config_scroll_set_natural_scroll_enabled(dev->libinput_device, 1);
litest_touch_down(dev, 0, 99, 20);
litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10, 0);
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, -4);
litest_assert_empty_queue(li);
litest_touch_down(dev, 0, 99, 80);
litest_touch_move_to(dev, 0, 99, 80, 99, 20, 10, 0);
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_assert_scroll(li, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, 4);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_edge_scroll)
{
struct litest_device *dev = litest_current_device();
@ -437,54 +463,60 @@ START_TEST(touchpad_edge_scroll_timeout)
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
double width = 0, height = 0;
int y_movement = 30; /* in percent of height */
int nevents = 0;
double mm; /* one mm in percent of the device */
/* account for different touchpad heights, let's move 100% on a 15mm
high touchpad, less on anything else. This number is picked at
random, we just want deltas less than 5.
*/
if (libinput_device_get_size(dev->libinput_device,
&width,
&height) != -1) {
y_movement = 100 * 15/height;
}
ck_assert_int_eq(libinput_device_get_size(dev->libinput_device,
&width,
&height), 0);
mm = 100.0/height;
/* timeout-based scrolling is disabled when software buttons are
* active, so switch to clickfinger. Not all test devices support
* that, hence the extra check. */
if (libinput_device_config_click_get_methods(dev->libinput_device) &
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
litest_enable_clickfinger(dev);
litest_drain_events(li);
litest_enable_edge_scroll(dev);
/* move 0.5mm, enough to load up the motion history, but less than
* the scroll threshold of 2mm */
litest_touch_down(dev, 0, 99, 20);
litest_touch_move_to(dev, 0, 99, 20, 99, 20 + mm/2, 8, 0);
libinput_dispatch(li);
litest_assert_empty_queue(li);
litest_timeout_edgescroll();
libinput_dispatch(li);
litest_touch_move_to(dev, 0, 99, 20, 99, 20 + y_movement, 100, 10);
litest_assert_empty_queue(li);
/* now move slowly up to the 2mm scroll threshold. we expect events */
litest_touch_move_to(dev, 0, 99, 20 + mm/2, 99, 20 + mm * 2, 20, 0);
litest_touch_up(dev, 0);
libinput_dispatch(li);
event = libinput_get_event(li);
ck_assert_notnull(event);
litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
while (libinput_next_event_type(li) != LIBINPUT_EVENT_NONE) {
double axisval;
ck_assert_int_eq(libinput_event_get_type(event),
LIBINPUT_EVENT_POINTER_AXIS);
ptrev = libinput_event_get_pointer_event(event);
axisval = libinput_event_pointer_get_axis_value(ptrev,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
ck_assert(axisval > 0.0);
/* this is to verify we test the right thing, if the value
is greater than scroll.threshold we triggered the wrong
condition */
ck_assert(axisval < 5.0);
while ((event = libinput_get_event(li))) {
double value;
ptrev = litest_is_axis_event(event,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
0);
value = libinput_event_pointer_get_axis_value(ptrev,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
ck_assert_double_lt(value, 5.0);
libinput_event_destroy(event);
event = libinput_get_event(li);
nevents++;
}
/* we sent 20 events but allow for some to be swallowed by rounding
* errors, the hysteresis, etc. */
ck_assert_int_ge(nevents, 10);
litest_assert_empty_queue(li);
libinput_event_destroy(event);
}
@ -3450,6 +3482,7 @@ litest_setup_tests(void)
litest_add("touchpad:scroll", touchpad_scroll_natural_defaults, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_scroll_natural_enable_config, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_scroll_natural_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_scroll_natural_edge, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_scroll_defaults, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_edge_scroll, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_edge_scroll_no_motion, LITEST_TOUCHPAD, LITEST_ANY);

View file

@ -39,3 +39,5 @@ event_gui_LDADD = ../src/libinput.la libshared.la $(CAIRO_LIBS) $(GTK_LIBS) $(LI
event_gui_CFLAGS = $(CAIRO_CFLAGS) $(GTK_CFLAGS) $(LIBUDEV_CFLAGS)
event_gui_LDFLAGS = -no-install
endif
EXTRA_DIST = make-ptraccel-graphs.sh

79
tools/make-ptraccel-graphs.sh Executable file
View file

@ -0,0 +1,79 @@
#!/bin/bash
tool=`dirname $0`/ptraccel-debug
gnuplot=/usr/bin/gnuplot
outfile="ptraccel-linear"
for speed in -1 -0.75 -0.5 -0.25 0 0.5 1; do
$tool --mode=accel --dpi=1000 --filter=linear --speed=$speed > $outfile-$speed.gnuplot
done
$gnuplot <<EOF
set terminal svg enhanced background rgb 'white'
set output "$outfile.svg"
set xlabel "speed in units/us"
set ylabel "accel factor"
set style data lines
set yrange [0:3]
set xrange [0:0.003]
plot "$outfile--1.gnuplot" using 1:2 title "-1.0", \
"$outfile--0.75.gnuplot" using 1:2 title "-0.75", \
"$outfile--0.5.gnuplot" using 1:2 title "-0.5", \
"$outfile--0.25.gnuplot" using 1:2 title "-0.25", \
"$outfile-0.gnuplot" using 1:2 title "0.0", \
"$outfile-0.5.gnuplot" using 1:2 title "0.5", \
"$outfile-1.gnuplot" using 1:2 title "1.0"
EOF
outfile="ptraccel-low-dpi"
for dpi in 200 400 800 1000; do
$tool --mode=accel --dpi=$dpi --filter=low-dpi > $outfile-$dpi.gnuplot
done
$gnuplot <<EOF
set terminal svg enhanced background rgb 'white'
set output "$outfile.svg"
set xlabel "speed in units/us"
set ylabel "accel factor"
set style data lines
set yrange [0:5]
set xrange [0:0.003]
plot "$outfile-200.gnuplot" using 1:2 title "200dpi", \
"$outfile-400.gnuplot" using 1:2 title "400dpi", \
"$outfile-800.gnuplot" using 1:2 title "800dpi", \
"$outfile-1000.gnuplot" using 1:2 title "1000dpi"
EOF
outfile="ptraccel-touchpad"
$tool --mode=accel --dpi=1000 --filter=linear > $outfile-mouse.gnuplot
$tool --mode=accel --dpi=1000 --filter=touchpad > $outfile-touchpad.gnuplot
$gnuplot <<EOF
set terminal svg enhanced background rgb 'white'
set output "$outfile.svg"
set xlabel "speed in units/us"
set ylabel "accel factor"
set style data lines
set yrange [0:3]
set xrange [0:0.003]
plot "$outfile-mouse.gnuplot" using 1:2 title "linear (mouse)", \
"$outfile-touchpad.gnuplot" using 1:2 title "touchpad"
EOF
outfile="ptraccel-trackpoint"
$tool --mode=accel --dpi=1000 --filter=linear > $outfile-mouse.gnuplot
for constaccel in 1 2 3; do
dpi=$((1000/$constaccel))
$tool --mode=accel --dpi=$dpi --filter=trackpoint > $outfile-trackpoint-$constaccel.gnuplot
done
$gnuplot <<EOF
set terminal svg enhanced background rgb 'white'
set output "$outfile.svg"
set xlabel "speed in units/us"
set ylabel "accel factor"
set style data lines
set yrange [0:5]
set xrange [0:0.003]
plot "$outfile-mouse.gnuplot" using 1:2 title "linear (mouse)", \
"$outfile-trackpoint-1.gnuplot" using 1:2 title "const accel 1", \
"$outfile-trackpoint-2.gnuplot" using 1:2 title "const accel 2", \
"$outfile-trackpoint-3.gnuplot" using 1:2 title "const accel 3"
EOF

View file

@ -138,7 +138,7 @@ print_ptraccel_sequence(struct motion_filter *filter,
}
static void
print_accel_func(struct motion_filter *filter)
print_accel_func(struct motion_filter *filter, accel_profile_func_t profile)
{
double vel;
@ -147,12 +147,9 @@ print_accel_func(struct motion_filter *filter)
printf("# set ylabel \"raw accel factor\"\n");
printf("# set style data lines\n");
printf("# plot \"gnuplot.data\" using 1:2\n");
for (vel = 0.0; vel < 3.0; vel += .0001) {
double result = pointer_accel_profile_linear(filter,
NULL,
vel,
0 /* time */);
printf("%.4f\t%.4f\n", vel, result);
for (vel = 0.0; vel < 0.003; vel += 0.0000001) {
double result = profile(filter, NULL, vel, 0 /* time */);
printf("%.8f\t%.4f\n", vel, result);
}
}
@ -171,6 +168,12 @@ 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"
"--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"
" touchpad ... the touchpad motion filter\n"
" x230 ... custom filter for the Lenovo x230 touchpad\n"
" trackpoint... trackpoint motion filter\n"
"\n"
"If extra arguments are present and mode is not given, mode defaults to 'sequence'\n"
"and the arguments are interpreted as sequence of delta x coordinates\n"
@ -195,6 +198,8 @@ main(int argc, char **argv)
double custom_deltas[1024];
double speed = 0.0;
int dpi = 1000;
const char *filter_type = "linear";
accel_profile_func_t profile = NULL;
enum {
OPT_MODE = 1,
@ -203,6 +208,7 @@ main(int argc, char **argv)
OPT_STEP,
OPT_SPEED,
OPT_DPI,
OPT_FILTER,
};
while (1) {
@ -215,6 +221,7 @@ main(int argc, char **argv)
{"step", 1, 0, OPT_STEP },
{"speed", 1, 0, OPT_SPEED },
{"dpi", 1, 0, OPT_DPI },
{"filter", 1, 0, OPT_FILTER},
{0, 0, 0, 0}
};
@ -265,6 +272,9 @@ main(int argc, char **argv)
case OPT_DPI:
dpi = strtod(optarg, NULL);
break;
case OPT_FILTER:
filter_type = optarg;
break;
default:
usage();
exit(1);
@ -272,8 +282,26 @@ main(int argc, char **argv)
}
}
filter = create_pointer_accelerator_filter(pointer_accel_profile_linear,
dpi);
if (streq(filter_type, "linear")) {
filter = create_pointer_accelerator_filter_linear(dpi);
profile = pointer_accel_profile_linear;
} else if (streq(filter_type, "low-dpi")) {
filter = create_pointer_accelerator_filter_linear_low_dpi(dpi);
profile = pointer_accel_profile_linear_low_dpi;
} else if (streq(filter_type, "touchpad")) {
filter = create_pointer_accelerator_filter_touchpad(dpi);
profile = touchpad_accel_profile_linear;
} else if (streq(filter_type, "x230")) {
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;
} else {
fprintf(stderr, "Invalid filter type %s\n", filter_type);
return 1;
}
assert(filter != NULL);
filter_set_speed(filter, speed);
@ -297,7 +325,7 @@ main(int argc, char **argv)
}
if (print_accel)
print_accel_func(filter);
print_accel_func(filter, profile);
else if (print_delta)
print_ptraccel_deltas(filter, step);
else if (print_motion)

View file

@ -166,131 +166,131 @@ tools_parse_args(int argc, char **argv, struct tools_context *context)
break;
switch(c) {
case 'h':
case OPT_HELP:
tools_usage();
exit(0);
case OPT_DEVICE:
options->backend = BACKEND_DEVICE;
if (!optarg) {
tools_usage();
return 1;
}
options->device = optarg;
break;
case OPT_UDEV:
options->backend = BACKEND_UDEV;
if (optarg)
options->seat = optarg;
break;
case OPT_GRAB:
options->grab = 1;
break;
case OPT_VERBOSE:
options->verbose = 1;
break;
case OPT_TAP_ENABLE:
options->tapping = 1;
break;
case OPT_TAP_DISABLE:
options->tapping = 0;
break;
case OPT_DRAG_LOCK_ENABLE:
options->drag_lock = 1;
break;
case OPT_DRAG_LOCK_DISABLE:
options->drag_lock = 0;
break;
case OPT_NATURAL_SCROLL_ENABLE:
options->natural_scroll = 1;
break;
case OPT_NATURAL_SCROLL_DISABLE:
options->natural_scroll = 0;
break;
case OPT_LEFT_HANDED_ENABLE:
options->left_handed = 1;
break;
case OPT_LEFT_HANDED_DISABLE:
options->left_handed = 0;
break;
case OPT_MIDDLEBUTTON_ENABLE:
options->middlebutton = 1;
break;
case OPT_MIDDLEBUTTON_DISABLE:
options->middlebutton = 0;
break;
case OPT_DWT_ENABLE:
options->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
break;
case OPT_DWT_DISABLE:
options->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
break;
case OPT_CLICK_METHOD:
if (!optarg) {
tools_usage();
return 1;
}
if (streq(optarg, "none")) {
options->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_NONE;
} else if (streq(optarg, "clickfinger")) {
options->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
} else if (streq(optarg, "buttonareas")) {
options->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
} else {
tools_usage();
return 1;
}
break;
case OPT_SCROLL_METHOD:
if (!optarg) {
tools_usage();
return 1;
}
if (streq(optarg, "none")) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
} else if (streq(optarg, "twofinger")) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_2FG;
} else if (streq(optarg, "edge")) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_EDGE;
} else if (streq(optarg, "button")) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
} else {
tools_usage();
return 1;
}
break;
case OPT_SCROLL_BUTTON:
if (!optarg) {
tools_usage();
return 1;
}
options->scroll_button =
libevdev_event_code_from_name(EV_KEY,
optarg);
if (options->scroll_button == -1) {
fprintf(stderr,
"Invalid button %s\n",
optarg);
return 1;
}
break;
case OPT_SPEED:
if (!optarg) {
tools_usage();
return 1;
}
options->speed = atof(optarg);
break;
default:
case 'h':
case OPT_HELP:
tools_usage();
exit(0);
case OPT_DEVICE:
options->backend = BACKEND_DEVICE;
if (!optarg) {
tools_usage();
return 1;
}
options->device = optarg;
break;
case OPT_UDEV:
options->backend = BACKEND_UDEV;
if (optarg)
options->seat = optarg;
break;
case OPT_GRAB:
options->grab = 1;
break;
case OPT_VERBOSE:
options->verbose = 1;
break;
case OPT_TAP_ENABLE:
options->tapping = 1;
break;
case OPT_TAP_DISABLE:
options->tapping = 0;
break;
case OPT_DRAG_LOCK_ENABLE:
options->drag_lock = 1;
break;
case OPT_DRAG_LOCK_DISABLE:
options->drag_lock = 0;
break;
case OPT_NATURAL_SCROLL_ENABLE:
options->natural_scroll = 1;
break;
case OPT_NATURAL_SCROLL_DISABLE:
options->natural_scroll = 0;
break;
case OPT_LEFT_HANDED_ENABLE:
options->left_handed = 1;
break;
case OPT_LEFT_HANDED_DISABLE:
options->left_handed = 0;
break;
case OPT_MIDDLEBUTTON_ENABLE:
options->middlebutton = 1;
break;
case OPT_MIDDLEBUTTON_DISABLE:
options->middlebutton = 0;
break;
case OPT_DWT_ENABLE:
options->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
break;
case OPT_DWT_DISABLE:
options->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
break;
case OPT_CLICK_METHOD:
if (!optarg) {
tools_usage();
return 1;
}
if (streq(optarg, "none")) {
options->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_NONE;
} else if (streq(optarg, "clickfinger")) {
options->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
} else if (streq(optarg, "buttonareas")) {
options->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
} else {
tools_usage();
return 1;
}
break;
case OPT_SCROLL_METHOD:
if (!optarg) {
tools_usage();
return 1;
}
if (streq(optarg, "none")) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
} else if (streq(optarg, "twofinger")) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_2FG;
} else if (streq(optarg, "edge")) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_EDGE;
} else if (streq(optarg, "button")) {
options->scroll_method =
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
} else {
tools_usage();
return 1;
}
break;
case OPT_SCROLL_BUTTON:
if (!optarg) {
tools_usage();
return 1;
}
options->scroll_button =
libevdev_event_code_from_name(EV_KEY,
optarg);
if (options->scroll_button == -1) {
fprintf(stderr,
"Invalid button %s\n",
optarg);
return 1;
}
break;
case OPT_SPEED:
if (!optarg) {
tools_usage();
return 1;
}
options->speed = atof(optarg);
break;
default:
tools_usage();
return 1;
}
}

View file

@ -127,7 +127,7 @@ int main(int argc, char **argv)
if (!device)
goto out;
if (udev_device_get_property_value(device, "ID_INPUT_TOUCHPAD"))
if (prop_value(device, "ID_INPUT_TOUCHPAD"))
handle_touchpad(device);
rc = 0;