2 * Copyright © 2012 Jonas Ådahl
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 #include "libinput-util.h"
35 filter_dispatch(struct motion_filter *filter,
36 struct motion_params *motion,
37 void *data, uint64_t time)
39 filter->interface->filter(filter, motion, data, time);
43 filter_destroy(struct motion_filter *filter)
48 filter->interface->destroy(filter);
52 * Default parameters for pointer acceleration profiles.
55 #define DEFAULT_THRESHOLD 0.4 /* in units/ms */
56 #define DEFAULT_ACCELERATION 2.0 /* unitless factor */
59 * Pointer acceleration filter constants
62 #define MAX_VELOCITY_DIFF 1.0 /* units/ms */
63 #define MOTION_TIMEOUT 300 /* (ms) */
64 #define NUM_POINTER_TRACKERS 16
66 struct pointer_tracker {
67 double dx; /* delta to most recent event, in device units */
68 double dy; /* delta to most recent event, in device units */
69 uint64_t time; /* ms */
73 struct pointer_accelerator;
74 struct pointer_accelerator {
75 struct motion_filter base;
77 accel_profile_func_t profile;
79 double velocity; /* units/ms */
80 double last_velocity; /* units/ms */
81 int last_dx; /* device units */
82 int last_dy; /* device units */
84 struct pointer_tracker *trackers;
89 feed_trackers(struct pointer_accelerator *accel,
94 struct pointer_tracker *trackers = accel->trackers;
96 for (i = 0; i < NUM_POINTER_TRACKERS; i++) {
101 current = (accel->cur_tracker + 1) % NUM_POINTER_TRACKERS;
102 accel->cur_tracker = current;
104 trackers[current].dx = 0.0;
105 trackers[current].dy = 0.0;
106 trackers[current].time = time;
107 trackers[current].dir = vector_get_direction(dx, dy);
110 static struct pointer_tracker *
111 tracker_by_offset(struct pointer_accelerator *accel, unsigned int offset)
114 (accel->cur_tracker + NUM_POINTER_TRACKERS - offset)
115 % NUM_POINTER_TRACKERS;
116 return &accel->trackers[index];
120 calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time)
128 distance = sqrt(dx*dx + dy*dy);
129 return distance / (double)(time - tracker->time); /* units/ms */
133 calculate_velocity(struct pointer_accelerator *accel, uint64_t time)
135 struct pointer_tracker *tracker;
138 double initial_velocity = 0.0;
139 double velocity_diff;
142 unsigned int dir = tracker_by_offset(accel, 0)->dir;
144 /* Find least recent vector within a timelimit, maximum velocity diff
145 * and direction threshold. */
146 for (offset = 1; offset < NUM_POINTER_TRACKERS; offset++) {
147 tracker = tracker_by_offset(accel, offset);
149 /* Stop if too far away in time */
150 if (time - tracker->time > MOTION_TIMEOUT ||
151 tracker->time > time)
154 /* Stop if direction changed */
159 velocity = calculate_tracker_velocity(tracker, time);
161 if (initial_velocity == 0.0) {
162 result = initial_velocity = velocity;
164 /* Stop if velocity differs too much from initial */
165 velocity_diff = fabs(initial_velocity - velocity);
166 if (velocity_diff > MAX_VELOCITY_DIFF)
173 return result; /* units/ms */
177 acceleration_profile(struct pointer_accelerator *accel,
178 void *data, double velocity, uint64_t time)
180 return accel->profile(&accel->base, data, velocity, time);
184 calculate_acceleration(struct pointer_accelerator *accel,
185 void *data, double velocity, uint64_t time)
189 /* Use Simpson's rule to calculate the avarage acceleration between
190 * the previous motion and the most recent. */
191 factor = acceleration_profile(accel, data, velocity, time);
192 factor += acceleration_profile(accel, data, accel->last_velocity, time);
194 acceleration_profile(accel, data,
195 (accel->last_velocity + velocity) / 2,
198 factor = factor / 6.0;
200 return factor; /* unitless factor */
204 accelerator_filter(struct motion_filter *filter,
205 struct motion_params *motion,
206 void *data, uint64_t time)
208 struct pointer_accelerator *accel =
209 (struct pointer_accelerator *) filter;
210 double velocity; /* units/ms */
211 double accel_value; /* unitless factor */
213 feed_trackers(accel, motion->dx, motion->dy, time);
214 velocity = calculate_velocity(accel, time);
215 accel_value = calculate_acceleration(accel, data, velocity, time);
217 motion->dx = accel_value * motion->dx;
218 motion->dy = accel_value * motion->dy;
220 accel->last_dx = motion->dx;
221 accel->last_dy = motion->dy;
223 accel->last_velocity = velocity;
227 accelerator_destroy(struct motion_filter *filter)
229 struct pointer_accelerator *accel =
230 (struct pointer_accelerator *) filter;
232 free(accel->trackers);
236 struct motion_filter_interface accelerator_interface = {
241 struct motion_filter *
242 create_pointer_accelator_filter(accel_profile_func_t profile)
244 struct pointer_accelerator *filter;
246 filter = malloc(sizeof *filter);
250 filter->base.interface = &accelerator_interface;
252 filter->profile = profile;
253 filter->last_velocity = 0.0;
258 calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers);
259 filter->cur_tracker = 0;
261 return &filter->base;
265 calc_penumbral_gradient(double x)
269 return 0.5 + (x * sqrt(1.0 - x * x) + asin(x)) / M_PI;
273 pointer_accel_profile_smooth_simple(struct motion_filter *filter,
275 double velocity, /* units/ms */
278 double threshold = DEFAULT_THRESHOLD; /* units/ms */
279 double accel = DEFAULT_ACCELERATION; /* unitless factor */
280 double smooth_accel_coefficient; /* unitless factor */
281 double factor; /* unitless factor */
288 /* We use units/ms as velocity but it has no real meaning unless all
289 devices have the same resolution. For touchpads, we normalize to
290 400dpi (15.75 units/mm), but the resolution on USB mice is all
291 over the place. Though most mice these days have either 400
292 dpi (15.75 units/mm), 800 dpi or 1000dpi, excluding gaming mice
293 that can usually adjust it on the fly anyway and currently go up
296 if (velocity < (threshold / 2.0))
297 return calc_penumbral_gradient(0.5 + velocity / threshold) * 2.0 - 1.0;
299 if (velocity <= threshold)
302 factor = velocity/threshold;
306 /* factor is between 1.0 and accel, scale this to 0.0 - 1.0 */
307 factor = (factor - 1.0) / (accel - 1.0);
308 smooth_accel_coefficient = calc_penumbral_gradient(factor);
309 return 1.0 + (smooth_accel_coefficient * (accel - 1.0));