• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Permission is hereby granted, free of charge, to any person obtaining a
3  * copy of this software and associated documentation files (the "Software"),
4  * to deal in the Software without restriction, including without limitation
5  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
6  * and/or sell copies of the Software, and to permit persons to whom the
7  * Software is furnished to do so, subject to the following conditions:
8  *
9  * The above copyright notice and this permission notice (including the next
10  * paragraph) shall be included in all copies or substantial portions of the
11  * Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
16  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19  * DEALINGS IN THE SOFTWARE.
20  */
21 
22 #include "config.h"
23 
24 #include <assert.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdint.h>
28 
29 #include "filter.h"
30 #include "filter-private.h"
31 
32 #define MOTION_TIMEOUT ms2us(1000)
33 #define FIRST_MOTION_TIME_INTERVAL ms2us(7) /* random but good enough interval for very first event */
34 
35 struct custom_accel_function {
36 	uint64_t last_time;
37 	double step;
38 	size_t npoints;
39 	double points[];
40 };
41 
42 static struct custom_accel_function *
create_custom_accel_function(double step,const double * points,size_t npoints)43 create_custom_accel_function(double step, const double *points, size_t npoints)
44 {
45 	if (npoints < LIBINPUT_ACCEL_NPOINTS_MIN ||
46 	    npoints > LIBINPUT_ACCEL_NPOINTS_MAX)
47 		return NULL;
48 
49 	if (step <= 0 || step > LIBINPUT_ACCEL_STEP_MAX)
50 		return NULL;
51 
52 	for (size_t idx = 0; idx < npoints; idx++) {
53 		if (points[idx] < LIBINPUT_ACCEL_POINT_MIN_VALUE ||
54 		    points[idx] > LIBINPUT_ACCEL_POINT_MAX_VALUE)
55 			return NULL;
56 	}
57 
58 	struct custom_accel_function *cf = zalloc(sizeof(*cf) + npoints * sizeof(*points));
59 	cf->last_time = 0;
60 	cf->step = step;
61 	cf->npoints = npoints;
62 	memcpy(cf->points, points, sizeof(*points) * npoints);
63 
64 	return cf;
65 }
66 
67 static void
custom_accel_function_destroy(struct custom_accel_function * cf)68 custom_accel_function_destroy(struct custom_accel_function *cf)
69 {
70 	if (cf == NULL)
71 		return;
72 
73 	free(cf);
74 }
75 
76 static double
custom_accel_function_calculate_speed(struct custom_accel_function * cf,const struct device_float_coords * unaccelerated,uint64_t time)77 custom_accel_function_calculate_speed(struct custom_accel_function *cf,
78 				      const struct device_float_coords *unaccelerated,
79 				      uint64_t time)
80 {
81 	/* Although most devices have a constant polling rate, and for fast
82 	 * movements these distances do represent the actual speed,
83 	 * for slow movements it is not the case.
84 	 *
85 	 * Since all devices have a finite resolution, real world events
86 	 * for a slow smooth movement could look like:
87 	 *   Event 1 - (0,  1) - time 0
88 	 *   Event 2 - (0,  0) - time 7  - filtered (zero event)
89 	 *   Event 3 - (1,  0) - time 14
90 	 *   Event 4 - (0,  0) - time 21 - filtered (zero event)
91 	 *   Event 5 - (0,  0) - time 28 - filtered (zero event)
92 	 *   Event 6 - (0,  1) - time 35
93 	 *
94 	 * Not taking the time into account would mean interpreting those events as:
95 	 *   Move 1 unit over 7 ms
96 	 *   Pause for 7 ms
97 	 *   Move 1 unit over 7 ms
98 	 *   Pause for 14 ms
99 	 *   Move 1 unit over 7ms
100 	 *
101 	 * Where in reality this was one smooth movement without pauses,
102 	 * so after normalizing for time we get:
103 	 *   Move 1 unit over 7 ms
104 	 *   Move 1 unit over 14 ms
105 	 *   Move 1 unit over 21ms
106 	 *
107 	 * which should give us better speed estimation.
108 	 */
109 
110 	/* calculate speed based on time passed since last event */
111 	double distance = hypot(unaccelerated->x, unaccelerated->y);
112 	/* handle first event in a motion */
113 	if (time - cf->last_time > MOTION_TIMEOUT)
114 		cf->last_time = time - FIRST_MOTION_TIME_INTERVAL;
115 
116 	double dt = us2ms_f(time - cf->last_time);
117 	double speed = distance / dt; /* speed is in device-units per ms */
118 	cf->last_time = time;
119 
120 	return speed;
121 }
122 
123 static double
custom_accel_function_profile(struct custom_accel_function * cf,double speed_in)124 custom_accel_function_profile(struct custom_accel_function *cf,
125 			      double speed_in)
126 {
127 	size_t npoints = cf->npoints;
128 	double step = cf->step;
129 	double *points = cf->points;
130 
131 	/* calculate the index of the first point used for interpolation */
132 	size_t i = speed_in / step;
133 
134 	/* if speed is greater than custom curve's max speed,
135 	   use last 2 points for linear extrapolation
136 	   (same calculation as linear interpolation) */
137 	i = min(i, npoints - 2);
138 
139 	/* the 2 points used for linear interpolation */
140 	double x0 = step * i;
141 	double x1 = step * (i + 1);
142 	double y0 = points[i];
143 	double y1 = points[i + 1];
144 
145 	/* linear interpolation */
146 	double speed_out = (y0 * (x1 - speed_in) + y1 * (speed_in - x0)) / step;
147 
148 	/* We moved (dx, dy) device units within the last N ms. This gives us a
149 	 * given speed S in units/ms, that's our accel input. Our curve says map
150 	 * that speed S to some other speed S'.
151 	 *
152 	 * Our device delta is represented by the vector, that vector needs to
153 	 * be modified to represent our intended speed.
154 	 *
155 	 * Example: we moved a delta of 7 over the last 7ms. Our speed is
156 	 * thus 1 u/ms, our out speed is 2 u/ms because we want to double our
157 	 * speed (points: [0.0, 2.0]). Our delta must thus be 14 - factor of 2,
158 	 * or out-speed/in-speed.
159 	 *
160 	 * Example: we moved a delta of 1 over the last 7ms. Our input speed is
161 	 * 1/7 u/ms, our out speed is 1/7ms because we set up a flat accel
162 	 * curve (points: [0.0, 1.0]). Our delta must thus be 1 - factor of 1,
163 	 * or out-speed/in-speed.
164 	 *
165 	 * Example: we moved a delta of 1 over the last 21ms. Our input speed is
166 	 * 1/21 u/ms, our out speed is 1u/ms because we set up a fixed-speed
167 	 * curve (points: [1.0, 1.0]). Our delta must thus be 21 - factor of 21,
168 	 * or out-speed/in-speed.
169 	 *
170 	 * Example: we moved a delta of 21 over the last 7ms. Our input speed is
171 	 * 3 u/ms, our out speed is 1u/ms because we set up a fixed-speed
172 	 * curve (points: [1.0, 1.0]). Our delta must thus be 7 - factor of 1/3,
173 	 * or out-speed/in-speed.
174 	 */
175 
176 	/* calculate the acceleration factor based on the user desired speed out */
177 	double accel_factor = speed_out / speed_in;
178 
179 	return accel_factor;
180 }
181 
182 static struct normalized_coords
custom_accel_function_filter(struct custom_accel_function * cf,const struct device_float_coords * unaccelerated,uint64_t time)183 custom_accel_function_filter(struct custom_accel_function *cf,
184 			     const struct device_float_coords *unaccelerated,
185 			     uint64_t time)
186 {
187 	double speed = custom_accel_function_calculate_speed(cf, unaccelerated, time);
188 
189 	double accel_factor = custom_accel_function_profile(cf, speed);
190 
191 	struct normalized_coords accelerated = {
192 		.x = unaccelerated->x * accel_factor,
193 		.y = unaccelerated->y * accel_factor,
194 	};
195 
196 	return accelerated;
197 }
198 
199 struct custom_accelerator {
200 	struct motion_filter base;
201 	struct {
202 		struct custom_accel_function *fallback;
203 		struct custom_accel_function *motion;
204 		struct custom_accel_function *scroll;
205 	} funcs;
206 };
207 
208 static struct custom_accel_function *
custom_accelerator_get_custom_function(struct custom_accelerator * f,enum libinput_config_accel_type accel_type)209 custom_accelerator_get_custom_function(struct custom_accelerator *f,
210 				       enum libinput_config_accel_type accel_type)
211 {
212 	switch (accel_type) {
213 	case LIBINPUT_ACCEL_TYPE_FALLBACK:
214 		return f->funcs.fallback;
215 	case LIBINPUT_ACCEL_TYPE_MOTION:
216 		return f->funcs.motion ? f->funcs.motion : f->funcs.fallback;
217 	case LIBINPUT_ACCEL_TYPE_SCROLL:
218 		return f->funcs.scroll ? f->funcs.scroll : f->funcs.fallback;
219 	}
220 
221 	return f->funcs.fallback;
222 }
223 
224 static double
custom_accelerator_profile(enum libinput_config_accel_type accel_type,struct motion_filter * filter,double speed_in)225 custom_accelerator_profile(enum libinput_config_accel_type accel_type,
226 			   struct motion_filter *filter,
227 			   double speed_in)
228 {
229 	struct custom_accelerator *f = (struct custom_accelerator *)filter;
230 	struct custom_accel_function *cf;
231 
232 	cf = custom_accelerator_get_custom_function(f, accel_type);
233 
234 	return custom_accel_function_profile(cf, speed_in);
235 }
236 
237 static struct normalized_coords
custom_accelerator_filter(enum libinput_config_accel_type accel_type,struct motion_filter * filter,const struct device_float_coords * unaccelerated,uint64_t time)238 custom_accelerator_filter(enum libinput_config_accel_type accel_type,
239 			  struct motion_filter *filter,
240 			  const struct device_float_coords *unaccelerated,
241 			  uint64_t time)
242 {
243 	struct custom_accelerator *f = (struct custom_accelerator *)filter;
244 	struct custom_accel_function *cf;
245 
246 	cf = custom_accelerator_get_custom_function(f, accel_type);
247 
248 	return custom_accel_function_filter(cf, unaccelerated, time);
249 }
250 
251 static void
custom_accelerator_restart(struct motion_filter * filter,void * data,uint64_t time)252 custom_accelerator_restart(struct motion_filter *filter,
253 			   void *data,
254 			   uint64_t time)
255 {
256 	/* noop, this function has no effect in the custom interface */
257 }
258 
259 static void
custom_accelerator_destroy(struct motion_filter * filter)260 custom_accelerator_destroy(struct motion_filter *filter)
261 {
262 	struct custom_accelerator *f =
263 		(struct custom_accelerator *)filter;
264 
265 	/* destroy all custom movement functions */
266 	custom_accel_function_destroy(f->funcs.fallback);
267 	custom_accel_function_destroy(f->funcs.motion);
268 	custom_accel_function_destroy(f->funcs.scroll);
269 	free(f);
270 }
271 
272 static bool
custom_accelerator_set_speed(struct motion_filter * filter,double speed_adjustment)273 custom_accelerator_set_speed(struct motion_filter *filter,
274 			     double speed_adjustment)
275 {
276 	assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
277 
278 	/* noop, this function has no effect in the custom interface */
279 
280 	return true;
281 }
282 
283 static bool
custom_accelerator_set_accel_config(struct motion_filter * filter,struct libinput_config_accel * config)284 custom_accelerator_set_accel_config(struct motion_filter *filter,
285 				    struct libinput_config_accel *config)
286 {
287 	struct custom_accelerator *f =
288 		(struct custom_accelerator *)filter;
289 
290 	struct custom_accel_function *fallback = NULL,
291 				     *motion = NULL,
292 				     *scroll = NULL;
293 
294 	if (config->custom.fallback) {
295 		fallback = create_custom_accel_function(config->custom.fallback->step,
296 							config->custom.fallback->points,
297 							config->custom.fallback->npoints);
298 		if (!fallback)
299 			goto out;
300 	}
301 
302 	if (config->custom.motion) {
303 		motion = create_custom_accel_function(config->custom.motion->step,
304 						      config->custom.motion->points,
305 						      config->custom.motion->npoints);
306 		if (!motion)
307 			goto out;
308 	}
309 
310 	if (config->custom.scroll) {
311 		scroll = create_custom_accel_function(config->custom.scroll->step,
312 						      config->custom.scroll->points,
313 						      config->custom.scroll->npoints);
314 		if (!scroll)
315 			goto out;
316 	}
317 
318 	custom_accel_function_destroy(f->funcs.fallback);
319 	custom_accel_function_destroy(f->funcs.motion);
320 	custom_accel_function_destroy(f->funcs.scroll);
321 
322 	f->funcs.fallback = fallback;
323 	f->funcs.motion = motion;
324 	f->funcs.scroll = scroll;
325 
326 	return true;
327 
328 out:
329 	custom_accel_function_destroy(fallback);
330 	custom_accel_function_destroy(motion);
331 	custom_accel_function_destroy(scroll);
332 
333 	return false;
334 }
335 
336 /* custom profiles and filters for the different accel types: */
337 
338 double
custom_accel_profile_fallback(struct motion_filter * filter,void * data,double speed_in,uint64_t time)339 custom_accel_profile_fallback(struct motion_filter *filter,
340 			      void *data,
341 			      double speed_in,
342 			      uint64_t time)
343 {
344 	return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_FALLBACK,
345 					  filter,
346 					  speed_in);
347 }
348 
349 static struct normalized_coords
custom_accelerator_filter_fallback(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)350 custom_accelerator_filter_fallback(struct motion_filter *filter,
351 				   const struct device_float_coords *unaccelerated,
352 				   void *data,
353 				   uint64_t time)
354 {
355 	return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_FALLBACK,
356 					 filter,
357 					 unaccelerated,
358 					 time);
359 }
360 
361 double
custom_accel_profile_motion(struct motion_filter * filter,void * data,double speed_in,uint64_t time)362 custom_accel_profile_motion(struct motion_filter *filter,
363 			    void *data,
364 			    double speed_in,
365 			    uint64_t time)
366 {
367 	return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_MOTION,
368 					  filter,
369 					  speed_in);
370 }
371 
372 static struct normalized_coords
custom_accelerator_filter_motion(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)373 custom_accelerator_filter_motion(struct motion_filter *filter,
374 				 const struct device_float_coords *unaccelerated,
375 				 void *data,
376 				 uint64_t time)
377 {
378 	return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_MOTION,
379 					 filter,
380 					 unaccelerated,
381 					 time);
382 }
383 
384 double
custom_accel_profile_scroll(struct motion_filter * filter,void * data,double speed_in,uint64_t time)385 custom_accel_profile_scroll(struct motion_filter *filter,
386 			    void *data,
387 			    double speed_in,
388 			    uint64_t time)
389 {
390 	return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_SCROLL,
391 					  filter,
392 					  speed_in);
393 }
394 
395 static struct normalized_coords
custom_accelerator_filter_scroll(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)396 custom_accelerator_filter_scroll(struct motion_filter *filter,
397 				 const struct device_float_coords *unaccelerated,
398 				 void *data,
399 				 uint64_t time)
400 {
401 	return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_SCROLL,
402 					 filter,
403 					 unaccelerated,
404 					 time);
405 }
406 
407 struct motion_filter_interface custom_accelerator_interface = {
408 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM,
409 	.filter = custom_accelerator_filter_motion,
410 	.filter_constant = custom_accelerator_filter_fallback,
411 	.filter_scroll = custom_accelerator_filter_scroll,
412 	.restart = custom_accelerator_restart,
413 	.destroy = custom_accelerator_destroy,
414 	.set_speed = custom_accelerator_set_speed,
415 	.set_accel_config = custom_accelerator_set_accel_config,
416 };
417 
418 struct motion_filter *
create_custom_accelerator_filter(void)419 create_custom_accelerator_filter(void)
420 {
421 	struct custom_accelerator *f = zalloc(sizeof(*f));
422 
423 	/* the unit function by default, speed in = speed out,
424 	   i.e. no acceleration */
425 	const double default_step = 1.0;
426 	const double default_points[2] = {0.0, 1.0};
427 
428 	/* initialize default acceleration, used as fallback */
429 	f->funcs.fallback = create_custom_accel_function(default_step,
430 							 default_points,
431 							 ARRAY_LENGTH(default_points));
432 	/* Don't initialize other acceleration functions. Those will be
433 	   initialized if the user sets their points, otherwise the fallback
434 	   acceleration function is used */
435 
436 	f->base.interface = &custom_accelerator_interface;
437 
438 	return &f->base;
439 }
440