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