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
33 #include "filter.h"
34 #include "libinput-util.h"
35 #include "filter-private.h"
36
37 /*
38 * Default parameters for pointer acceleration profiles.
39 */
40
41 #define DEFAULT_THRESHOLD v_ms2us(0.4) /* in units/us */
42 #define MINIMUM_THRESHOLD v_ms2us(0.2) /* in units/us */
43 #define DEFAULT_ACCELERATION 2.0 /* unitless factor */
44 #define DEFAULT_INCLINE 1.1 /* unitless factor */
45
46 struct pointer_accelerator_low_dpi {
47 struct motion_filter base;
48
49 accel_profile_func_t profile;
50
51 double velocity; /* units/us */
52 double last_velocity; /* units/us */
53
54 struct pointer_trackers trackers;
55
56 double threshold; /* units/us */
57 double accel; /* unitless factor */
58 double incline; /* incline of the function */
59
60 int dpi;
61 };
62
63 /**
64 * Custom acceleration function for mice < 1000dpi.
65 * At slow motion, a single device unit causes a one-pixel movement.
66 * The threshold/max accel depends on the DPI, the smaller the DPI the
67 * earlier we accelerate and the higher the maximum acceleration is. Result:
68 * at low speeds we get pixel-precision, at high speeds we get approx. the
69 * same movement as a high-dpi mouse.
70 *
71 * Note: data fed to this function is in device units, not normalized.
72 */
73 double
pointer_accel_profile_linear_low_dpi(struct motion_filter * filter,void * data,double speed_in,uint64_t time)74 pointer_accel_profile_linear_low_dpi(struct motion_filter *filter,
75 void *data,
76 double speed_in, /* in device units (units/us) */
77 uint64_t time)
78 {
79 struct pointer_accelerator_low_dpi *accel_filter =
80 (struct pointer_accelerator_low_dpi *)filter;
81
82 double max_accel = accel_filter->accel; /* unitless factor */
83 double threshold = accel_filter->threshold; /* units/us */
84 const double incline = accel_filter->incline;
85 double dpi_factor = accel_filter->dpi/(double)DEFAULT_MOUSE_DPI;
86 double factor; /* unitless */
87
88 /* dpi_factor is always < 1.0, increase max_accel, reduce
89 the threshold so it kicks in earlier */
90 max_accel /= dpi_factor;
91 threshold *= dpi_factor;
92
93 /* see pointer_accel_profile_linear for a long description */
94 if (v_us2ms(speed_in) < 0.07)
95 factor = 10 * v_us2ms(speed_in) + 0.3;
96 else if (speed_in < threshold)
97 factor = 1;
98 else
99 factor = incline * v_us2ms(speed_in - threshold) + 1;
100
101 factor = min(max_accel, factor);
102
103 return factor;
104 }
105
106 static inline double
calculate_acceleration_factor(struct pointer_accelerator_low_dpi * accel,const struct device_float_coords * unaccelerated,void * data,uint64_t time)107 calculate_acceleration_factor(struct pointer_accelerator_low_dpi *accel,
108 const struct device_float_coords *unaccelerated,
109 void *data,
110 uint64_t time)
111 {
112 double velocity; /* units/us in device-native dpi*/
113 double accel_factor;
114
115 trackers_feed(&accel->trackers, unaccelerated, time);
116 velocity = trackers_velocity(&accel->trackers, time);
117 accel_factor = calculate_acceleration_simpsons(&accel->base,
118 accel->profile,
119 data,
120 velocity,
121 accel->last_velocity,
122 time);
123 accel->last_velocity = velocity;
124
125 return accel_factor;
126 }
127
128 static struct normalized_coords
accelerator_filter_low_dpi(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)129 accelerator_filter_low_dpi(struct motion_filter *filter,
130 const struct device_float_coords *unaccelerated,
131 void *data, uint64_t time)
132 {
133 struct pointer_accelerator_low_dpi *accel =
134 (struct pointer_accelerator_low_dpi *) filter;
135
136 /* Accelerate for device units and return device units */
137 double accel_factor = calculate_acceleration_factor(accel,
138 unaccelerated,
139 data,
140 time);
141 const struct normalized_coords normalized = {
142 .x = accel_factor * unaccelerated->x,
143 .y = accel_factor * unaccelerated->y,
144 };
145 return normalized;
146 }
147
148 static struct normalized_coords
accelerator_filter_noop(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)149 accelerator_filter_noop(struct motion_filter *filter,
150 const struct device_float_coords *unaccelerated,
151 void *data, uint64_t time)
152 {
153 const struct normalized_coords normalized = {
154 .x = unaccelerated->x,
155 .y = unaccelerated->y,
156 };
157 return normalized;
158 }
159
160 static void
accelerator_restart(struct motion_filter * filter,void * data,uint64_t time)161 accelerator_restart(struct motion_filter *filter,
162 void *data,
163 uint64_t time)
164 {
165 struct pointer_accelerator_low_dpi *accel =
166 (struct pointer_accelerator_low_dpi *) filter;
167
168 trackers_reset(&accel->trackers, time);
169 }
170
171 static void
accelerator_destroy(struct motion_filter * filter)172 accelerator_destroy(struct motion_filter *filter)
173 {
174 struct pointer_accelerator_low_dpi *accel =
175 (struct pointer_accelerator_low_dpi *) filter;
176
177 trackers_free(&accel->trackers);
178 free(accel);
179 }
180
181 static bool
accelerator_set_speed(struct motion_filter * filter,double speed_adjustment)182 accelerator_set_speed(struct motion_filter *filter,
183 double speed_adjustment)
184 {
185 struct pointer_accelerator_low_dpi *accel_filter =
186 (struct pointer_accelerator_low_dpi *)filter;
187
188 assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
189
190 /* Note: the numbers below are nothing but trial-and-error magic,
191 don't read more into them other than "they mostly worked ok" */
192
193 /* delay when accel kicks in */
194 accel_filter->threshold = DEFAULT_THRESHOLD -
195 v_ms2us(0.25) * speed_adjustment;
196 if (accel_filter->threshold < MINIMUM_THRESHOLD)
197 accel_filter->threshold = MINIMUM_THRESHOLD;
198
199 /* adjust max accel factor */
200 accel_filter->accel = DEFAULT_ACCELERATION + speed_adjustment * 1.5;
201
202 /* higher speed -> faster to reach max */
203 accel_filter->incline = DEFAULT_INCLINE + speed_adjustment * 0.75;
204
205 filter->speed_adjustment = speed_adjustment;
206 return true;
207 }
208
209 static const struct motion_filter_interface accelerator_interface_low_dpi = {
210 .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
211 .filter = accelerator_filter_low_dpi,
212 .filter_constant = accelerator_filter_noop,
213 .filter_scroll = accelerator_filter_noop,
214 .restart = accelerator_restart,
215 .destroy = accelerator_destroy,
216 .set_speed = accelerator_set_speed,
217 };
218
219 static struct pointer_accelerator_low_dpi *
create_default_filter(int dpi,bool use_velocity_averaging)220 create_default_filter(int dpi, bool use_velocity_averaging)
221 {
222 struct pointer_accelerator_low_dpi *filter;
223
224 filter = zalloc(sizeof *filter);
225 filter->last_velocity = 0.0;
226
227 trackers_init(&filter->trackers, use_velocity_averaging ? 16 : 2);
228
229 filter->threshold = DEFAULT_THRESHOLD;
230 filter->accel = DEFAULT_ACCELERATION;
231 filter->incline = DEFAULT_INCLINE;
232 filter->dpi = dpi;
233
234 return filter;
235 }
236
237 struct motion_filter *
create_pointer_accelerator_filter_linear_low_dpi(int dpi,bool use_velocity_averaging)238 create_pointer_accelerator_filter_linear_low_dpi(int dpi, bool use_velocity_averaging)
239 {
240 struct pointer_accelerator_low_dpi *filter;
241
242 filter = create_default_filter(dpi, use_velocity_averaging);
243 if (!filter)
244 return NULL;
245
246 filter->base.interface = &accelerator_interface_low_dpi;
247 filter->profile = pointer_accel_profile_linear_low_dpi;
248
249 return &filter->base;
250 }
251