1 /*
2 * Copyright © 2015 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "config.h"
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <getopt.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include "filter.h"
36 #include "libinput-util.h"
37
38 static void
print_ptraccel_deltas(struct motion_filter * filter,double step)39 print_ptraccel_deltas(struct motion_filter *filter, double step)
40 {
41 struct device_float_coords motion;
42 struct normalized_coords accel;
43 uint64_t time = 0;
44 double i;
45
46 printf("# gnuplot:\n");
47 printf("# set xlabel dx unaccelerated\n");
48 printf("# set ylabel dx accelerated\n");
49 printf("# set style data lines\n");
50 printf("# plot \"gnuplot.data\" using 1:2 title \"step %.2f\"\n", step);
51 printf("#\n");
52
53 /* Accel flattens out after 15 and becomes linear */
54 for (i = 0.0; i < 15.0; i += step) {
55 motion.x = i;
56 motion.y = 0;
57 time += us(12500); /* pretend 80Hz data */
58
59 accel = filter_dispatch(filter, &motion, NULL, time);
60
61 printf("%.2f %.3f\n", i, accel.x);
62 }
63 }
64
65 static void
print_ptraccel_movement(struct motion_filter * filter,int nevents,double max_dx,double step)66 print_ptraccel_movement(struct motion_filter *filter,
67 int nevents,
68 double max_dx,
69 double step)
70 {
71 struct device_float_coords motion;
72 struct normalized_coords accel;
73 uint64_t time = 0;
74 double dx;
75 int i;
76
77 printf("# gnuplot:\n");
78 printf("# set xlabel \"event number\"\n");
79 printf("# set ylabel \"delta motion\"\n");
80 printf("# set style data lines\n");
81 printf("# plot \"gnuplot.data\" using 1:2 title \"dx out\", \\\n");
82 printf("# \"gnuplot.data\" using 1:3 title \"dx in\"\n");
83 printf("#\n");
84
85 if (nevents == 0) {
86 if (step > 1.0)
87 nevents = max_dx;
88 else
89 nevents = 1.0 * max_dx/step + 0.5;
90
91 /* Print more events than needed so we see the curve
92 * flattening out */
93 nevents *= 1.5;
94 }
95
96 dx = 0;
97
98 for (i = 0; i < nevents; i++) {
99 motion.x = dx;
100 motion.y = 0;
101 time += us(12500); /* pretend 80Hz data */
102
103 accel = filter_dispatch(filter, &motion, NULL, time);
104
105 printf("%d %.3f %.3f\n", i, accel.x, dx);
106
107 if (dx < max_dx)
108 dx += step;
109 }
110 }
111
112 static void
print_ptraccel_sequence(struct motion_filter * filter,int nevents,double * deltas)113 print_ptraccel_sequence(struct motion_filter *filter,
114 int nevents,
115 double *deltas)
116 {
117 struct device_float_coords motion;
118 struct normalized_coords accel;
119 uint64_t time = 0;
120 double *dx;
121 int i;
122
123 printf("# gnuplot:\n");
124 printf("# set xlabel \"event number\"\n");
125 printf("# set ylabel \"delta motion\"\n");
126 printf("# set style data lines\n");
127 printf("# plot \"gnuplot.data\" using 1:2 title \"dx out\", \\\n");
128 printf("# \"gnuplot.data\" using 1:3 title \"dx in\"\n");
129 printf("#\n");
130
131 dx = deltas;
132
133 for (i = 0; i < nevents; i++, dx++) {
134 motion.x = *dx;
135 motion.y = 0;
136 time += us(12500); /* pretend 80Hz data */
137
138 accel = filter_dispatch(filter, &motion, NULL, time);
139
140 printf("%d %.3f %.3f\n", i, accel.x, *dx);
141 }
142 }
143
144 /* mm/s → units/µs */
145 static inline double
mmps_to_upus(double mmps,int dpi)146 mmps_to_upus(double mmps, int dpi)
147 {
148 return mmps * (dpi/25.4) / 1e6;
149 }
150
151 static void
print_accel_func(struct motion_filter * filter,accel_profile_func_t profile,int dpi)152 print_accel_func(struct motion_filter *filter,
153 accel_profile_func_t profile,
154 int dpi)
155 {
156 double mmps;
157
158 printf("# gnuplot:\n");
159 printf("# set xlabel \"speed (mm/s)\"\n");
160 printf("# set ylabel \"raw accel factor\"\n");
161 printf("# set style data lines\n");
162 printf("# plot \"gnuplot.data\" using 1:2 title 'accel factor'\n");
163 printf("#\n");
164 printf("# data: velocity(mm/s) factor velocity(units/us) velocity(units/ms)\n");
165 for (mmps = 0.0; mmps < 1000.0; mmps += 1) {
166 double units_per_us = mmps_to_upus(mmps, dpi);
167 double units_per_ms = units_per_us * 1000.0;
168 double result = profile(filter, NULL, units_per_us, 0 /* time */);
169 printf("%.8f\t%.4f\t%.8f\t%.8f\n", mmps, result, units_per_us, units_per_ms);
170 }
171 }
172
173 static void
usage(void)174 usage(void)
175 {
176 printf("Usage: %s [options] [dx1] [dx2] [...] > gnuplot.data\n", program_invocation_short_name);
177 printf("\n"
178 "Options:\n"
179 "--mode=<accel|motion|delta|sequence> \n"
180 " accel ... print accel factor (default)\n"
181 " motion ... print motion to accelerated motion\n"
182 " delta ... print delta to accelerated delta\n"
183 " sequence ... print motion for custom delta sequence\n"
184 "--maxdx=<double> ... in motion mode only. Stop increasing dx at maxdx\n"
185 "--steps=<double> ... in motion and delta modes only. Increase dx by step each round\n"
186 "--speed=<double> ... accel speed [-1, 1], default 0\n"
187 "--dpi=<int> ... device resolution in DPI (default: 1000)\n"
188 "--filter=<linear|low-dpi|touchpad|x230|trackpoint> \n"
189 " linear ... the default motion filter\n"
190 " low-dpi ... low-dpi filter, use --dpi with this argument\n"
191 " touchpad ... the touchpad motion filter\n"
192 " x230 ... custom filter for the Lenovo x230 touchpad\n"
193 " trackpoint... trackpoint motion filter\n"
194 "\n"
195 "If extra arguments are present and mode is not given, mode defaults to 'sequence'\n"
196 "and the arguments are interpreted as sequence of delta x coordinates\n"
197 "\n"
198 "If stdin is a pipe, mode defaults to 'sequence' and the pipe is read \n"
199 "for delta coordinates\n"
200 "\n"
201 "Delta coordinates passed into this tool must be in dpi as\n"
202 "specified by the --dpi argument\n"
203 "\n"
204 "Output best viewed with gnuplot. See output for gnuplot commands\n");
205 }
206
207 enum mode {
208 ACCEL,
209 MOTION,
210 DELTA,
211 SEQUENCE,
212 };
213
214 int
main(int argc,char ** argv)215 main(int argc, char **argv)
216 {
217 struct motion_filter *filter;
218 double step = 0.1,
219 max_dx = 10;
220 int nevents = 0;
221 enum mode mode = ACCEL;
222 double custom_deltas[1024];
223 double speed = 0.0;
224 int dpi = 1000;
225 bool use_averaging = false;
226 const char *filter_type = "linear";
227 accel_profile_func_t profile = NULL;
228 double tp_multiplier = 1.0;
229
230 enum {
231 OPT_HELP = 1,
232 OPT_MODE,
233 OPT_NEVENTS,
234 OPT_MAXDX,
235 OPT_STEP,
236 OPT_SPEED,
237 OPT_DPI,
238 OPT_FILTER,
239 };
240
241 while (1) {
242 int c;
243 int option_index = 0;
244 static struct option long_options[] = {
245 {"help", 0, 0, OPT_HELP },
246 {"mode", 1, 0, OPT_MODE },
247 {"nevents", 1, 0, OPT_NEVENTS },
248 {"maxdx", 1, 0, OPT_MAXDX },
249 {"step", 1, 0, OPT_STEP },
250 {"speed", 1, 0, OPT_SPEED },
251 {"dpi", 1, 0, OPT_DPI },
252 {"filter", 1, 0, OPT_FILTER },
253 {0, 0, 0, 0}
254 };
255
256 c = getopt_long(argc, argv, "",
257 long_options, &option_index);
258 if (c == -1)
259 break;
260
261 switch (c) {
262 case OPT_HELP:
263 usage();
264 exit(0);
265 break;
266 case OPT_MODE:
267 if (streq(optarg, "accel"))
268 mode = ACCEL;
269 else if (streq(optarg, "motion"))
270 mode = MOTION;
271 else if (streq(optarg, "delta"))
272 mode = DELTA;
273 else if (streq(optarg, "sequence"))
274 mode = SEQUENCE;
275 else {
276 usage();
277 return 1;
278 }
279 break;
280 case OPT_NEVENTS:
281 nevents = atoi(optarg);
282 if (nevents == 0) {
283 usage();
284 return 1;
285 }
286 break;
287 case OPT_MAXDX:
288 max_dx = strtod(optarg, NULL);
289 if (max_dx == 0.0) {
290 usage();
291 return 1;
292 }
293 break;
294 case OPT_STEP:
295 step = strtod(optarg, NULL);
296 if (step == 0.0) {
297 usage();
298 return 1;
299 }
300 break;
301 case OPT_SPEED:
302 speed = strtod(optarg, NULL);
303 break;
304 case OPT_DPI:
305 dpi = strtod(optarg, NULL);
306 break;
307 case OPT_FILTER:
308 filter_type = optarg;
309 break;
310 default:
311 usage();
312 exit(1);
313 break;
314 }
315 }
316
317 if (streq(filter_type, "linear")) {
318 filter = create_pointer_accelerator_filter_linear(dpi,
319 use_averaging);
320 profile = pointer_accel_profile_linear;
321 } else if (streq(filter_type, "low-dpi")) {
322 filter = create_pointer_accelerator_filter_linear_low_dpi(dpi,
323 use_averaging);
324 profile = pointer_accel_profile_linear_low_dpi;
325 } else if (streq(filter_type, "touchpad")) {
326 filter = create_pointer_accelerator_filter_touchpad(dpi,
327 0, 0,
328 use_averaging);
329 profile = touchpad_accel_profile_linear;
330 } else if (streq(filter_type, "x230")) {
331 filter = create_pointer_accelerator_filter_lenovo_x230(dpi,
332 use_averaging);
333 profile = touchpad_lenovo_x230_accel_profile;
334 } else if (streq(filter_type, "trackpoint")) {
335 filter = create_pointer_accelerator_filter_trackpoint(tp_multiplier,
336 use_averaging);
337 profile = trackpoint_accel_profile;
338 } else {
339 fprintf(stderr, "Invalid filter type %s\n", filter_type);
340 return 1;
341 }
342
343 assert(filter != NULL);
344 filter_set_speed(filter, speed);
345
346 if (!isatty(STDIN_FILENO)) {
347 char buf[12];
348 mode = SEQUENCE;
349 nevents = 0;
350 memset(custom_deltas, 0, sizeof(custom_deltas));
351
352 while(fgets(buf, sizeof(buf), stdin) && nevents < 1024) {
353 custom_deltas[nevents++] = strtod(buf, NULL);
354 }
355 } else if (optind < argc) {
356 mode = SEQUENCE;
357 nevents = 0;
358 memset(custom_deltas, 0, sizeof(custom_deltas));
359 while (optind < argc)
360 custom_deltas[nevents++] = strtod(argv[optind++], NULL);
361 } else if (mode == SEQUENCE) {
362 usage();
363 return 1;
364 }
365
366 switch (mode) {
367 case ACCEL:
368 print_accel_func(filter, profile, dpi);
369 break;
370 case DELTA:
371 print_ptraccel_deltas(filter, step);
372 break;
373 case MOTION:
374 print_ptraccel_movement(filter, nevents, max_dx, step);
375 break;
376 case SEQUENCE:
377 print_ptraccel_sequence(filter, nevents, custom_deltas);
378 break;
379 }
380
381 filter_destroy(filter);
382
383 return 0;
384 }
385