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