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