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 <math.h>
33
34 #include "filter.h"
35 #include "libinput-util.h"
36 #include "filter-private.h"
37
38 #define MOTION_TIMEOUT ms2us(1000)
39
40 struct normalized_coords
filter_dispatch(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)41 filter_dispatch(struct motion_filter *filter,
42 const struct device_float_coords *unaccelerated,
43 void *data, uint64_t time)
44 {
45 return filter->interface->filter(filter, unaccelerated, data, time);
46 }
47
48 struct normalized_coords
filter_dispatch_constant(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)49 filter_dispatch_constant(struct motion_filter *filter,
50 const struct device_float_coords *unaccelerated,
51 void *data, uint64_t time)
52 {
53 return filter->interface->filter_constant(filter, unaccelerated, data, time);
54 }
55
56 void
filter_restart(struct motion_filter * filter,void * data,uint64_t time)57 filter_restart(struct motion_filter *filter,
58 void *data, uint64_t time)
59 {
60 if (filter->interface->restart)
61 filter->interface->restart(filter, data, time);
62 }
63
64 void
filter_destroy(struct motion_filter * filter)65 filter_destroy(struct motion_filter *filter)
66 {
67 if (!filter || !filter->interface->destroy)
68 return;
69
70 filter->interface->destroy(filter);
71 }
72
73 bool
filter_set_speed(struct motion_filter * filter,double speed_adjustment)74 filter_set_speed(struct motion_filter *filter,
75 double speed_adjustment)
76 {
77 return filter->interface->set_speed(filter, speed_adjustment);
78 }
79
80 double
filter_get_speed(struct motion_filter * filter)81 filter_get_speed(struct motion_filter *filter)
82 {
83 return filter->speed_adjustment;
84 }
85
86 enum libinput_config_accel_profile
filter_get_type(struct motion_filter * filter)87 filter_get_type(struct motion_filter *filter)
88 {
89 return filter->interface->type;
90 }
91
92 void
trackers_init(struct pointer_trackers * trackers,int ntrackers)93 trackers_init(struct pointer_trackers *trackers, int ntrackers)
94 {
95 trackers->trackers = zalloc(ntrackers *
96 sizeof(*trackers->trackers));
97 trackers->ntrackers = ntrackers;
98 trackers->cur_tracker = 0;
99 trackers->smoothener = NULL;
100 }
101
102 void
trackers_free(struct pointer_trackers * trackers)103 trackers_free(struct pointer_trackers *trackers)
104 {
105 free(trackers->trackers);
106 free(trackers->smoothener);
107 }
108
109 void
trackers_reset(struct pointer_trackers * trackers,uint64_t time)110 trackers_reset(struct pointer_trackers *trackers,
111 uint64_t time)
112 {
113 unsigned int offset;
114 struct pointer_tracker *tracker;
115
116 for (offset = 1; offset < trackers->ntrackers; offset++) {
117 tracker = trackers_by_offset(trackers, offset);
118 tracker->time = 0;
119 tracker->dir = 0;
120 tracker->delta.x = 0;
121 tracker->delta.y = 0;
122 }
123
124 tracker = trackers_by_offset(trackers, 0);
125 tracker->time = time;
126 tracker->dir = UNDEFINED_DIRECTION;
127 }
128
129 void
trackers_feed(struct pointer_trackers * trackers,const struct device_float_coords * delta,uint64_t time)130 trackers_feed(struct pointer_trackers *trackers,
131 const struct device_float_coords *delta,
132 uint64_t time)
133 {
134 unsigned int i, current;
135 struct pointer_tracker *ts = trackers->trackers;
136
137 assert(trackers->ntrackers);
138
139 for (i = 0; i < trackers->ntrackers; i++) {
140 ts[i].delta.x += delta->x;
141 ts[i].delta.y += delta->y;
142 }
143
144 current = (trackers->cur_tracker + 1) % trackers->ntrackers;
145 trackers->cur_tracker = current;
146
147 ts[current].delta.x = 0.0;
148 ts[current].delta.y = 0.0;
149 ts[current].time = time;
150 ts[current].dir = device_float_get_direction(*delta);
151 }
152
153 struct pointer_tracker *
trackers_by_offset(struct pointer_trackers * trackers,unsigned int offset)154 trackers_by_offset(struct pointer_trackers *trackers, unsigned int offset)
155 {
156 unsigned int index =
157 (trackers->cur_tracker + trackers->ntrackers - offset)
158 % trackers->ntrackers;
159 return &trackers->trackers[index];
160 }
161
162 static double
calculate_trackers_velocity(struct pointer_tracker * tracker,uint64_t time,struct pointer_delta_smoothener * smoothener)163 calculate_trackers_velocity(struct pointer_tracker *tracker,
164 uint64_t time,
165 struct pointer_delta_smoothener *smoothener)
166 {
167 uint64_t tdelta = time - tracker->time + 1;
168
169 if (smoothener && tdelta < smoothener->threshold)
170 tdelta = smoothener->value;
171
172 return hypot(tracker->delta.x, tracker->delta.y) /
173 (double)tdelta; /* units/us */
174 }
175
176 static double
trackers_velocity_after_timeout(struct pointer_tracker * tracker,struct pointer_delta_smoothener * smoothener)177 trackers_velocity_after_timeout(struct pointer_tracker *tracker,
178 struct pointer_delta_smoothener *smoothener)
179 {
180 /* First movement after timeout needs special handling.
181 *
182 * When we trigger the timeout, the last event is too far in the
183 * past to use it for velocity calculation across multiple tracker
184 * values.
185 *
186 * Use the motion timeout itself to calculate the speed rather than
187 * the last tracker time. This errs on the side of being too fast
188 * for really slow movements but provides much more useful initial
189 * movement in normal use-cases (pause, move, pause, move)
190 */
191 return calculate_trackers_velocity(tracker,
192 tracker->time + MOTION_TIMEOUT,
193 smoothener);
194 }
195
196 /**
197 * Calculate the velocity based on the tracker data. Velocity is averaged
198 * across multiple historical values, provided those values aren't "too
199 * different" to our current one. That includes either being too far in the
200 * past, moving into a different direction or having too much of a velocity
201 * change between events.
202 */
203 double
trackers_velocity(struct pointer_trackers * trackers,uint64_t time)204 trackers_velocity(struct pointer_trackers *trackers, uint64_t time)
205 {
206 const double MAX_VELOCITY_DIFF = v_ms2us(1); /* units/us */
207 struct pointer_tracker *tracker;
208 double velocity;
209 double result = 0.0;
210 double initial_velocity = 0.0;
211 double velocity_diff;
212 unsigned int offset;
213
214 unsigned int dir = trackers_by_offset(trackers, 0)->dir;
215
216 /* Find least recent vector within a timelimit, maximum velocity diff
217 * and direction threshold. */
218 for (offset = 1; offset < trackers->ntrackers; offset++) {
219 tracker = trackers_by_offset(trackers, offset);
220
221 /* Bug: time running backwards */
222 if (tracker->time > time)
223 break;
224
225 /* Stop if too far away in time */
226 if (time - tracker->time > MOTION_TIMEOUT) {
227 if (offset == 1)
228 result = trackers_velocity_after_timeout(
229 tracker,
230 trackers->smoothener);
231 break;
232 }
233
234 velocity = calculate_trackers_velocity(tracker,
235 time,
236 trackers->smoothener);
237
238 /* Stop if direction changed */
239 dir &= tracker->dir;
240 if (dir == 0) {
241 /* First movement after dirchange - velocity is that
242 * of the last movement */
243 if (offset == 1)
244 result = velocity;
245 break;
246 }
247
248 /* Always average the first two events. On some touchpads
249 * where the first event is jumpy, this somewhat reduces
250 * pointer jumps on slow motions. */
251 if (initial_velocity == 0.0 || offset <= 2) {
252 result = initial_velocity = velocity;
253 } else {
254 /* Stop if velocity differs too much from initial */
255 velocity_diff = fabs(initial_velocity - velocity);
256 if (velocity_diff > MAX_VELOCITY_DIFF)
257 break;
258
259 result = velocity;
260 }
261 }
262
263 return result; /* units/us */
264 }
265
266 /**
267 * Calculate the acceleration factor for our current velocity, averaging
268 * between our current and the most recent velocity to smoothen out changes.
269 *
270 * @param accel The acceleration filter
271 * @param data Caller-specific data
272 * @param velocity Velocity in device-units per µs
273 * @param last_velocity Previous velocity in device-units per µs
274 * @param time Current time in µs
275 *
276 * @return A unitless acceleration factor, to be applied to the delta
277 */
278 double
calculate_acceleration_simpsons(struct motion_filter * filter,accel_profile_func_t profile,void * data,double velocity,double last_velocity,uint64_t time)279 calculate_acceleration_simpsons(struct motion_filter *filter,
280 accel_profile_func_t profile,
281 void *data,
282 double velocity,
283 double last_velocity,
284 uint64_t time)
285 {
286 double factor;
287
288 /* Use Simpson's rule to calculate the average acceleration between
289 * the previous motion and the most recent. */
290 factor = profile(filter, data, velocity, time);
291 factor += profile(filter, data, last_velocity, time);
292 factor += 4.0 * profile(filter, data,
293 (last_velocity + velocity) / 2,
294 time);
295
296 factor = factor / 6.0;
297
298 return factor; /* unitless factor */
299 }
300