• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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