1 /*
2 * Copyright © 2006-2009 Simon Thum
3 * Copyright © 2012 Jonas Ådahl
4 * Copyright © 2014-2018 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 struct trackpoint_accelerator {
39 struct motion_filter base;
40
41 struct pointer_trackers trackers;
42 double speed_factor;
43
44 double multiplier;
45 };
46
47 double
trackpoint_accel_profile(struct motion_filter * filter,void * data,double velocity,uint64_t time)48 trackpoint_accel_profile(struct motion_filter *filter,
49 void *data,
50 double velocity,
51 uint64_t time)
52 {
53 struct trackpoint_accelerator *accel_filter =
54 (struct trackpoint_accelerator *)filter;
55 double factor;
56
57 velocity = v_us2ms(velocity); /* make it units/ms */
58
59 /* Just a nice-enough curve that provides fluid factor conversion
60 * from the minimum speed up to the real maximum. Generated by
61 * https://www.mycurvefit.com/ with input data
62 * 0 0.3
63 * 0.1 1
64 * 0.4 3
65 * 0.6 4
66 */
67 factor = 10.06254 + (0.3 - 10.06254)/(1 + pow(velocity/0.9205459, 1.15363));
68
69 factor *= accel_filter->speed_factor;
70 return factor;
71 }
72
73 static struct normalized_coords
trackpoint_accelerator_filter(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)74 trackpoint_accelerator_filter(struct motion_filter *filter,
75 const struct device_float_coords *unaccelerated,
76 void *data, uint64_t time)
77 {
78 struct trackpoint_accelerator *accel_filter =
79 (struct trackpoint_accelerator *)filter;
80 struct device_float_coords multiplied;
81 struct normalized_coords coords;
82 double f;
83 double velocity;
84
85 multiplied.x = unaccelerated->x * accel_filter->multiplier;
86 multiplied.y = unaccelerated->y * accel_filter->multiplier;
87
88 trackers_feed(&accel_filter->trackers, &multiplied, time);
89 velocity = trackers_velocity(&accel_filter->trackers, time);
90
91 f = trackpoint_accel_profile(filter, data, velocity, time);
92 coords.x = multiplied.x * f;
93 coords.y = multiplied.y * f;
94
95 return coords;
96 }
97
98 static struct normalized_coords
trackpoint_accelerator_filter_noop(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)99 trackpoint_accelerator_filter_noop(struct motion_filter *filter,
100 const struct device_float_coords *unaccelerated,
101 void *data, uint64_t time)
102 {
103 struct trackpoint_accelerator *accel_filter =
104 (struct trackpoint_accelerator *)filter;
105 struct normalized_coords coords;
106
107 coords.x = unaccelerated->x * accel_filter->multiplier;
108 coords.y = unaccelerated->y * accel_filter->multiplier;
109
110 return coords;
111 }
112
113 /* Maps the [-1, 1] speed setting into a constant acceleration
114 * range. This isn't a linear scale, we keep 0 as the 'optimized'
115 * mid-point and scale down to 0 for setting -1 and up to 5 for
116 * setting 1. On the premise that if you want a faster cursor, it
117 * doesn't matter as much whether you have 0.56789 or 0.56790,
118 * but for lower settings it does because you may lose movements.
119 * *shrug*.
120 *
121 * Magic numbers calculated by MyCurveFit.com, data points were
122 * 0.0 0.0
123 * 0.1 0.1 (because we need 4 points)
124 * 1 1
125 * 2 5
126 *
127 * This curve fits nicely into the range necessary.
128 */
129 static inline double
speed_factor(double s)130 speed_factor(double s)
131 {
132 s += 1; /* map to [0, 2] */
133 return 435837.2 + (0.04762636 - 435837.2)/(1 + pow(s/240.4549,
134 2.377168));
135 }
136
137 static bool
trackpoint_accelerator_set_speed(struct motion_filter * filter,double speed_adjustment)138 trackpoint_accelerator_set_speed(struct motion_filter *filter,
139 double speed_adjustment)
140 {
141 struct trackpoint_accelerator *accel_filter =
142 (struct trackpoint_accelerator*)filter;
143
144 assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
145
146 filter->speed_adjustment = speed_adjustment;
147 accel_filter->speed_factor = speed_factor(speed_adjustment);
148
149
150 return true;
151 }
152
153 static void
trackpoint_accelerator_restart(struct motion_filter * filter,void * data,uint64_t time)154 trackpoint_accelerator_restart(struct motion_filter *filter,
155 void *data,
156 uint64_t time)
157 {
158 struct trackpoint_accelerator *accel =
159 (struct trackpoint_accelerator *) filter;
160
161 trackers_reset(&accel->trackers, time);
162 }
163
164 static void
trackpoint_accelerator_destroy(struct motion_filter * filter)165 trackpoint_accelerator_destroy(struct motion_filter *filter)
166 {
167 struct trackpoint_accelerator *accel_filter =
168 (struct trackpoint_accelerator *)filter;
169
170 trackers_free(&accel_filter->trackers);
171 free(accel_filter);
172 }
173
174 struct motion_filter_interface accelerator_interface_trackpoint = {
175 .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
176 .filter = trackpoint_accelerator_filter,
177 .filter_constant = trackpoint_accelerator_filter_noop,
178 .restart = trackpoint_accelerator_restart,
179 .destroy = trackpoint_accelerator_destroy,
180 .set_speed = trackpoint_accelerator_set_speed,
181 };
182
183 struct motion_filter *
create_pointer_accelerator_filter_trackpoint(double multiplier,bool use_velocity_averaging)184 create_pointer_accelerator_filter_trackpoint(double multiplier, bool use_velocity_averaging)
185 {
186 struct trackpoint_accelerator *filter;
187 struct pointer_delta_smoothener *smoothener;
188
189 assert(multiplier > 0.0);
190
191 /* Trackpoints are special. They don't have a movement speed like a
192 * mouse or a finger, instead they send a stream of events based on
193 * the pressure applied.
194 *
195 * Physical ranges on a trackpoint are the max values for relative
196 * deltas, but these are highly device-specific and unreliable to
197 * measure.
198 *
199 * Instead, we just have a constant multiplier we have in the quirks
200 * system.
201 */
202
203 filter = zalloc(sizeof *filter);
204 if (!filter)
205 return NULL;
206
207 filter->multiplier = multiplier;
208
209 trackers_init(&filter->trackers, use_velocity_averaging ? 16 : 2);
210
211 filter->base.interface = &accelerator_interface_trackpoint;
212
213 smoothener = zalloc(sizeof(*smoothener));
214 smoothener->threshold = ms2us(10);
215 smoothener->value = ms2us(10);
216 filter->trackers.smoothener = smoothener;
217
218 return &filter->base;
219 }
220