2018-04-10 14:21:26 +10:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2006-2009 Simon Thum
|
|
|
|
|
* Copyright © 2012 Jonas Ådahl
|
|
|
|
|
* Copyright © 2014-2015 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
#include "filter.h"
|
|
|
|
|
#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
|
|
|
|
|
*/
|
2018-03-12 15:24:07 +10:00
|
|
|
#define TP_MAGIC_SLOWDOWN 0.2968 /* unitless factor */
|
2018-04-10 14:21:26 +10:00
|
|
|
|
2018-04-11 12:06:06 +10:00
|
|
|
struct touchpad_accelerator {
|
|
|
|
|
struct motion_filter base;
|
|
|
|
|
|
|
|
|
|
accel_profile_func_t profile;
|
|
|
|
|
|
|
|
|
|
double velocity; /* units/us */
|
|
|
|
|
double last_velocity; /* units/us */
|
|
|
|
|
|
|
|
|
|
struct pointer_trackers trackers;
|
|
|
|
|
|
2021-03-16 14:28:31 +10:00
|
|
|
double threshold; /* mm/s */
|
2018-04-11 12:06:06 +10:00
|
|
|
double accel; /* unitless factor */
|
|
|
|
|
|
|
|
|
|
int dpi;
|
2018-03-12 15:24:07 +10:00
|
|
|
|
|
|
|
|
double speed_factor; /* factor based on speed setting */
|
2018-04-11 12:06:06 +10:00
|
|
|
};
|
|
|
|
|
|
2018-04-10 14:21:26 +10:00
|
|
|
/**
|
|
|
|
|
* Calculate the acceleration factor for the given delta with the timestamp.
|
|
|
|
|
*
|
|
|
|
|
* @param accel The acceleration filter
|
|
|
|
|
* @param unaccelerated The raw delta in the device's dpi
|
|
|
|
|
* @param data Caller-specific data
|
|
|
|
|
* @param time Current time in µs
|
|
|
|
|
*
|
|
|
|
|
* @return A unitless acceleration factor, to be applied to the delta
|
|
|
|
|
*/
|
|
|
|
|
static inline double
|
2018-04-11 12:06:06 +10:00
|
|
|
calculate_acceleration_factor(struct touchpad_accelerator *accel,
|
2018-04-10 14:21:26 +10:00
|
|
|
const struct device_float_coords *unaccelerated,
|
|
|
|
|
void *data,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
double velocity; /* units/us in device-native dpi*/
|
|
|
|
|
double accel_factor;
|
|
|
|
|
|
2018-04-11 11:49:15 +10:00
|
|
|
trackers_feed(&accel->trackers, unaccelerated, time);
|
|
|
|
|
velocity = trackers_velocity(&accel->trackers, time);
|
2018-04-10 14:21:26 +10:00
|
|
|
accel_factor = calculate_acceleration_simpsons(&accel->base,
|
|
|
|
|
accel->profile,
|
|
|
|
|
data,
|
|
|
|
|
velocity,
|
|
|
|
|
accel->last_velocity,
|
|
|
|
|
time);
|
|
|
|
|
accel->last_velocity = velocity;
|
|
|
|
|
|
|
|
|
|
return accel_factor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct normalized_coords
|
2022-09-02 14:34:56 +10:00
|
|
|
accelerator_filter_touchpad(struct motion_filter *filter,
|
|
|
|
|
const struct device_float_coords *unaccelerated,
|
|
|
|
|
void *data, uint64_t time)
|
2018-04-10 14:21:26 +10:00
|
|
|
{
|
2018-04-11 12:06:06 +10:00
|
|
|
struct touchpad_accelerator *accel =
|
|
|
|
|
(struct touchpad_accelerator *) filter;
|
2018-04-10 14:21:26 +10:00
|
|
|
|
|
|
|
|
/* Accelerate for device units, normalize afterwards */
|
2022-09-02 14:34:56 +10:00
|
|
|
double accel_factor = calculate_acceleration_factor(accel,
|
|
|
|
|
unaccelerated,
|
|
|
|
|
data,
|
|
|
|
|
time);
|
|
|
|
|
const struct device_float_coords accelerated = {
|
|
|
|
|
.x = unaccelerated->x * accel_factor,
|
|
|
|
|
.y = unaccelerated->y * accel_factor,
|
|
|
|
|
};
|
|
|
|
|
|
2018-04-10 14:21:26 +10:00
|
|
|
return normalize_for_dpi(&accelerated, accel->dpi);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-12 15:24:07 +10:00
|
|
|
/* Maps the [-1, 1] speed setting into a constant acceleration
|
|
|
|
|
* range. This isn't a linear scale, we keep 0 as the 'optimized'
|
2023-02-08 10:56:48 +00:00
|
|
|
* mid-point and scale down to 0.05 for setting -1 and up to 5 for
|
2018-03-12 15:24:07 +10:00
|
|
|
* setting 1. On the premise that if you want a faster cursor, it
|
|
|
|
|
* doesn't matter as much whether you have 0.56789 or 0.56790,
|
|
|
|
|
* but for lower settings it does because you may lose movements.
|
|
|
|
|
* *shrug*.
|
|
|
|
|
*/
|
|
|
|
|
static inline double
|
|
|
|
|
speed_factor(double s)
|
|
|
|
|
{
|
2023-02-08 10:56:48 +00:00
|
|
|
return pow(s + 1, 2.38) * 0.95 + 0.05;
|
2018-03-12 15:24:07 +10:00
|
|
|
}
|
|
|
|
|
|
2018-04-10 14:21:26 +10:00
|
|
|
static bool
|
|
|
|
|
touchpad_accelerator_set_speed(struct motion_filter *filter,
|
|
|
|
|
double speed_adjustment)
|
|
|
|
|
{
|
2018-04-11 12:06:06 +10:00
|
|
|
struct touchpad_accelerator *accel_filter =
|
|
|
|
|
(struct touchpad_accelerator *)filter;
|
2018-04-10 14:21:26 +10:00
|
|
|
|
|
|
|
|
assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
|
|
|
|
|
|
|
|
|
|
filter->speed_adjustment = speed_adjustment;
|
2018-03-12 15:24:07 +10:00
|
|
|
accel_filter->speed_factor = speed_factor(speed_adjustment);
|
2018-04-10 14:21:26 +10:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct normalized_coords
|
|
|
|
|
touchpad_constant_filter(struct motion_filter *filter,
|
|
|
|
|
const struct device_float_coords *unaccelerated,
|
|
|
|
|
void *data, uint64_t time)
|
|
|
|
|
{
|
2018-04-11 12:06:06 +10:00
|
|
|
struct touchpad_accelerator *accel =
|
|
|
|
|
(struct touchpad_accelerator *)filter;
|
2018-04-10 14:21:26 +10:00
|
|
|
struct normalized_coords normalized;
|
2019-10-14 11:23:30 +10:00
|
|
|
/* We need to use the same baseline here as the accelerated code,
|
|
|
|
|
* otherwise our unaccelerated speed is different to the accelerated
|
|
|
|
|
* speed on the plateau.
|
|
|
|
|
*
|
|
|
|
|
* This is a hack, the baseline should be incorporated into the
|
|
|
|
|
* TP_MAGIC_SLOWDOWN so we only have one number here but meanwhile
|
|
|
|
|
* this will do.
|
|
|
|
|
*/
|
|
|
|
|
const double baseline = 0.9;
|
2018-04-10 14:21:26 +10:00
|
|
|
|
|
|
|
|
normalized = normalize_for_dpi(unaccelerated, accel->dpi);
|
2019-10-14 11:23:30 +10:00
|
|
|
normalized.x = baseline * TP_MAGIC_SLOWDOWN * normalized.x;
|
|
|
|
|
normalized.y = baseline * TP_MAGIC_SLOWDOWN * normalized.y;
|
2018-04-10 14:21:26 +10:00
|
|
|
|
|
|
|
|
return normalized;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
touchpad_accelerator_restart(struct motion_filter *filter,
|
|
|
|
|
void *data,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
2018-04-11 12:06:06 +10:00
|
|
|
struct touchpad_accelerator *accel =
|
|
|
|
|
(struct touchpad_accelerator *) filter;
|
2018-04-10 14:21:26 +10:00
|
|
|
|
2018-04-11 11:49:15 +10:00
|
|
|
trackers_reset(&accel->trackers, time);
|
2018-04-10 14:21:26 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
touchpad_accelerator_destroy(struct motion_filter *filter)
|
|
|
|
|
{
|
2018-04-11 12:06:06 +10:00
|
|
|
struct touchpad_accelerator *accel =
|
|
|
|
|
(struct touchpad_accelerator *) filter;
|
2018-04-10 14:21:26 +10:00
|
|
|
|
2018-04-11 11:49:15 +10:00
|
|
|
trackers_free(&accel->trackers);
|
2018-04-10 14:21:26 +10:00
|
|
|
free(accel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
|
touchpad_accel_profile_linear(struct motion_filter *filter,
|
|
|
|
|
void *data,
|
|
|
|
|
double speed_in, /* in device units/µs */
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
2018-04-11 12:06:06 +10:00
|
|
|
struct touchpad_accelerator *accel_filter =
|
|
|
|
|
(struct touchpad_accelerator *)filter;
|
2021-03-16 14:28:31 +10:00
|
|
|
const double threshold = accel_filter->threshold; /* mm/s */
|
2018-03-12 15:24:07 +10:00
|
|
|
const double baseline = 0.9;
|
2018-04-10 14:21:26 +10:00
|
|
|
double factor; /* unitless */
|
|
|
|
|
|
|
|
|
|
/* Convert to mm/s because that's something one can understand */
|
|
|
|
|
speed_in = v_us2s(speed_in) * 25.4/accel_filter->dpi;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
accel
|
|
|
|
|
factor
|
2018-03-12 15:24:07 +10:00
|
|
|
^ ______
|
|
|
|
|
| )
|
|
|
|
|
| _____)
|
2018-04-10 14:21:26 +10:00
|
|
|
| /
|
|
|
|
|
|/
|
|
|
|
|
+-------------> speed in
|
|
|
|
|
|
2018-03-12 15:24:07 +10:00
|
|
|
Except the second incline is a curve, but well, asciiart.
|
|
|
|
|
|
|
|
|
|
The first incline is a linear function in the form
|
2018-04-10 14:21:26 +10:00
|
|
|
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 the lower threshold, we decelerate, down to 30%
|
|
|
|
|
of input speed.
|
|
|
|
|
hence 1 = a * 7 + 0.3
|
|
|
|
|
0.7 = a * 7 => a := 0.1
|
|
|
|
|
deceleration function is thus:
|
|
|
|
|
y = 0.1x + 0.3
|
|
|
|
|
|
2018-03-12 15:24:07 +10:00
|
|
|
The first plateau is the baseline.
|
|
|
|
|
|
|
|
|
|
The second incline is a curve up, based on magic numbers
|
|
|
|
|
obtained by trial-and-error.
|
|
|
|
|
|
|
|
|
|
Above the second incline we have another plateau because
|
|
|
|
|
by then you're moving so fast that extra acceleration doesn't
|
|
|
|
|
help.
|
|
|
|
|
|
2018-04-10 14:21:26 +10:00
|
|
|
Note:
|
|
|
|
|
* The minimum threshold is a result of trial-and-error and
|
2018-03-12 15:24:07 +10:00
|
|
|
has no other special meaning.
|
2018-04-10 14:21:26 +10:00
|
|
|
* 0.3 is chosen simply because it is above the Nyquist frequency
|
|
|
|
|
for subpixel motion within a pixel.
|
|
|
|
|
*/
|
2018-03-12 15:24:07 +10:00
|
|
|
|
2018-04-10 14:21:26 +10:00
|
|
|
if (speed_in < 7.0) {
|
2018-03-12 15:24:07 +10:00
|
|
|
factor = min(baseline, 0.1 * speed_in + 0.3);
|
2018-04-10 14:21:26 +10:00
|
|
|
/* up to the threshold, we keep factor 1, i.e. 1:1 movement */
|
|
|
|
|
} else if (speed_in < threshold) {
|
2018-03-12 15:24:07 +10:00
|
|
|
factor = baseline;
|
2018-04-10 14:21:26 +10:00
|
|
|
} else {
|
|
|
|
|
|
2018-03-12 15:24:07 +10:00
|
|
|
/* Acceleration function above the threshold is a curve up
|
|
|
|
|
to four times the threshold, because why not.
|
2018-04-10 14:21:26 +10:00
|
|
|
|
2018-03-12 15:24:07 +10:00
|
|
|
Don't assume anything about the specific numbers though, this was
|
|
|
|
|
all just trial and error by tweaking numbers here and there, then
|
|
|
|
|
the formula was optimized doing basic maths.
|
|
|
|
|
|
|
|
|
|
You could replace this with some other random formula that gives
|
|
|
|
|
the same numbers and it would be just as correct.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
const double upper_threshold = threshold * 4.0;
|
|
|
|
|
speed_in = min(speed_in, upper_threshold);
|
|
|
|
|
|
|
|
|
|
factor = 0.0025 * (speed_in/threshold) * (speed_in - threshold) + baseline;
|
|
|
|
|
}
|
2018-04-10 14:21:26 +10:00
|
|
|
|
2018-03-12 15:24:07 +10:00
|
|
|
factor *= accel_filter->speed_factor;
|
2018-04-10 14:21:26 +10:00
|
|
|
return factor * TP_MAGIC_SLOWDOWN;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-05 11:33:20 +10:00
|
|
|
static const struct motion_filter_interface accelerator_interface_touchpad = {
|
2018-04-10 14:21:26 +10:00
|
|
|
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
2022-09-02 14:34:56 +10:00
|
|
|
.filter = accelerator_filter_touchpad,
|
2018-04-10 14:21:26 +10:00
|
|
|
.filter_constant = touchpad_constant_filter,
|
2023-02-18 21:12:13 +02:00
|
|
|
.filter_scroll = touchpad_constant_filter,
|
2018-04-10 14:21:26 +10:00
|
|
|
.restart = touchpad_accelerator_restart,
|
|
|
|
|
.destroy = touchpad_accelerator_destroy,
|
|
|
|
|
.set_speed = touchpad_accelerator_set_speed,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct motion_filter *
|
|
|
|
|
create_pointer_accelerator_filter_touchpad(int dpi,
|
|
|
|
|
uint64_t event_delta_smooth_threshold,
|
2018-08-17 15:12:58 +02:00
|
|
|
uint64_t event_delta_smooth_value,
|
|
|
|
|
bool use_velocity_averaging)
|
2018-04-10 14:21:26 +10:00
|
|
|
{
|
2018-04-11 12:06:06 +10:00
|
|
|
struct touchpad_accelerator *filter;
|
2018-04-10 14:21:26 +10:00
|
|
|
|
2018-04-11 11:14:56 +10:00
|
|
|
filter = zalloc(sizeof *filter);
|
|
|
|
|
filter->last_velocity = 0.0;
|
|
|
|
|
|
2018-08-17 15:12:58 +02:00
|
|
|
trackers_init(&filter->trackers, use_velocity_averaging ? 16 : 2);
|
2018-04-11 11:14:56 +10:00
|
|
|
|
2018-03-12 15:24:07 +10:00
|
|
|
filter->threshold = 130;
|
2018-04-11 11:14:56 +10:00
|
|
|
filter->dpi = dpi;
|
2018-04-10 14:21:26 +10:00
|
|
|
|
|
|
|
|
filter->base.interface = &accelerator_interface_touchpad;
|
|
|
|
|
filter->profile = touchpad_accel_profile_linear;
|
2022-11-22 11:34:06 +10:00
|
|
|
filter->trackers.smoothener = pointer_delta_smoothener_create(event_delta_smooth_threshold, event_delta_smooth_value);
|
2018-04-10 14:21:26 +10:00
|
|
|
|
|
|
|
|
return &filter->base;
|
|
|
|
|
}
|