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