1 /*
2  * Copyright © 2006-2009 Simon Thum
3  * Copyright © 2012 Jonas Ådahl
4  * Copyright © 2014-2015 Red Hat, Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
25 
26 #include "config.h"
27 
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <limits.h>
33 #include <math.h>
34 
35 #include "filter.h"
36 #include "libinput-util.h"
37 #include "filter-private.h"
38 
39 /* Once normalized, touchpads see the same acceleration as mice. that is
40  * technically correct but subjectively wrong, we expect a touchpad to be a
41  * lot slower than a mouse. Apply a magic factor to slow down all movements
42  */
43 #define TP_MAGIC_SLOWDOWN 0.2968 /* unitless factor */
44 
45 struct touchpad_accelerator {
46 	struct motion_filter base;
47 
48 	accel_profile_func_t profile;
49 
50 	double velocity;	/* units/us */
51 	double last_velocity;	/* units/us */
52 
53 	struct pointer_trackers trackers;
54 
55 	double threshold;	/* units/us */
56 	double accel;		/* unitless factor */
57 
58 	int dpi;
59 
60 	double speed_factor;    /* factor based on speed setting */
61 };
62 
63 /**
64  * Calculate the acceleration factor for the given delta with the timestamp.
65  *
66  * @param accel The acceleration filter
67  * @param unaccelerated The raw delta in the device's dpi
68  * @param data Caller-specific data
69  * @param time Current time in µs
70  *
71  * @return A unitless acceleration factor, to be applied to the delta
72  */
73 static inline double
calculate_acceleration_factor(struct touchpad_accelerator * accel,const struct device_float_coords * unaccelerated,void * data,uint64_t time)74 calculate_acceleration_factor(struct touchpad_accelerator *accel,
75 			      const struct device_float_coords *unaccelerated,
76 			      void *data,
77 			      uint64_t time)
78 {
79 	double velocity; /* units/us in device-native dpi*/
80 	double accel_factor;
81 
82 	trackers_feed(&accel->trackers, unaccelerated, time);
83 	velocity = trackers_velocity(&accel->trackers, time);
84 	accel_factor = calculate_acceleration_simpsons(&accel->base,
85 						       accel->profile,
86 						       data,
87 						       velocity,
88 						       accel->last_velocity,
89 						       time);
90 	accel->last_velocity = velocity;
91 
92 	return accel_factor;
93 }
94 
95 /**
96  * Generic filter that calculates the acceleration factor and applies it to
97  * the coordinates.
98  *
99  * @param filter The acceleration filter
100  * @param unaccelerated The raw delta in the device's dpi
101  * @param data Caller-specific data
102  * @param time Current time in µs
103  *
104  * @return An accelerated tuple of coordinates representing accelerated
105  * motion, still in device units.
106  */
107 static struct device_float_coords
accelerator_filter_generic(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)108 accelerator_filter_generic(struct motion_filter *filter,
109 			   const struct device_float_coords *unaccelerated,
110 			   void *data, uint64_t time)
111 {
112 	struct touchpad_accelerator *accel =
113 		(struct touchpad_accelerator *) filter;
114 	double accel_value; /* unitless factor */
115 	struct device_float_coords accelerated;
116 
117 	accel_value = calculate_acceleration_factor(accel,
118 						    unaccelerated,
119 						    data,
120 						    time);
121 
122 	accelerated.x = accel_value * unaccelerated->x;
123 	accelerated.y = accel_value * unaccelerated->y;
124 
125 	return accelerated;
126 }
127 
128 static struct normalized_coords
accelerator_filter_post_normalized(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)129 accelerator_filter_post_normalized(struct motion_filter *filter,
130 				   const struct device_float_coords *unaccelerated,
131 				   void *data, uint64_t time)
132 {
133 	struct touchpad_accelerator *accel =
134 		(struct touchpad_accelerator *) filter;
135 	struct device_float_coords accelerated;
136 
137 	/* Accelerate for device units, normalize afterwards */
138 	accelerated = accelerator_filter_generic(filter,
139 						 unaccelerated,
140 						 data,
141 						 time);
142 	return normalize_for_dpi(&accelerated, accel->dpi);
143 }
144 
145 /* Maps the [-1, 1] speed setting into a constant acceleration
146  * range. This isn't a linear scale, we keep 0 as the 'optimized'
147  * mid-point and scale down to 0 for setting -1 and up to 5 for
148  * setting 1. On the premise that if you want a faster cursor, it
149  * doesn't matter as much whether you have 0.56789 or 0.56790,
150  * but for lower settings it does because you may lose movements.
151  * *shrug*.
152  *
153  * Magic numbers calculated by MyCurveFit.com, data points were
154  *  0.0 0.0
155  *  0.1 0.1 (because we need 4 points)
156  *  1   1
157  *  2   5
158  *
159  *  This curve fits nicely into the range necessary.
160  */
161 static inline double
speed_factor(double s)162 speed_factor(double s)
163 {
164 	s += 1; /* map to [0, 2] */
165 	return 435837.2 + (0.04762636 - 435837.2)/(1 + pow(s/240.4549,
166 							   2.377168));
167 }
168 
169 static bool
touchpad_accelerator_set_speed(struct motion_filter * filter,double speed_adjustment)170 touchpad_accelerator_set_speed(struct motion_filter *filter,
171 		      double speed_adjustment)
172 {
173 	struct touchpad_accelerator *accel_filter =
174 		(struct touchpad_accelerator *)filter;
175 
176 	assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
177 
178 	filter->speed_adjustment = speed_adjustment;
179 	accel_filter->speed_factor = speed_factor(speed_adjustment);
180 
181 	return true;
182 }
183 
184 static struct normalized_coords
touchpad_constant_filter(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)185 touchpad_constant_filter(struct motion_filter *filter,
186 			 const struct device_float_coords *unaccelerated,
187 			 void *data, uint64_t time)
188 {
189 	struct touchpad_accelerator *accel =
190 		(struct touchpad_accelerator *)filter;
191 	struct normalized_coords normalized;
192 
193 	normalized = normalize_for_dpi(unaccelerated, accel->dpi);
194 	normalized.x = TP_MAGIC_SLOWDOWN * normalized.x;
195 	normalized.y = TP_MAGIC_SLOWDOWN * normalized.y;
196 
197 	return normalized;
198 }
199 
200 static void
touchpad_accelerator_restart(struct motion_filter * filter,void * data,uint64_t time)201 touchpad_accelerator_restart(struct motion_filter *filter,
202 			     void *data,
203 			     uint64_t time)
204 {
205 	struct touchpad_accelerator *accel =
206 		(struct touchpad_accelerator *) filter;
207 
208 	trackers_reset(&accel->trackers, time);
209 }
210 
211 static void
touchpad_accelerator_destroy(struct motion_filter * filter)212 touchpad_accelerator_destroy(struct motion_filter *filter)
213 {
214 	struct touchpad_accelerator *accel =
215 		(struct touchpad_accelerator *) filter;
216 
217 	trackers_free(&accel->trackers);
218 	free(accel);
219 }
220 
221 double
touchpad_accel_profile_linear(struct motion_filter * filter,void * data,double speed_in,uint64_t time)222 touchpad_accel_profile_linear(struct motion_filter *filter,
223 			      void *data,
224 			      double speed_in, /* in device units/µs */
225 			      uint64_t time)
226 {
227 	struct touchpad_accelerator *accel_filter =
228 		(struct touchpad_accelerator *)filter;
229 	const double threshold = accel_filter->threshold; /* units/us */
230 	const double baseline = 0.9;
231 	double factor; /* unitless */
232 
233 	/* Convert to mm/s because that's something one can understand */
234 	speed_in = v_us2s(speed_in) * 25.4/accel_filter->dpi;
235 
236 	/*
237 	   Our acceleration function calculates a factor to accelerate input
238 	   deltas with. The function is a double incline with a plateau,
239 	   with a rough shape like this:
240 
241 	  accel
242 	 factor
243 	   ^         ______
244 	   |        )
245 	   |  _____)
246 	   | /
247 	   |/
248 	   +-------------> speed in
249 
250 	   Except the second incline is a curve, but well, asciiart.
251 
252 	   The first incline is a linear function in the form
253 		   y = ax + b
254 		   where y is speed_out
255 		         x is speed_in
256 			 a is the incline of acceleration
257 			 b is minimum acceleration factor
258 	   for speeds up to the lower threshold, we decelerate, down to 30%
259 	   of input speed.
260 		   hence 1 = a * 7 + 0.3
261 		       0.7 = a * 7  => a := 0.1
262 		   deceleration function is thus:
263 			y = 0.1x + 0.3
264 
265 	   The first plateau is the baseline.
266 
267 	   The second incline is a curve up, based on magic numbers
268 	   obtained by trial-and-error.
269 
270 	   Above the second incline we have another plateau because
271 	   by then you're moving so fast that extra acceleration doesn't
272 	   help.
273 
274 	  Note:
275 	  * The minimum threshold is a result of trial-and-error and
276 	    has no other special meaning.
277 	  * 0.3 is chosen simply because it is above the Nyquist frequency
278 	    for subpixel motion within a pixel.
279 	*/
280 
281 	if (speed_in < 7.0) {
282 		factor = min(baseline, 0.1 * speed_in + 0.3);
283 	/* up to the threshold, we keep factor 1, i.e. 1:1 movement */
284 	} else if (speed_in < threshold) {
285 		factor = baseline;
286 	} else {
287 
288 	/* Acceleration function above the threshold is a curve up
289 	   to four times the threshold, because why not.
290 
291 	   Don't assume anything about the specific numbers though, this was
292 	   all just trial and error by tweaking numbers here and there, then
293 	   the formula was optimized doing basic maths.
294 
295 	   You could replace this with some other random formula that gives
296 	   the same numbers and it would be just as correct.
297 
298 	 */
299 		const double upper_threshold = threshold * 4.0;
300 		speed_in = min(speed_in, upper_threshold);
301 
302 		factor = 0.0025 * (speed_in/threshold) * (speed_in - threshold) + baseline;
303 	}
304 
305 	factor *= accel_filter->speed_factor;
306 	return factor * TP_MAGIC_SLOWDOWN;
307 }
308 
309 struct motion_filter_interface accelerator_interface_touchpad = {
310 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
311 	.filter = accelerator_filter_post_normalized,
312 	.filter_constant = touchpad_constant_filter,
313 	.restart = touchpad_accelerator_restart,
314 	.destroy = touchpad_accelerator_destroy,
315 	.set_speed = touchpad_accelerator_set_speed,
316 };
317 
318 struct motion_filter *
create_pointer_accelerator_filter_touchpad(int dpi,uint64_t event_delta_smooth_threshold,uint64_t event_delta_smooth_value,bool use_velocity_averaging)319 create_pointer_accelerator_filter_touchpad(int dpi,
320 	uint64_t event_delta_smooth_threshold,
321 	uint64_t event_delta_smooth_value,
322 	bool use_velocity_averaging)
323 {
324 	struct touchpad_accelerator *filter;
325 	struct pointer_delta_smoothener *smoothener;
326 
327 	filter = zalloc(sizeof *filter);
328 	filter->last_velocity = 0.0;
329 
330 	trackers_init(&filter->trackers, use_velocity_averaging ? 16 : 2);
331 
332 	filter->threshold = 130;
333 	filter->dpi = dpi;
334 
335 	filter->base.interface = &accelerator_interface_touchpad;
336 	filter->profile = touchpad_accel_profile_linear;
337 
338 	smoothener = zalloc(sizeof(*smoothener));
339 	smoothener->threshold = event_delta_smooth_threshold,
340 	smoothener->value = event_delta_smooth_value,
341 	filter->trackers.smoothener = smoothener;
342 
343 	return &filter->base;
344 }
345