1 /*
2 * Copyright © 2014 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 <fcntl.h>
29 #include <fnmatch.h>
30 #include <getopt.h>
31 #include <limits.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <libudev.h>
37 #include <unistd.h>
38
39 #include <libevdev/libevdev.h>
40
41 #include "builddir.h"
42 #include "shared.h"
43 #include "util-macros.h"
44 #include "util-strings.h"
45
46 static uint32_t dispatch_counter = 0;
47
48 void
tools_dispatch(struct libinput *libinput)49 tools_dispatch(struct libinput *libinput)
50 {
51 dispatch_counter++;
52 libinput_dispatch(libinput);
53 }
54
55 LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
56 static void
log_handler(struct libinput *li, enum libinput_log_priority priority, const char *format, va_list args)57 log_handler(struct libinput *li,
58 enum libinput_log_priority priority,
59 const char *format,
60 va_list args)
61 {
62 static int is_tty = -1;
63 static uint32_t last_dispatch_no = 0;
64 static bool color_toggle = false;
65
66 if (is_tty == -1)
67 is_tty = isatty(STDOUT_FILENO);
68
69 if (is_tty) {
70 if (priority >= LIBINPUT_LOG_PRIORITY_ERROR) {
71 printf(ANSI_RED);
72 } else if (priority >= LIBINPUT_LOG_PRIORITY_INFO) {
73 printf(ANSI_HIGHLIGHT);
74 } else if (priority == LIBINPUT_LOG_PRIORITY_DEBUG) {
75 if (dispatch_counter != last_dispatch_no)
76 color_toggle = !color_toggle;
77 uint8_t r = 0,
78 g = 135,
79 b = 95 + (color_toggle ? 80 :0);
80 printf("\x1B[38;2;%u;%u;%um", r, g, b);
81 }
82 }
83
84 if (priority < LIBINPUT_LOG_PRIORITY_INFO) {
85 if (dispatch_counter != last_dispatch_no) {
86 last_dispatch_no = dispatch_counter;
87 printf("%4u: ", dispatch_counter);
88 } else {
89 printf(" %4s ", "...");
90 }
91 }
92 vprintf(format, args);
93
94 if (is_tty)
95 printf(ANSI_NORMAL);
96 }
97
98 void
tools_init_options(struct tools_options *options)99 tools_init_options(struct tools_options *options)
100 {
101 memset(options, 0, sizeof(*options));
102 options->tapping = -1;
103 options->tap_map = -1;
104 options->drag = -1;
105 options->drag_lock = -1;
106 options->natural_scroll = -1;
107 options->left_handed = -1;
108 options->middlebutton = -1;
109 options->dwt = -1;
110 options->dwtp = -1;
111 options->click_method = -1;
112 options->scroll_method = -1;
113 options->scroll_button = -1;
114 options->scroll_button_lock = -1;
115 options->speed = 0.0;
116 options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
117 /* initialize accel args */
118 static double points[] = {0.0, 1.0};
119 options->custom_points = points;
120 options->custom_npoints = ARRAY_LENGTH(points);
121 options->custom_type = LIBINPUT_ACCEL_TYPE_FALLBACK;
122 options->custom_step = 1.0;
123 }
124
125 int
tools_parse_option(int option, const char *optarg, struct tools_options *options)126 tools_parse_option(int option,
127 const char *optarg,
128 struct tools_options *options)
129 {
130 switch(option) {
131 case OPT_TAP_ENABLE:
132 options->tapping = 1;
133 break;
134 case OPT_TAP_DISABLE:
135 options->tapping = 0;
136 break;
137 case OPT_TAP_MAP:
138 if (!optarg)
139 return 1;
140
141 if (streq(optarg, "lrm")) {
142 options->tap_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
143 } else if (streq(optarg, "lmr")) {
144 options->tap_map = LIBINPUT_CONFIG_TAP_MAP_LMR;
145 } else {
146 return 1;
147 }
148 break;
149 case OPT_DRAG_ENABLE:
150 options->drag = 1;
151 break;
152 case OPT_DRAG_DISABLE:
153 options->drag = 0;
154 break;
155 case OPT_DRAG_LOCK_ENABLE:
156 options->drag_lock = 1;
157 break;
158 case OPT_DRAG_LOCK_DISABLE:
159 options->drag_lock = 0;
160 break;
161 case OPT_NATURAL_SCROLL_ENABLE:
162 options->natural_scroll = 1;
163 break;
164 case OPT_NATURAL_SCROLL_DISABLE:
165 options->natural_scroll = 0;
166 break;
167 case OPT_LEFT_HANDED_ENABLE:
168 options->left_handed = 1;
169 break;
170 case OPT_LEFT_HANDED_DISABLE:
171 options->left_handed = 0;
172 break;
173 case OPT_MIDDLEBUTTON_ENABLE:
174 options->middlebutton = 1;
175 break;
176 case OPT_MIDDLEBUTTON_DISABLE:
177 options->middlebutton = 0;
178 break;
179 case OPT_DWT_ENABLE:
180 options->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
181 break;
182 case OPT_DWT_DISABLE:
183 options->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
184 break;
185 case OPT_DWTP_ENABLE:
186 options->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED;
187 break;
188 case OPT_DWTP_DISABLE:
189 options->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED;
190 break;
191 case OPT_CLICK_METHOD:
192 if (!optarg)
193 return 1;
194
195 if (streq(optarg, "none")) {
196 options->click_method =
197 LIBINPUT_CONFIG_CLICK_METHOD_NONE;
198 } else if (streq(optarg, "clickfinger")) {
199 options->click_method =
200 LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
201 } else if (streq(optarg, "buttonareas")) {
202 options->click_method =
203 LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
204 } else {
205 return 1;
206 }
207 break;
208 case OPT_SCROLL_METHOD:
209 if (!optarg)
210 return 1;
211
212 if (streq(optarg, "none")) {
213 options->scroll_method =
214 LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
215 } else if (streq(optarg, "twofinger")) {
216 options->scroll_method =
217 LIBINPUT_CONFIG_SCROLL_2FG;
218 } else if (streq(optarg, "edge")) {
219 options->scroll_method =
220 LIBINPUT_CONFIG_SCROLL_EDGE;
221 } else if (streq(optarg, "button")) {
222 options->scroll_method =
223 LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
224 } else {
225 return 1;
226 }
227 break;
228 case OPT_SCROLL_BUTTON:
229 if (!optarg) {
230 return 1;
231 }
232 options->scroll_button =
233 libevdev_event_code_from_name(EV_KEY,
234 optarg);
235 if (options->scroll_button == -1) {
236 fprintf(stderr,
237 "Invalid button %s\n",
238 optarg);
239 return 1;
240 }
241 break;
242 case OPT_SCROLL_BUTTON_LOCK_ENABLE:
243 options->scroll_button_lock = true;
244 break;
245 case OPT_SCROLL_BUTTON_LOCK_DISABLE:
246 options->scroll_button_lock = false;
247 break;
248 case OPT_SPEED:
249 if (!optarg)
250 return 1;
251 options->speed = atof(optarg);
252 break;
253 case OPT_PROFILE:
254 if (!optarg)
255 return 1;
256
257 if (streq(optarg, "adaptive"))
258 options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
259 else if (streq(optarg, "flat"))
260 options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
261 else if (streq(optarg, "custom"))
262 options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM;
263 else
264 return 1;
265 break;
266 case OPT_DISABLE_SENDEVENTS:
267 if (!optarg)
268 return 1;
269
270 snprintf(options->disable_pattern,
271 sizeof(options->disable_pattern),
272 "%s",
273 optarg);
274 break;
275 case OPT_APPLY_TO:
276 if (!optarg)
277 return 1;
278
279 snprintf(options->match,
280 sizeof(options->match),
281 "%s",
282 optarg);
283 break;
284 case OPT_CUSTOM_POINTS:
285 if (!optarg)
286 return 1;
287 options->custom_points = double_array_from_string(optarg,
288 ";",
289 &options->custom_npoints);
290 if (!options->custom_points || options->custom_npoints < 2) {
291 fprintf(stderr,
292 "Invalid --set-custom-points\n"
293 "Please provide at least 2 points separated by a semicolon\n"
294 " e.g. --set-custom-points=\"1.0;1.5\"\n");
295 return 1;
296 }
297 break;
298 case OPT_CUSTOM_STEP:
299 if (!optarg)
300 return 1;
301 options->custom_step = strtod(optarg, NULL);
302 break;
303 case OPT_CUSTOM_TYPE:
304 if (!optarg)
305 return 1;
306 if (streq(optarg, "fallback"))
307 options->custom_type = LIBINPUT_ACCEL_TYPE_FALLBACK;
308 else if (streq(optarg, "motion"))
309 options->custom_type = LIBINPUT_ACCEL_TYPE_MOTION;
310 else if (streq(optarg, "scroll"))
311 options->custom_type = LIBINPUT_ACCEL_TYPE_SCROLL;
312 else {
313 fprintf(stderr, "Invalid --set-custom-type\n"
314 "Valid custom types: fallback|motion|scroll\n");
315 return 1;
316 }
317 break;
318 case OPT_ROTATION_ANGLE:
319 if (!optarg)
320 return 1;
321
322 if (!safe_atou(optarg, &options->angle)) {
323 fprintf(stderr, "Invalid --set-rotation-angle value\n");
324 return 1;
325 }
326 }
327 return 0;
328 }
329
330 static int
open_restricted(const char *path, int flags, void *user_data)331 open_restricted(const char *path, int flags, void *user_data)
332 {
333 bool *grab = user_data;
334 int fd = open(path, flags);
335
336 if (fd < 0)
337 fprintf(stderr, "Failed to open %s (%s)\n",
338 path, strerror(errno));
339 else if (grab && *grab && ioctl(fd, EVIOCGRAB, (void*)1) == -1)
340 fprintf(stderr, "Grab requested, but failed for %s (%s)\n",
341 path, strerror(errno));
342
343 return fd < 0 ? -errno : fd;
344 }
345
346 static void
close_restricted(int fd, void *user_data)347 close_restricted(int fd, void *user_data)
348 {
349 close(fd);
350 }
351
352 static const struct libinput_interface interface = {
353 .open_restricted = open_restricted,
354 .close_restricted = close_restricted,
355 };
356
357 static struct libinput *
tools_open_udev(const char *seat, bool verbose, bool *grab)358 tools_open_udev(const char *seat, bool verbose, bool *grab)
359 {
360 struct libinput *li;
361 struct udev *udev = udev_new();
362
363 if (!udev) {
364 fprintf(stderr, "Failed to initialize udev\n");
365 return NULL;
366 }
367
368 li = libinput_udev_create_context(&interface, grab, udev);
369 if (!li) {
370 fprintf(stderr, "Failed to initialize context from udev\n");
371 goto out;
372 }
373
374 libinput_log_set_handler(li, log_handler);
375 if (verbose)
376 libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
377
378 if (libinput_udev_assign_seat(li, seat)) {
379 fprintf(stderr, "Failed to set seat\n");
380 libinput_unref(li);
381 li = NULL;
382 goto out;
383 }
384
385 out:
386 udev_unref(udev);
387 return li;
388 }
389
390 static struct libinput *
tools_open_device(const char **paths, bool verbose, bool *grab)391 tools_open_device(const char **paths, bool verbose, bool *grab)
392 {
393 struct libinput_device *device;
394 struct libinput *li;
395 const char **p = paths;
396
397 li = libinput_path_create_context(&interface, grab);
398 if (!li) {
399 fprintf(stderr, "Failed to initialize path context\n");
400 return NULL;
401 }
402
403 if (verbose) {
404 libinput_log_set_handler(li, log_handler);
405 libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
406 }
407
408 while (*p) {
409 device = libinput_path_add_device(li, *p);
410 if (!device) {
411 fprintf(stderr, "Failed to initialize device %s\n", *p);
412 libinput_unref(li);
413 li = NULL;
414 break;
415 }
416 p++;
417 }
418
419 return li;
420 }
421
422 static void
tools_setenv_quirks_dir(void)423 tools_setenv_quirks_dir(void)
424 {
425 char *builddir = builddir_lookup();
426 if (builddir) {
427 setenv("LIBINPUT_QUIRKS_DIR", LIBINPUT_QUIRKS_SRCDIR, 0);
428 free(builddir);
429 }
430 }
431
432 struct libinput *
tools_open_backend(enum tools_backend which, const char **seat_or_device, bool verbose, bool *grab)433 tools_open_backend(enum tools_backend which,
434 const char **seat_or_device,
435 bool verbose,
436 bool *grab)
437 {
438 struct libinput *li;
439
440 tools_setenv_quirks_dir();
441
442 switch (which) {
443 case BACKEND_UDEV:
444 li = tools_open_udev(seat_or_device[0], verbose, grab);
445 break;
446 case BACKEND_DEVICE:
447 li = tools_open_device(seat_or_device, verbose, grab);
448 break;
449 default:
450 abort();
451 }
452
453 return li;
454 }
455
456 void
tools_device_apply_config(struct libinput_device *device, struct tools_options *options)457 tools_device_apply_config(struct libinput_device *device,
458 struct tools_options *options)
459 {
460 const char *name = libinput_device_get_name(device);
461
462 if (libinput_device_config_send_events_get_modes(device) &
463 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED &&
464 fnmatch(options->disable_pattern, name, 0) != FNM_NOMATCH) {
465 libinput_device_config_send_events_set_mode(device,
466 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
467 }
468
469 if (strlen(options->match) > 0 &&
470 fnmatch(options->match, name, 0) == FNM_NOMATCH)
471 return;
472
473 if (options->tapping != -1)
474 libinput_device_config_tap_set_enabled(device, options->tapping);
475 if (options->tap_map != (enum libinput_config_tap_button_map)-1)
476 libinput_device_config_tap_set_button_map(device,
477 options->tap_map);
478 if (options->drag != -1)
479 libinput_device_config_tap_set_drag_enabled(device,
480 options->drag);
481 if (options->drag_lock != -1)
482 libinput_device_config_tap_set_drag_lock_enabled(device,
483 options->drag_lock);
484 if (options->natural_scroll != -1)
485 libinput_device_config_scroll_set_natural_scroll_enabled(device,
486 options->natural_scroll);
487 if (options->left_handed != -1)
488 libinput_device_config_left_handed_set(device, options->left_handed);
489 if (options->middlebutton != -1)
490 libinput_device_config_middle_emulation_set_enabled(device,
491 options->middlebutton);
492
493 if (options->dwt != -1)
494 libinput_device_config_dwt_set_enabled(device, options->dwt);
495
496 if (options->dwtp != -1)
497 libinput_device_config_dwtp_set_enabled(device, options->dwtp);
498
499 if (options->click_method != (enum libinput_config_click_method)-1)
500 libinput_device_config_click_set_method(device, options->click_method);
501
502 if (options->scroll_method != (enum libinput_config_scroll_method)-1)
503 libinput_device_config_scroll_set_method(device,
504 options->scroll_method);
505 if (options->scroll_button != -1)
506 libinput_device_config_scroll_set_button(device,
507 options->scroll_button);
508 if (options->scroll_button_lock != -1)
509 libinput_device_config_scroll_set_button_lock(device,
510 options->scroll_button_lock);
511
512 if (libinput_device_config_accel_is_available(device)) {
513 libinput_device_config_accel_set_speed(device,
514 options->speed);
515 if (options->profile != LIBINPUT_CONFIG_ACCEL_PROFILE_NONE)
516 libinput_device_config_accel_set_profile(device,
517 options->profile);
518 }
519
520 if (options->profile == LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM) {
521 struct libinput_config_accel *config =
522 libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
523 libinput_config_accel_set_points(config,
524 options->custom_type,
525 options->custom_step,
526 options->custom_npoints,
527 options->custom_points);
528 libinput_device_config_accel_apply(device, config);
529 libinput_config_accel_destroy(config);
530 }
531
532 if (options->angle != 0)
533 libinput_device_config_rotation_set_angle(device, options->angle % 360);
534 }
535
536 static char*
find_device(const char *udev_tag)537 find_device(const char *udev_tag)
538 {
539 struct udev *udev;
540 struct udev_enumerate *e = NULL;
541 struct udev_list_entry *entry = NULL;
542 struct udev_device *device;
543 const char *path, *sysname;
544 char *device_node = NULL;
545
546 udev = udev_new();
547 if (!udev)
548 goto out;
549
550 e = udev_enumerate_new(udev);
551 udev_enumerate_add_match_subsystem(e, "input");
552 udev_enumerate_scan_devices(e);
553
554 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
555 path = udev_list_entry_get_name(entry);
556 device = udev_device_new_from_syspath(udev, path);
557 if (!device)
558 continue;
559
560 sysname = udev_device_get_sysname(device);
561 if (!strneq("event", sysname, 5)) {
562 udev_device_unref(device);
563 continue;
564 }
565
566 if (udev_device_get_property_value(device, udev_tag))
567 device_node = safe_strdup(udev_device_get_devnode(device));
568
569 udev_device_unref(device);
570
571 if (device_node)
572 break;
573 }
574 out:
575 udev_enumerate_unref(e);
576 udev_unref(udev);
577
578 return device_node;
579 }
580
581 bool
find_touchpad_device(char *path, size_t path_len)582 find_touchpad_device(char *path, size_t path_len)
583 {
584 char *devnode = find_device("ID_INPUT_TOUCHPAD");
585
586 if (devnode) {
587 snprintf(path, path_len, "%s", devnode);
588 free(devnode);
589 }
590
591 return devnode != NULL;
592 }
593
594 bool
is_touchpad_device(const char *devnode)595 is_touchpad_device(const char *devnode)
596 {
597 struct udev *udev;
598 struct udev_device *dev = NULL;
599 struct stat st;
600 bool is_touchpad = false;
601
602 if (stat(devnode, &st) < 0)
603 return false;
604
605 udev = udev_new();
606 if (!udev)
607 goto out;
608
609 dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
610 if (!dev)
611 goto out;
612
613 is_touchpad = udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD");
614 out:
615 if (dev)
616 udev_device_unref(dev);
617 udev_unref(udev);
618
619 return is_touchpad;
620 }
621
622 static inline void
setup_path(void)623 setup_path(void)
624 {
625 const char *path = getenv("PATH");
626 char new_path[PATH_MAX];
627 const char *extra_path = LIBINPUT_TOOL_PATH;
628 char *builddir = builddir_lookup();
629
630 snprintf(new_path,
631 sizeof(new_path),
632 "%s:%s",
633 builddir ? builddir : extra_path,
634 path ? path : "");
635 setenv("PATH", new_path, 1);
636 free(builddir);
637 }
638
639 int
tools_exec_command(const char *prefix, int real_argc, char **real_argv)640 tools_exec_command(const char *prefix, int real_argc, char **real_argv)
641 {
642 char *argv[64] = {NULL};
643 char executable[128];
644 const char *command;
645 int rc;
646
647 assert((size_t)real_argc < ARRAY_LENGTH(argv));
648
649 command = real_argv[0];
650
651 rc = snprintf(executable,
652 sizeof(executable),
653 "%s-%s",
654 prefix,
655 command);
656 if (rc >= (int)sizeof(executable)) {
657 fprintf(stderr, "Failed to assemble command.\n");
658 return EXIT_FAILURE;
659 }
660
661 argv[0] = executable;
662 for (int i = 1; i < real_argc; i++)
663 argv[i] = real_argv[i];
664
665 setup_path();
666
667 rc = execvp(executable, argv);
668 if (rc) {
669 if (errno == ENOENT) {
670 fprintf(stderr,
671 "libinput: %s is not installed\n",
672 command);
673 return EXIT_INVALID_USAGE;
674 }
675 fprintf(stderr,
676 "Failed to execute '%s' (%s)\n",
677 command,
678 strerror(errno));
679 }
680
681 return EXIT_FAILURE;
682 }
683
684 static void
sprintf_event_codes(char *buf, size_t sz, struct quirks *quirks, enum quirk q)685 sprintf_event_codes(char *buf, size_t sz, struct quirks *quirks, enum quirk q)
686 {
687 const struct quirk_tuples *t;
688 size_t off = 0;
689 int printed;
690 const char *name;
691
692 quirks_get_tuples(quirks, q, &t);
693 name = quirk_get_name(q);
694 printed = snprintf(buf, sz, "%s=", name);
695 assert(printed != -1);
696 off += printed;
697
698 for (size_t i = 0; off < sz && i < t->ntuples; i++) {
699 unsigned int type = t->tuples[i].first;
700 unsigned int code = t->tuples[i].second;
701 bool enable = t->tuples[i].third;
702
703 const char *name = libevdev_event_code_get_name(type, code);
704
705 printed = snprintf(buf + off, sz - off, "%c%s;", enable ? '+' : '-', name);
706 assert(printed != -1);
707 off += printed;
708 }
709 }
710
711 static void
sprintf_input_props(char *buf, size_t sz, struct quirks *quirks, enum quirk q)712 sprintf_input_props(char *buf, size_t sz, struct quirks *quirks, enum quirk q)
713 {
714 const struct quirk_tuples *t;
715 size_t off = 0;
716 int printed;
717 const char *name;
718
719 quirks_get_tuples(quirks, q, &t);
720 name = quirk_get_name(q);
721 printed = snprintf(buf, sz, "%s=", name);
722 assert(printed != -1);
723 off += printed;
724
725 for (size_t i = 0; off < sz && i < t->ntuples; i++) {
726 unsigned int prop = t->tuples[i].first;
727 bool enable = t->tuples[i].second;
728
729 const char *name = libevdev_property_get_name(prop);
730
731 printed = snprintf(buf + off, sz - off, "%c%s;", enable ? '+' : '-', name);
732 assert(printed != -1);
733 off += printed;
734 }
735 }
736
737 void
tools_list_device_quirks(struct quirks_context *ctx, struct udev_device *device, void (*callback)(void *data, const char *str), void *userdata)738 tools_list_device_quirks(struct quirks_context *ctx,
739 struct udev_device *device,
740 void (*callback)(void *data, const char *str),
741 void *userdata)
742 {
743 char buf[256];
744
745 struct quirks *quirks;
746 enum quirk q;
747
748 quirks = quirks_fetch_for_device(ctx, device);
749 if (!quirks)
750 return;
751
752 q = QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD;
753 do {
754 if (quirks_has_quirk(quirks, q)) {
755 const char *name;
756 bool b;
757
758 name = quirk_get_name(q);
759 quirks_get_bool(quirks, q, &b);
760 snprintf(buf, sizeof(buf), "%s=%d", name, b ? 1 : 0);
761 callback(userdata, buf);
762 }
763 } while(++q < _QUIRK_LAST_MODEL_QUIRK_);
764
765 q = QUIRK_ATTR_SIZE_HINT;
766 do {
767 if (quirks_has_quirk(quirks, q)) {
768 const char *name;
769 struct quirk_dimensions dim;
770 struct quirk_range r;
771 uint32_t v;
772 char *s;
773 double d;
774 bool b;
775
776 name = quirk_get_name(q);
777
778 switch (q) {
779 case QUIRK_ATTR_SIZE_HINT:
780 case QUIRK_ATTR_RESOLUTION_HINT:
781 quirks_get_dimensions(quirks, q, &dim);
782 snprintf(buf, sizeof(buf), "%s=%zdx%zd", name, dim.x, dim.y);
783 callback(userdata, buf);
784 break;
785 case QUIRK_ATTR_TOUCH_SIZE_RANGE:
786 case QUIRK_ATTR_PRESSURE_RANGE:
787 quirks_get_range(quirks, q, &r);
788 snprintf(buf, sizeof(buf), "%s=%d:%d", name, r.upper, r.lower);
789 callback(userdata, buf);
790 break;
791 case QUIRK_ATTR_PALM_SIZE_THRESHOLD:
792 case QUIRK_ATTR_PALM_PRESSURE_THRESHOLD:
793 case QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD:
794 case QUIRK_ATTR_THUMB_SIZE_THRESHOLD:
795 quirks_get_uint32(quirks, q, &v);
796 snprintf(buf, sizeof(buf), "%s=%u", name, v);
797 callback(userdata, buf);
798 break;
799 case QUIRK_ATTR_LID_SWITCH_RELIABILITY:
800 case QUIRK_ATTR_KEYBOARD_INTEGRATION:
801 case QUIRK_ATTR_TRACKPOINT_INTEGRATION:
802 case QUIRK_ATTR_TPKBCOMBO_LAYOUT:
803 case QUIRK_ATTR_MSC_TIMESTAMP:
804 quirks_get_string(quirks, q, &s);
805 snprintf(buf, sizeof(buf), "%s=%s", name, s);
806 callback(userdata, buf);
807 break;
808 case QUIRK_ATTR_TRACKPOINT_MULTIPLIER:
809 quirks_get_double(quirks, q, &d);
810 snprintf(buf, sizeof(buf), "%s=%0.2f", name, d);
811 callback(userdata, buf);
812 break;
813 case QUIRK_ATTR_USE_VELOCITY_AVERAGING:
814 case QUIRK_ATTR_TABLET_SMOOTHING:
815 quirks_get_bool(quirks, q, &b);
816 snprintf(buf, sizeof(buf), "%s=%d", name, b);
817 callback(userdata, buf);
818 break;
819 case QUIRK_ATTR_EVENT_CODE:
820 sprintf_event_codes(buf, sizeof(buf), quirks, q);
821 callback(userdata, buf);
822 break;
823 case QUIRK_ATTR_INPUT_PROP:
824 sprintf_input_props(buf, sizeof(buf), quirks, q);
825 callback(userdata, buf);
826 break;
827 default:
828 abort();
829 break;
830 }
831 }
832 } while(++q < _QUIRK_LAST_ATTR_QUIRK_);
833
834 quirks_unref(quirks);
835 }
836