mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-01-08 05:50:13 +01:00
Merge branch 'touchpad-pointer-accel-revamp'
This commit is contained in:
commit
acbbe882a2
11 changed files with 743 additions and 1770 deletions
|
|
@ -96,10 +96,11 @@ 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.
|
||||
Touchpad pointer acceleration uses the same approach as 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"
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 42 KiB |
|
|
@ -51,11 +51,17 @@ tp_filter_motion(struct tp_dispatch *tp,
|
|||
const struct normalized_coords *unaccelerated,
|
||||
uint64_t time)
|
||||
{
|
||||
struct device_float_coords raw;
|
||||
|
||||
if (normalized_is_zero(*unaccelerated))
|
||||
return *unaccelerated;
|
||||
|
||||
/* Temporary solution only: convert back to raw coordinates, but
|
||||
* make sure we're on the same resolution for both axes */
|
||||
raw = tp_unnormalize_for_xaxis(tp, *unaccelerated);
|
||||
|
||||
return filter_dispatch(tp->device->pointer.filter,
|
||||
unaccelerated, tp, time);
|
||||
&raw, tp, time);
|
||||
}
|
||||
|
||||
struct normalized_coords
|
||||
|
|
@ -63,11 +69,17 @@ tp_filter_motion_unaccelerated(struct tp_dispatch *tp,
|
|||
const struct normalized_coords *unaccelerated,
|
||||
uint64_t time)
|
||||
{
|
||||
struct device_float_coords raw;
|
||||
|
||||
if (normalized_is_zero(*unaccelerated))
|
||||
return *unaccelerated;
|
||||
|
||||
/* Temporary solution only: convert back to raw coordinates, but
|
||||
* make sure we're on the same resolution for both axes */
|
||||
raw = tp_unnormalize_for_xaxis(tp, *unaccelerated);
|
||||
|
||||
return filter_dispatch_constant(tp->device->pointer.filter,
|
||||
unaccelerated, tp, time);
|
||||
&raw, tp, time);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
|
@ -2266,6 +2278,10 @@ tp_init(struct tp_dispatch *tp,
|
|||
EV_ABS,
|
||||
ABS_MT_DISTANCE);
|
||||
|
||||
/* Set the dpi to that of the x axis, because that's what we normalize
|
||||
to when needed*/
|
||||
device->dpi = device->abs.absinfo_x->resolution * 25.4;
|
||||
|
||||
tp_init_hysteresis(tp);
|
||||
|
||||
if (!tp_init_accel(tp))
|
||||
|
|
|
|||
|
|
@ -392,13 +392,14 @@ tool_process_delta(struct libinput_tablet_tool *tool,
|
|||
const struct device_coords *delta,
|
||||
uint64_t time)
|
||||
{
|
||||
struct normalized_coords accel;
|
||||
const struct normalized_coords zero = { 0.0, 0.0 };
|
||||
struct device_float_coords accel;
|
||||
|
||||
accel.x = 1.0 * delta->x;
|
||||
accel.y = 1.0 * delta->y;
|
||||
|
||||
if (normalized_is_zero(accel))
|
||||
return accel;
|
||||
if (device_float_is_zero(accel))
|
||||
return zero;
|
||||
|
||||
return filter_dispatch(device->pointer.filter,
|
||||
&accel,
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@ fallback_flush_relative_motion(struct fallback_dispatch *dispatch,
|
|||
if (device->pointer.filter) {
|
||||
/* Apply pointer acceleration. */
|
||||
accel = filter_dispatch(device->pointer.filter,
|
||||
&unaccel,
|
||||
&raw,
|
||||
device,
|
||||
time);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@ struct motion_filter_interface {
|
|||
enum libinput_config_accel_profile type;
|
||||
struct normalized_coords (*filter)(
|
||||
struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time);
|
||||
struct normalized_coords (*filter_constant)(
|
||||
struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time);
|
||||
void (*restart)(struct motion_filter *filter,
|
||||
void *data,
|
||||
|
|
|
|||
355
src/filter.c
355
src/filter.c
|
|
@ -40,7 +40,7 @@
|
|||
* 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 */
|
||||
#define TP_MAGIC_SLOWDOWN 0.37 /* unitless factor */
|
||||
|
||||
/* Convert speed/velocity from units/us to units/ms */
|
||||
static inline double
|
||||
|
|
@ -49,6 +49,12 @@ v_us2ms(double units_per_us)
|
|||
return units_per_us * 1000.0;
|
||||
}
|
||||
|
||||
static inline double
|
||||
v_us2s(double units_per_us)
|
||||
{
|
||||
return units_per_us * 1000000.0;
|
||||
}
|
||||
|
||||
/* Convert speed/velocity from units/ms to units/us */
|
||||
static inline double
|
||||
v_ms2us(double units_per_ms)
|
||||
|
|
@ -56,9 +62,20 @@ v_ms2us(double units_per_ms)
|
|||
return units_per_ms/1000.0;
|
||||
}
|
||||
|
||||
static inline struct normalized_coords
|
||||
normalize_for_dpi(const struct device_float_coords *coords, int dpi)
|
||||
{
|
||||
struct normalized_coords norm;
|
||||
|
||||
norm.x = coords->x * DEFAULT_MOUSE_DPI/dpi;
|
||||
norm.y = coords->y * DEFAULT_MOUSE_DPI/dpi;
|
||||
|
||||
return norm;
|
||||
}
|
||||
|
||||
struct normalized_coords
|
||||
filter_dispatch(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time)
|
||||
{
|
||||
return filter->interface->filter(filter, unaccelerated, data, time);
|
||||
|
|
@ -66,7 +83,7 @@ filter_dispatch(struct motion_filter *filter,
|
|||
|
||||
struct normalized_coords
|
||||
filter_dispatch_constant(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time)
|
||||
{
|
||||
return filter->interface->filter_constant(filter, unaccelerated, data, time);
|
||||
|
|
@ -117,6 +134,12 @@ filter_get_type(struct motion_filter *filter)
|
|||
#define DEFAULT_ACCELERATION 2.0 /* unitless factor */
|
||||
#define DEFAULT_INCLINE 1.1 /* unitless factor */
|
||||
|
||||
/* Touchpad acceleration */
|
||||
#define TOUCHPAD_DEFAULT_THRESHOLD 254 /* mm/s */
|
||||
#define TOUCHPAD_THRESHOLD_RANGE 184 /* mm/s */
|
||||
#define TOUCHPAD_ACCELERATION 9.0 /* unitless factor */
|
||||
#define TOUCHPAD_INCLINE 0.011 /* 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 */
|
||||
|
|
@ -153,14 +176,14 @@ struct pointer_accelerator {
|
|||
double accel; /* unitless factor */
|
||||
double incline; /* incline of the function */
|
||||
|
||||
double dpi_factor;
|
||||
int dpi;
|
||||
};
|
||||
|
||||
struct pointer_accelerator_flat {
|
||||
struct motion_filter base;
|
||||
|
||||
double factor;
|
||||
double dpi_factor;
|
||||
int dpi;
|
||||
};
|
||||
|
||||
struct tablet_accelerator_flat {
|
||||
|
|
@ -174,7 +197,7 @@ struct tablet_accelerator_flat {
|
|||
|
||||
static void
|
||||
feed_trackers(struct pointer_accelerator *accel,
|
||||
const struct normalized_coords *delta,
|
||||
const struct device_float_coords *delta,
|
||||
uint64_t time)
|
||||
{
|
||||
int i, current;
|
||||
|
|
@ -191,7 +214,7 @@ feed_trackers(struct pointer_accelerator *accel,
|
|||
trackers[current].delta.x = 0.0;
|
||||
trackers[current].delta.y = 0.0;
|
||||
trackers[current].time = time;
|
||||
trackers[current].dir = normalized_get_direction(*delta);
|
||||
trackers[current].dir = device_float_get_direction(*delta);
|
||||
}
|
||||
|
||||
static struct pointer_tracker *
|
||||
|
|
@ -290,6 +313,16 @@ calculate_velocity(struct pointer_accelerator *accel, uint64_t time)
|
|||
return result; /* units/us */
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the acceleration profile to the given velocity.
|
||||
*
|
||||
* @param accel The acceleration filter
|
||||
* @param data Caller-specific data
|
||||
* @param velocity Velocity in device-units per µs
|
||||
* @param time Current time in µs
|
||||
*
|
||||
* @return A unitless acceleration factor, to be applied to the delta
|
||||
*/
|
||||
static double
|
||||
acceleration_profile(struct pointer_accelerator *accel,
|
||||
void *data, double velocity, uint64_t time)
|
||||
|
|
@ -297,6 +330,18 @@ acceleration_profile(struct pointer_accelerator *accel,
|
|||
return accel->profile(&accel->base, data, velocity, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the acceleration factor for our current velocity, averaging
|
||||
* between our current and the most recent velocity to smoothen out changes.
|
||||
*
|
||||
* @param accel The acceleration filter
|
||||
* @param data Caller-specific data
|
||||
* @param velocity Velocity in device-units per µs
|
||||
* @param last_velocity Previous velocity in device-units per µs
|
||||
* @param time Current time in µs
|
||||
*
|
||||
* @return A unitless acceleration factor, to be applied to the delta
|
||||
*/
|
||||
static double
|
||||
calculate_acceleration(struct pointer_accelerator *accel,
|
||||
void *data,
|
||||
|
|
@ -320,13 +365,23 @@ calculate_acceleration(struct pointer_accelerator *accel,
|
|||
return factor; /* unitless factor */
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
calculate_acceleration_factor(struct pointer_accelerator *accel,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data,
|
||||
uint64_t time)
|
||||
{
|
||||
double velocity; /* units/us */
|
||||
double velocity; /* units/us in device-native dpi*/
|
||||
double accel_factor;
|
||||
|
||||
feed_trackers(accel, unaccelerated, time);
|
||||
|
|
@ -341,10 +396,22 @@ calculate_acceleration_factor(struct pointer_accelerator *accel,
|
|||
return accel_factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic filter that calculates the acceleration factor and applies it to
|
||||
* the coordinates.
|
||||
*
|
||||
* @param filter 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 An accelerated tuple of coordinates representing normalized
|
||||
* motion
|
||||
*/
|
||||
static struct normalized_coords
|
||||
accelerator_filter(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
void *data, uint64_t time)
|
||||
accelerator_filter_generic(struct motion_filter *filter,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time)
|
||||
{
|
||||
struct pointer_accelerator *accel =
|
||||
(struct pointer_accelerator *) filter;
|
||||
|
|
@ -362,85 +429,54 @@ accelerator_filter(struct motion_filter *filter,
|
|||
return accelerated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic filter that does nothing beyond converting from the device's
|
||||
* native dpi into normalized coordinates.
|
||||
*
|
||||
* @param filter 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 An accelerated tuple of coordinates representing normalized
|
||||
* motion
|
||||
*/
|
||||
static struct normalized_coords
|
||||
accelerator_filter_noop(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_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 accel_value; /* unitless factor */
|
||||
struct normalized_coords accelerated;
|
||||
struct normalized_coords unnormalized;
|
||||
double dpi_factor = accel->dpi_factor;
|
||||
|
||||
/* For low-dpi mice, use device units, everything else uses
|
||||
1000dpi normalized */
|
||||
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_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;
|
||||
return normalize_for_dpi(unaccelerated, accel->dpi);
|
||||
}
|
||||
|
||||
static struct normalized_coords
|
||||
accelerator_filter_x230(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *raw,
|
||||
void *data, uint64_t time)
|
||||
{
|
||||
struct pointer_accelerator *accel =
|
||||
(struct pointer_accelerator *) filter;
|
||||
double accel_factor; /* unitless factor */
|
||||
struct normalized_coords accelerated;
|
||||
struct device_float_coords delta_normalized;
|
||||
struct normalized_coords unaccelerated;
|
||||
double velocity; /* units/us */
|
||||
|
||||
feed_trackers(accel, unaccelerated, time);
|
||||
/* This filter is a "do not touch me" filter. So the hack here is
|
||||
* just to replicate the old behavior before filters switched to
|
||||
* device-native dpi:
|
||||
* 1) convert from device-native to 1000dpi normalized
|
||||
* 2) run all calculation on 1000dpi-normalized data
|
||||
* 3) apply accel factor no normalized data
|
||||
*/
|
||||
unaccelerated = normalize_for_dpi(raw, accel->dpi);
|
||||
delta_normalized.x = unaccelerated.x;
|
||||
delta_normalized.y = unaccelerated.y;
|
||||
|
||||
feed_trackers(accel, &delta_normalized, time);
|
||||
velocity = calculate_velocity(accel, time);
|
||||
accel_factor = calculate_acceleration(accel,
|
||||
data,
|
||||
|
|
@ -449,36 +485,64 @@ accelerator_filter_x230(struct motion_filter *filter,
|
|||
time);
|
||||
accel->last_velocity = velocity;
|
||||
|
||||
accelerated.x = accel_factor * unaccelerated->x;
|
||||
accelerated.y = accel_factor * unaccelerated->y;
|
||||
accelerated.x = accel_factor * delta_normalized.x;
|
||||
accelerated.y = accel_factor * delta_normalized.y;
|
||||
|
||||
return accelerated;
|
||||
}
|
||||
|
||||
static struct normalized_coords
|
||||
accelerator_filter_constant_x230(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time)
|
||||
{
|
||||
struct pointer_accelerator *accel =
|
||||
(struct pointer_accelerator *) filter;
|
||||
struct normalized_coords normalized;
|
||||
const double factor =
|
||||
X230_MAGIC_SLOWDOWN/X230_TP_MAGIC_LOW_RES_FACTOR;
|
||||
|
||||
normalized.x = factor * unaccelerated->x;
|
||||
normalized.y = factor * unaccelerated->y;
|
||||
normalized = normalize_for_dpi(unaccelerated, accel->dpi);
|
||||
normalized.x = factor * normalized.x;
|
||||
normalized.y = factor * normalized.y;
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
static bool
|
||||
touchpad_accelerator_set_speed(struct motion_filter *filter,
|
||||
double speed_adjustment)
|
||||
{
|
||||
struct pointer_accelerator *accel_filter =
|
||||
(struct pointer_accelerator *)filter;
|
||||
|
||||
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" */
|
||||
|
||||
/* adjust when accel kicks in */
|
||||
accel_filter->threshold = TOUCHPAD_DEFAULT_THRESHOLD -
|
||||
TOUCHPAD_THRESHOLD_RANGE * speed_adjustment;
|
||||
accel_filter->accel = TOUCHPAD_ACCELERATION;
|
||||
accel_filter->incline = TOUCHPAD_INCLINE;
|
||||
filter->speed_adjustment = speed_adjustment;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct normalized_coords
|
||||
touchpad_constant_filter(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time)
|
||||
{
|
||||
struct pointer_accelerator *accel =
|
||||
(struct pointer_accelerator *)filter;
|
||||
struct normalized_coords normalized;
|
||||
|
||||
normalized.x = TP_MAGIC_SLOWDOWN * unaccelerated->x;
|
||||
normalized.y = TP_MAGIC_SLOWDOWN * unaccelerated->y;
|
||||
normalized = normalize_for_dpi(unaccelerated, accel->dpi);
|
||||
normalized.x = TP_MAGIC_SLOWDOWN * normalized.x;
|
||||
normalized.y = TP_MAGIC_SLOWDOWN * normalized.y;
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
|
@ -566,8 +630,8 @@ pointer_accel_profile_linear_low_dpi(struct motion_filter *filter,
|
|||
double max_accel = accel_filter->accel; /* unitless factor */
|
||||
double threshold = accel_filter->threshold; /* units/us */
|
||||
const double incline = accel_filter->incline;
|
||||
double dpi_factor = accel_filter->dpi/(double)DEFAULT_MOUSE_DPI;
|
||||
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 */
|
||||
|
|
@ -590,7 +654,7 @@ pointer_accel_profile_linear_low_dpi(struct motion_filter *filter,
|
|||
double
|
||||
pointer_accel_profile_linear(struct motion_filter *filter,
|
||||
void *data,
|
||||
double speed_in, /* 1000-dpi normalized */
|
||||
double speed_in, /* in device units (units/µs) */
|
||||
uint64_t time)
|
||||
{
|
||||
struct pointer_accelerator *accel_filter =
|
||||
|
|
@ -600,6 +664,9 @@ pointer_accel_profile_linear(struct motion_filter *filter,
|
|||
const double incline = accel_filter->incline;
|
||||
double factor; /* unitless */
|
||||
|
||||
/* Normalize to 1000dpi, because the rest below relies on that */
|
||||
speed_in = speed_in * DEFAULT_MOUSE_DPI/accel_filter->dpi;
|
||||
|
||||
/*
|
||||
Our acceleration function calculates a factor to accelerate input
|
||||
deltas with. The function is a double incline with a plateau,
|
||||
|
|
@ -662,15 +729,78 @@ pointer_accel_profile_linear(struct motion_filter *filter,
|
|||
|
||||
double
|
||||
touchpad_accel_profile_linear(struct motion_filter *filter,
|
||||
void *data,
|
||||
double speed_in, /* units/us */
|
||||
uint64_t time)
|
||||
void *data,
|
||||
double speed_in, /* in device units/µs */
|
||||
uint64_t time)
|
||||
{
|
||||
struct pointer_accelerator *accel_filter =
|
||||
(struct pointer_accelerator *)filter;
|
||||
const double max_accel = accel_filter->accel; /* unitless factor */
|
||||
const double threshold = accel_filter->threshold; /* units/us */
|
||||
const double incline = accel_filter->incline;
|
||||
double factor; /* unitless */
|
||||
|
||||
speed_in *= TP_MAGIC_SLOWDOWN;
|
||||
/* Convert to mm/s because that's something one can understand */
|
||||
speed_in = v_us2s(speed_in) * 25.4/accel_filter->dpi;
|
||||
|
||||
factor = pointer_accel_profile_linear(filter, data, speed_in, time);
|
||||
/*
|
||||
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
|
||||
^
|
||||
| /
|
||||
| _____/
|
||||
| /
|
||||
|/
|
||||
+-------------> 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 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
|
||||
|
||||
Note:
|
||||
* The minimum 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 (speed_in < 7.0) {
|
||||
factor = 0.1 * 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 * (speed_in - threshold) + 1;
|
||||
}
|
||||
|
||||
/* Cap at the maximum acceleration factor */
|
||||
factor = min(max_accel, factor);
|
||||
|
||||
/* Scale everything depending on the acceleration set */
|
||||
factor *= 1 + 0.5 * filter->speed_adjustment;
|
||||
|
||||
return factor * TP_MAGIC_SLOWDOWN;
|
||||
}
|
||||
|
|
@ -678,7 +808,7 @@ touchpad_accel_profile_linear(struct motion_filter *filter,
|
|||
double
|
||||
touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
|
||||
void *data,
|
||||
double speed_in,
|
||||
double speed_in, /* 1000dpi-units/µs */
|
||||
uint64_t time)
|
||||
{
|
||||
/* Those touchpads presents an actual lower resolution that what is
|
||||
|
|
@ -725,8 +855,8 @@ trackpoint_accel_profile(struct motion_filter *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;
|
||||
double dpi_factor = accel_filter->dpi_factor;
|
||||
|
||||
/* dpi_factor is always < 1.0, increase max_accel, reduce
|
||||
the threshold so it kicks in earlier */
|
||||
|
|
@ -748,7 +878,7 @@ trackpoint_accel_profile(struct motion_filter *filter,
|
|||
|
||||
struct motion_filter_interface accelerator_interface = {
|
||||
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
||||
.filter = accelerator_filter,
|
||||
.filter = accelerator_filter_generic,
|
||||
.filter_constant = accelerator_filter_noop,
|
||||
.restart = accelerator_restart,
|
||||
.destroy = accelerator_destroy,
|
||||
|
|
@ -773,8 +903,7 @@ create_default_filter(int dpi)
|
|||
filter->threshold = DEFAULT_THRESHOLD;
|
||||
filter->accel = DEFAULT_ACCELERATION;
|
||||
filter->incline = DEFAULT_INCLINE;
|
||||
|
||||
filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI;
|
||||
filter->dpi = dpi;
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
|
@ -796,7 +925,7 @@ create_pointer_accelerator_filter_linear(int dpi)
|
|||
|
||||
struct motion_filter_interface accelerator_interface_low_dpi = {
|
||||
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
||||
.filter = accelerator_filter_low_dpi,
|
||||
.filter = accelerator_filter_generic,
|
||||
.filter_constant = accelerator_filter_noop,
|
||||
.restart = accelerator_restart,
|
||||
.destroy = accelerator_destroy,
|
||||
|
|
@ -820,11 +949,11 @@ create_pointer_accelerator_filter_linear_low_dpi(int dpi)
|
|||
|
||||
struct motion_filter_interface accelerator_interface_touchpad = {
|
||||
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
||||
.filter = accelerator_filter,
|
||||
.filter = accelerator_filter_generic,
|
||||
.filter_constant = touchpad_constant_filter,
|
||||
.restart = accelerator_restart,
|
||||
.destroy = accelerator_destroy,
|
||||
.set_speed = accelerator_set_speed,
|
||||
.set_speed = touchpad_accelerator_set_speed,
|
||||
};
|
||||
|
||||
struct motion_filter *
|
||||
|
|
@ -875,15 +1004,14 @@ create_pointer_accelerator_filter_lenovo_x230(int dpi)
|
|||
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 */
|
||||
filter->dpi = dpi;
|
||||
|
||||
return &filter->base;
|
||||
}
|
||||
|
||||
struct motion_filter_interface accelerator_interface_trackpoint = {
|
||||
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
||||
.filter = accelerator_filter_trackpoint,
|
||||
.filter = accelerator_filter_generic,
|
||||
.filter_constant = accelerator_filter_noop,
|
||||
.restart = accelerator_restart,
|
||||
.destroy = accelerator_destroy,
|
||||
|
|
@ -904,29 +1032,26 @@ create_pointer_accelerator_filter_trackpoint(int dpi)
|
|||
filter->threshold = DEFAULT_THRESHOLD;
|
||||
filter->accel = DEFAULT_ACCELERATION;
|
||||
filter->incline = DEFAULT_INCLINE;
|
||||
filter->dpi = dpi;
|
||||
|
||||
return &filter->base;
|
||||
}
|
||||
|
||||
static struct normalized_coords
|
||||
accelerator_filter_flat(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time)
|
||||
{
|
||||
struct pointer_accelerator_flat *accel_filter =
|
||||
(struct pointer_accelerator_flat *)filter;
|
||||
double factor; /* unitless factor */
|
||||
struct normalized_coords accelerated;
|
||||
struct normalized_coords unnormalized;
|
||||
|
||||
/* You want flat acceleration, you get flat acceleration for the
|
||||
* device */
|
||||
unnormalized.x = unaccelerated->x * accel_filter->dpi_factor;
|
||||
unnormalized.y = unaccelerated->y * accel_filter->dpi_factor;
|
||||
factor = accel_filter->factor;
|
||||
|
||||
accelerated.x = factor * unnormalized.x;
|
||||
accelerated.y = factor * unnormalized.y;
|
||||
accelerated.x = factor * unaccelerated->x;
|
||||
accelerated.y = factor * unaccelerated->y;
|
||||
|
||||
return accelerated;
|
||||
}
|
||||
|
|
@ -979,26 +1104,24 @@ create_pointer_accelerator_filter_flat(int dpi)
|
|||
return NULL;
|
||||
|
||||
filter->base.interface = &accelerator_interface_flat;
|
||||
filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI;
|
||||
filter->dpi = dpi;
|
||||
|
||||
return &filter->base;
|
||||
}
|
||||
|
||||
static inline struct normalized_coords
|
||||
tablet_accelerator_filter_flat_mouse(struct tablet_accelerator_flat *filter,
|
||||
const struct normalized_coords *units)
|
||||
const struct device_float_coords *units)
|
||||
{
|
||||
struct normalized_coords accelerated;
|
||||
|
||||
/*
|
||||
The input for and output of accel methods is usually a delta in
|
||||
1000dpi equivalents. Tablets are high res (Intuos 4 is 5080 dpi)
|
||||
and unmodified deltas are way too high. Slow it down to the
|
||||
equivalent of a 1000dpi mouse. The ratio of that is:
|
||||
Tablets are high res (Intuos 4 is 5080 dpi) and unmodified deltas
|
||||
are way too high. Slow it down to the equivalent of a 1000dpi
|
||||
mouse. The ratio of that is:
|
||||
ratio = 1000/(resolution_per_mm * 25.4)
|
||||
|
||||
i.e. on the Intuos4 it's a ratio of ~1/5.
|
||||
|
||||
*/
|
||||
|
||||
accelerated.x = units->x * filter->xres_scale;
|
||||
|
|
@ -1012,12 +1135,12 @@ tablet_accelerator_filter_flat_mouse(struct tablet_accelerator_flat *filter,
|
|||
|
||||
static struct normalized_coords
|
||||
tablet_accelerator_filter_flat_pen(struct tablet_accelerator_flat *filter,
|
||||
const struct normalized_coords *units)
|
||||
const struct device_float_coords *units)
|
||||
{
|
||||
struct normalized_coords accelerated;
|
||||
|
||||
/* Tablet input is in device units, output is supposed to be in logical
|
||||
* pixels roughly equivalent to a mouse/touchpad.
|
||||
/* Tablet input is in device units, output is supposed to be in
|
||||
* logical pixels roughly equivalent to a mouse/touchpad.
|
||||
*
|
||||
* This is a magical constant found by trial and error. On a 96dpi
|
||||
* screen 0.4mm of movement correspond to 1px logical pixel which
|
||||
|
|
@ -1037,7 +1160,7 @@ tablet_accelerator_filter_flat_pen(struct tablet_accelerator_flat *filter,
|
|||
|
||||
static struct normalized_coords
|
||||
tablet_accelerator_filter_flat(struct motion_filter *filter,
|
||||
const struct normalized_coords *units,
|
||||
const struct device_float_coords *units,
|
||||
void *data, uint64_t time)
|
||||
{
|
||||
struct tablet_accelerator_flat *accel_filter =
|
||||
|
|
|
|||
25
src/filter.h
25
src/filter.h
|
|
@ -41,11 +41,24 @@ struct motion_filter;
|
|||
*
|
||||
* This is a superset of filter_dispatch_constant()
|
||||
*
|
||||
* @param filter The device's motion filter
|
||||
* @param unaccelerated The unaccelerated delta in the device's dpi
|
||||
* resolution as specified during filter creation. If a device has uneven
|
||||
* resolution for x and y, one axis needs to be scaled to match the
|
||||
* originally provided resolution.
|
||||
* @param data Custom data
|
||||
* @param time The time of the delta
|
||||
*
|
||||
* @return A set of normalized coordinates that can be used for pixel
|
||||
* movement. The normalized coordiantes are scaled to the default dpi range,
|
||||
* i.e. regardless of the resolution of the underlying device, the returned
|
||||
* values always reflect a 1000dpi mouse.
|
||||
*
|
||||
* @see filter_dispatch_constant
|
||||
*/
|
||||
struct normalized_coords
|
||||
filter_dispatch(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time);
|
||||
|
||||
/**
|
||||
|
|
@ -54,11 +67,19 @@ filter_dispatch(struct motion_filter *filter,
|
|||
* Takes a set of unaccelerated deltas and applies any constant filters to
|
||||
* it but does not accelerate the delta in the conventional sense.
|
||||
*
|
||||
* @param filter The device's motion filter
|
||||
* @param unaccelerated The unaccelerated delta in the device's dpi
|
||||
* resolution as specified during filter creation. If a device has uneven
|
||||
* resolution for x and y, one axis needs to be scaled to match the
|
||||
* originally provided resolution.
|
||||
* @param data Custom data
|
||||
* @param time The time of the delta
|
||||
*
|
||||
* @see filter_dispatch
|
||||
*/
|
||||
struct normalized_coords
|
||||
filter_dispatch_constant(struct motion_filter *filter,
|
||||
const struct normalized_coords *unaccelerated,
|
||||
const struct device_float_coords *unaccelerated,
|
||||
void *data, uint64_t time);
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -676,6 +676,12 @@ device_float_average(struct device_float_coords a, struct device_float_coords b)
|
|||
return average;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
device_float_is_zero(struct device_float_coords coords)
|
||||
{
|
||||
return coords.x == 0.0 && coords.y == 0.0;
|
||||
}
|
||||
|
||||
static inline double
|
||||
normalized_length(struct normalized_coords norm)
|
||||
{
|
||||
|
|
@ -701,28 +707,28 @@ enum directions {
|
|||
};
|
||||
|
||||
static inline uint32_t
|
||||
normalized_get_direction(struct normalized_coords norm)
|
||||
xy_get_direction(double x, double y)
|
||||
{
|
||||
uint32_t dir = UNDEFINED_DIRECTION;
|
||||
int d1, d2;
|
||||
double r;
|
||||
|
||||
if (fabs(norm.x) < 2.0 && fabs(norm.y) < 2.0) {
|
||||
if (norm.x > 0.0 && norm.y > 0.0)
|
||||
if (fabs(x) < 2.0 && fabs(y) < 2.0) {
|
||||
if (x > 0.0 && y > 0.0)
|
||||
dir = S | SE | E;
|
||||
else if (norm.x > 0.0 && norm.y < 0.0)
|
||||
else if (x > 0.0 && y < 0.0)
|
||||
dir = N | NE | E;
|
||||
else if (norm.x < 0.0 && norm.y > 0.0)
|
||||
else if (x < 0.0 && y > 0.0)
|
||||
dir = S | SW | W;
|
||||
else if (norm.x < 0.0 && norm.y < 0.0)
|
||||
else if (x < 0.0 && y < 0.0)
|
||||
dir = N | NW | W;
|
||||
else if (norm.x > 0.0)
|
||||
else if (x > 0.0)
|
||||
dir = NE | E | SE;
|
||||
else if (norm.x < 0.0)
|
||||
else if (x < 0.0)
|
||||
dir = NW | W | SW;
|
||||
else if (norm.y > 0.0)
|
||||
else if (y > 0.0)
|
||||
dir = SE | S | SW;
|
||||
else if (norm.y < 0.0)
|
||||
else if (y < 0.0)
|
||||
dir = NE | N | NW;
|
||||
} else {
|
||||
/* Calculate r within the interval [0 to 8)
|
||||
|
|
@ -731,7 +737,7 @@ normalized_get_direction(struct normalized_coords norm)
|
|||
* d_f = r / 2π ([0 .. 1))
|
||||
* d_8 = 8 * d_f
|
||||
*/
|
||||
r = atan2(norm.y, norm.x);
|
||||
r = atan2(y, x);
|
||||
r = fmod(r + 2.5*M_PI, 2*M_PI);
|
||||
r *= 4*M_1_PI;
|
||||
|
||||
|
|
@ -745,4 +751,19 @@ normalized_get_direction(struct normalized_coords norm)
|
|||
return dir;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
normalized_get_direction(struct normalized_coords norm)
|
||||
{
|
||||
return xy_get_direction(norm.x, norm.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direction for the given set of coordinates.
|
||||
* assumption: coordinates are normalized to one axis resolution.
|
||||
*/
|
||||
static inline uint32_t
|
||||
device_float_get_direction(struct device_float_coords coords)
|
||||
{
|
||||
return xy_get_direction(coords.x, coords.y);
|
||||
}
|
||||
#endif /* LIBINPUT_PRIVATE_H */
|
||||
|
|
|
|||
|
|
@ -44,18 +44,20 @@ plot "$outfile-200.gnuplot" using 1:2 title "200dpi", \
|
|||
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 xlabel "speed in mm/s"
|
||||
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"
|
||||
set xrange [0:400]
|
||||
plot \
|
||||
$(
|
||||
for speed in -1 -0.5 -0.2 0 0.2 0.5 1; do
|
||||
$tool --mode=accel --dpi=1000 --filter=touchpad --speed=$speed> $outfile-$speed.gnuplot
|
||||
echo "\"$outfile-$speed.gnuplot\" using 1:2 title '$speed', \\"
|
||||
done
|
||||
)
|
||||
EOF
|
||||
|
||||
outfile="ptraccel-trackpoint"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
static void
|
||||
print_ptraccel_deltas(struct motion_filter *filter, double step)
|
||||
{
|
||||
struct normalized_coords motion;
|
||||
struct device_float_coords motion;
|
||||
struct normalized_coords accel;
|
||||
uint64_t time = 0;
|
||||
double i;
|
||||
|
||||
|
|
@ -55,9 +56,9 @@ print_ptraccel_deltas(struct motion_filter *filter, double step)
|
|||
motion.y = 0;
|
||||
time += us(12500); /* pretend 80Hz data */
|
||||
|
||||
motion = filter_dispatch(filter, &motion, NULL, time);
|
||||
accel = filter_dispatch(filter, &motion, NULL, time);
|
||||
|
||||
printf("%.2f %.3f\n", i, motion.x);
|
||||
printf("%.2f %.3f\n", i, accel.x);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +68,8 @@ print_ptraccel_movement(struct motion_filter *filter,
|
|||
double max_dx,
|
||||
double step)
|
||||
{
|
||||
struct normalized_coords motion;
|
||||
struct device_float_coords motion;
|
||||
struct normalized_coords accel;
|
||||
uint64_t time = 0;
|
||||
double dx;
|
||||
int i;
|
||||
|
|
@ -98,9 +100,9 @@ print_ptraccel_movement(struct motion_filter *filter,
|
|||
motion.y = 0;
|
||||
time += us(12500); /* pretend 80Hz data */
|
||||
|
||||
motion = filter_dispatch(filter, &motion, NULL, time);
|
||||
accel = filter_dispatch(filter, &motion, NULL, time);
|
||||
|
||||
printf("%d %.3f %.3f\n", i, motion.x, dx);
|
||||
printf("%d %.3f %.3f\n", i, accel.x, dx);
|
||||
|
||||
if (dx < max_dx)
|
||||
dx += step;
|
||||
|
|
@ -112,7 +114,8 @@ print_ptraccel_sequence(struct motion_filter *filter,
|
|||
int nevents,
|
||||
double *deltas)
|
||||
{
|
||||
struct normalized_coords motion;
|
||||
struct device_float_coords motion;
|
||||
struct normalized_coords accel;
|
||||
uint64_t time = 0;
|
||||
double *dx;
|
||||
int i;
|
||||
|
|
@ -132,25 +135,37 @@ print_ptraccel_sequence(struct motion_filter *filter,
|
|||
motion.y = 0;
|
||||
time += us(12500); /* pretend 80Hz data */
|
||||
|
||||
motion = filter_dispatch(filter, &motion, NULL, time);
|
||||
accel = filter_dispatch(filter, &motion, NULL, time);
|
||||
|
||||
printf("%d %.3f %.3f\n", i, motion.x, *dx);
|
||||
printf("%d %.3f %.3f\n", i, accel.x, *dx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_accel_func(struct motion_filter *filter, accel_profile_func_t profile)
|
||||
/* mm/s → units/µs */
|
||||
static inline double
|
||||
mmps_to_upus(double mmps, int dpi)
|
||||
{
|
||||
double vel;
|
||||
return mmps * (dpi/25.4) / 1e6;
|
||||
}
|
||||
|
||||
static void
|
||||
print_accel_func(struct motion_filter *filter,
|
||||
accel_profile_func_t profile,
|
||||
int dpi)
|
||||
{
|
||||
double mmps;
|
||||
|
||||
printf("# gnuplot:\n");
|
||||
printf("# set xlabel \"speed\"\n");
|
||||
printf("# set xlabel \"speed (mm/s)\"\n");
|
||||
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 < 0.004; vel += 0.0000001) {
|
||||
double result = profile(filter, NULL, vel, 0 /* time */);
|
||||
printf("%.8f\t%.4f\n", vel, result);
|
||||
printf("# plot \"gnuplot.data\" using 1:2 title 'accel factor'\n");
|
||||
printf("#\n");
|
||||
printf("# data: velocity(mm/s) factor velocity(units/us)\n");
|
||||
for (mmps = 0.0; mmps < 300.0; mmps += 1) {
|
||||
double units_per_us = mmps_to_upus(mmps, dpi);
|
||||
double result = profile(filter, NULL, units_per_us, 0 /* time */);
|
||||
printf("%.8f\t%.4f\t%.8f\n", mmps, result, units_per_us);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -335,7 +350,7 @@ main(int argc, char **argv)
|
|||
}
|
||||
|
||||
if (print_accel)
|
||||
print_accel_func(filter, profile);
|
||||
print_accel_func(filter, profile, dpi);
|
||||
else if (print_delta)
|
||||
print_ptraccel_deltas(filter, step);
|
||||
else if (print_motion)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue