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