• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 device_float_coords
accelerator_filter_generic(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)129 accelerator_filter_generic(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 	double accel_value; /* unitless factor */
136 	struct device_float_coords accelerated;
137 
138 	accel_value = calculate_acceleration_factor(accel,
139 						    unaccelerated,
140 						    data,
141 						    time);
142 
143 	accelerated.x = accel_value * unaccelerated->x;
144 	accelerated.y = accel_value * unaccelerated->y;
145 
146 	return accelerated;
147 }
148 
149 static struct normalized_coords
accelerator_filter_unnormalized(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)150 accelerator_filter_unnormalized(struct motion_filter *filter,
151 				const struct device_float_coords *unaccelerated,
152 				void *data, uint64_t time)
153 {
154 	struct device_float_coords accelerated;
155 	struct normalized_coords normalized;
156 
157 	/* Accelerate for device units and return device units */
158 	accelerated = accelerator_filter_generic(filter,
159 						 unaccelerated,
160 						 data,
161 						 time);
162 	normalized.x = accelerated.x;
163 	normalized.y = accelerated.y;
164 	return normalized;
165 }
166 
167 static struct normalized_coords
accelerator_filter_noop(struct motion_filter * filter,const struct device_float_coords * unaccelerated,void * data,uint64_t time)168 accelerator_filter_noop(struct motion_filter *filter,
169 			const struct device_float_coords *unaccelerated,
170 			void *data, uint64_t time)
171 {
172 	struct pointer_accelerator_low_dpi *accel =
173 		(struct pointer_accelerator_low_dpi *) filter;
174 
175 	return normalize_for_dpi(unaccelerated, accel->dpi);
176 }
177 
178 static void
accelerator_restart(struct motion_filter * filter,void * data,uint64_t time)179 accelerator_restart(struct motion_filter *filter,
180 		    void *data,
181 		    uint64_t time)
182 {
183 	struct pointer_accelerator_low_dpi *accel =
184 		(struct pointer_accelerator_low_dpi *) filter;
185 
186 	trackers_reset(&accel->trackers, time);
187 }
188 
189 static void
accelerator_destroy(struct motion_filter * filter)190 accelerator_destroy(struct motion_filter *filter)
191 {
192 	struct pointer_accelerator_low_dpi *accel =
193 		(struct pointer_accelerator_low_dpi *) filter;
194 
195 	trackers_free(&accel->trackers);
196 	free(accel);
197 }
198 
199 static bool
accelerator_set_speed(struct motion_filter * filter,double speed_adjustment)200 accelerator_set_speed(struct motion_filter *filter,
201 		      double speed_adjustment)
202 {
203 	struct pointer_accelerator_low_dpi *accel_filter =
204 		(struct pointer_accelerator_low_dpi *)filter;
205 
206 	assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
207 
208 	/* Note: the numbers below are nothing but trial-and-error magic,
209 	   don't read more into them other than "they mostly worked ok" */
210 
211 	/* delay when accel kicks in */
212 	accel_filter->threshold = DEFAULT_THRESHOLD -
213 					v_ms2us(0.25) * speed_adjustment;
214 	if (accel_filter->threshold < MINIMUM_THRESHOLD)
215 		accel_filter->threshold = MINIMUM_THRESHOLD;
216 
217 	/* adjust max accel factor */
218 	accel_filter->accel = DEFAULT_ACCELERATION + speed_adjustment * 1.5;
219 
220 	/* higher speed -> faster to reach max */
221 	accel_filter->incline = DEFAULT_INCLINE + speed_adjustment * 0.75;
222 
223 	filter->speed_adjustment = speed_adjustment;
224 	return true;
225 }
226 
227 struct motion_filter_interface accelerator_interface_low_dpi = {
228 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
229 	.filter = accelerator_filter_unnormalized,
230 	.filter_constant = accelerator_filter_noop,
231 	.restart = accelerator_restart,
232 	.destroy = accelerator_destroy,
233 	.set_speed = accelerator_set_speed,
234 };
235 
236 static struct pointer_accelerator_low_dpi *
create_default_filter(int dpi,bool use_velocity_averaging)237 create_default_filter(int dpi, bool use_velocity_averaging)
238 {
239 	struct pointer_accelerator_low_dpi *filter;
240 
241 	filter = zalloc(sizeof *filter);
242 	filter->last_velocity = 0.0;
243 
244 	trackers_init(&filter->trackers, use_velocity_averaging ? 16 : 2);
245 
246 	filter->threshold = DEFAULT_THRESHOLD;
247 	filter->accel = DEFAULT_ACCELERATION;
248 	filter->incline = DEFAULT_INCLINE;
249 	filter->dpi = dpi;
250 
251 	return filter;
252 }
253 
254 struct motion_filter *
create_pointer_accelerator_filter_linear_low_dpi(int dpi,bool use_velocity_averaging)255 create_pointer_accelerator_filter_linear_low_dpi(int dpi, bool use_velocity_averaging)
256 {
257 	struct pointer_accelerator_low_dpi *filter;
258 
259 	filter = create_default_filter(dpi, use_velocity_averaging);
260 	if (!filter)
261 		return NULL;
262 
263 	filter->base.interface = &accelerator_interface_low_dpi;
264 	filter->profile = pointer_accel_profile_linear_low_dpi;
265 
266 	return &filter->base;
267 }
268