diff --git a/doc/pointer-acceleration.dox b/doc/pointer-acceleration.dox
index 502bc7b5..f6237c6b 100644
--- a/doc/pointer-acceleration.dox
+++ b/doc/pointer-acceleration.dox
@@ -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"
diff --git a/doc/svg/ptraccel-touchpad.svg b/doc/svg/ptraccel-touchpad.svg
index 1befa1e5..b6fa4658 100644
--- a/doc/svg/ptraccel-touchpad.svg
+++ b/doc/svg/ptraccel-touchpad.svg
@@ -9,7 +9,7 @@
>
Gnuplot
-Produced by GNUPLOT 5.0 patchlevel 0
+Produced by GNUPLOT 5.0 patchlevel 5
@@ -44,73 +44,103 @@
-
+
0
-
- 0.5
+
+ 0.2
-
+
+ 0.4
+
+
+
+
+ 0.6
+
+
+
+
+ 0.8
+
+
+
+
1
-
- 1.5
+
+ 1.2
-
+
+ 1.4
+
+
+
+
+ 1.6
+
+
+
+
+ 1.8
+
+
+
+
2
-
- 2.5
-
-
-
-
- 3
-
-
-
-
+
0
-
- 0.0005
+
+ 50
-
- 0.001
+
+ 100
-
- 0.0015
+
+ 150
-
- 0.002
+
+ 200
-
- 0.0025
+
+ 250
-
- 0.003
+
+ 300
+
+
+
+
+ 350
+
+
+
+
+ 400
@@ -118,1597 +148,340 @@
-
+
accel factor
-
- speed in units/us
+
+ speed in mm/s
- linear (mouse)
+ -1
-
- linear (mouse)
+
+ -1
-
+
- touchpad
+ -0.5
-
- touchpad
+
+ -0.5
-
+
-
+ -0.2
+
+
+ -0.2
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+
+
+
+ 0.2
+
+
+ 0.2
+
+
+
+
+
+ 0.5
+
+
+ 0.5
+
+
+
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index b1666ea7..f437c2df 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -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))
diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
index b840e724..90d3b44c 100644
--- a/src/evdev-tablet.c
+++ b/src/evdev-tablet.c
@@ -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,
diff --git a/src/evdev.c b/src/evdev.c
index 5a3850ce..b4de1997 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -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 {
diff --git a/src/filter-private.h b/src/filter-private.h
index 637125a0..6ccbf97f 100644
--- a/src/filter-private.h
+++ b/src/filter-private.h
@@ -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,
diff --git a/src/filter.c b/src/filter.c
index 4b15c307..d7a15158 100644
--- a/src/filter.c
+++ b/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 =
diff --git a/src/filter.h b/src/filter.h
index c1b43a5d..e24c20d4 100644
--- a/src/filter.h
+++ b/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
diff --git a/src/libinput-private.h b/src/libinput-private.h
index 46258802..0c75b3aa 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -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 */
diff --git a/tools/make-ptraccel-graphs.sh b/tools/make-ptraccel-graphs.sh
index 901baf9c..870ea925 100755
--- a/tools/make-ptraccel-graphs.sh
+++ b/tools/make-ptraccel-graphs.sh
@@ -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 < $outfile-$speed.gnuplot
+ echo "\"$outfile-$speed.gnuplot\" using 1:2 title '$speed', \\"
+ done
+)
EOF
outfile="ptraccel-trackpoint"
diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c
index 84a1221c..4fcb45f4 100644
--- a/tools/ptraccel-debug.c
+++ b/tools/ptraccel-debug.c
@@ -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)