libinput-debug-events.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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. #include "config.h"
  24. #include <errno.h>
  25. #include <getopt.h>
  26. #include <inttypes.h>
  27. #include <libevdev/libevdev.h>
  28. #include <libinput.h>
  29. #include <poll.h>
  30. #include <signal.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <time.h>
  35. #include <unistd.h>
  36. #include "util-libinput.h"
  37. #include "util-macros.h"
  38. #include "util-strings.h"
  39. #include "libinput-version.h"
  40. #include "linux/input.h"
  41. #include "shared.h"
  42. static struct tools_options options;
  43. static bool show_keycodes;
  44. static volatile sig_atomic_t stop = 0;
  45. static bool be_quiet = false;
  46. static bool compress_motion_events = false;
  47. static bool is_tty = false;
  48. #define printq(...) ({ if (!be_quiet) printf(__VA_ARGS__); })
  49. static int
  50. handle_and_print_events(struct libinput *li, const struct libinput_print_options *opts)
  51. {
  52. int rc = -1;
  53. struct libinput_event *ev;
  54. static struct libinput_device *last_device = NULL;
  55. static enum libinput_event_type last_event_type = LIBINPUT_EVENT_NONE;
  56. static size_t event_repeat_count = 0;
  57. static uint32_t last_log_serial = 0;
  58. tools_dispatch(li);
  59. while ((ev = libinput_get_event(li))) {
  60. struct libinput_device *device = libinput_event_get_device(ev);
  61. enum libinput_event_type type = libinput_event_get_type(ev);
  62. if (type == LIBINPUT_EVENT_POINTER_AXIS) {
  63. libinput_event_destroy(ev);
  64. continue;
  65. }
  66. bool is_repeat = false;
  67. switch (type) {
  68. case LIBINPUT_EVENT_POINTER_MOTION:
  69. case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
  70. case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
  71. case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
  72. case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
  73. case LIBINPUT_EVENT_TOUCH_MOTION:
  74. case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
  75. case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
  76. case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
  77. is_repeat = last_event_type == type && last_device == device &&
  78. log_serial == last_log_serial;
  79. break;
  80. default:
  81. break;
  82. }
  83. if (is_repeat) {
  84. event_repeat_count++;
  85. if (compress_motion_events)
  86. printq("\e[1A");
  87. } else {
  88. event_repeat_count = 0;
  89. }
  90. if (type != LIBINPUT_EVENT_TOUCH_FRAME || !compress_motion_events) {
  91. _autofree_ char *event_str =
  92. libinput_event_to_str(ev, event_repeat_count + 1, opts);
  93. switch (type) {
  94. case LIBINPUT_EVENT_DEVICE_ADDED:
  95. tools_device_apply_config(libinput_event_get_device(ev),
  96. &options);
  97. break;
  98. case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: {
  99. struct libinput_event_tablet_tool *tev =
  100. libinput_event_get_tablet_tool_event(ev);
  101. struct libinput_tablet_tool *tool =
  102. libinput_event_tablet_tool_get_tool(tev);
  103. tools_tablet_tool_apply_config(tool, &options);
  104. break;
  105. }
  106. default:
  107. break;
  108. }
  109. printq("%s\n", event_str);
  110. }
  111. last_device = device;
  112. if (type != LIBINPUT_EVENT_TOUCH_FRAME)
  113. last_event_type = type;
  114. last_log_serial = log_serial;
  115. libinput_event_destroy(ev);
  116. rc = 0;
  117. }
  118. fflush(stdout);
  119. return rc;
  120. }
  121. static void
  122. sighandler(int signal, siginfo_t *siginfo, void *userdata)
  123. {
  124. stop = 1;
  125. }
  126. static void
  127. mainloop(struct libinput *li)
  128. {
  129. struct pollfd fds;
  130. fds.fd = libinput_get_fd(li);
  131. fds.events = POLLIN;
  132. fds.revents = 0;
  133. struct libinput_print_options opts = {
  134. .screen_width = 100,
  135. .screen_height = 100,
  136. .show_keycodes = show_keycodes,
  137. .start_time = 0,
  138. };
  139. /* Handle already-pending device added events */
  140. if (handle_and_print_events(li, &opts))
  141. fprintf(stderr,
  142. "Expected device added events on startup but got none. "
  143. "Maybe you don't have the right permissions?\n");
  144. /* time offset starts with our first received event */
  145. if (poll(&fds, 1, -1) > -1) {
  146. struct timespec tp;
  147. clock_gettime(CLOCK_MONOTONIC, &tp);
  148. opts.start_time = tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
  149. do {
  150. handle_and_print_events(li, &opts);
  151. } while (!stop && poll(&fds, 1, -1) > -1);
  152. }
  153. printf("\n");
  154. }
  155. static void
  156. usage(struct option *opts)
  157. {
  158. printf("Usage: libinput debug-events [options] [--udev <seat>|--device /dev/input/event0 ...]\n");
  159. if (opts)
  160. tools_print_usage_option_list(opts);
  161. }
  162. int
  163. main(int argc, char **argv)
  164. {
  165. struct libinput *li;
  166. enum tools_backend backend = BACKEND_NONE;
  167. const char *seat_or_devices[60] = { NULL };
  168. size_t ndevices = 0;
  169. bool grab = false;
  170. bool verbose = false;
  171. struct sigaction act;
  172. tools_init_options(&options);
  173. is_tty = isatty(STDOUT_FILENO);
  174. while (1) {
  175. int c;
  176. int option_index = 0;
  177. enum {
  178. OPT_DEVICE = 1,
  179. OPT_UDEV,
  180. OPT_GRAB,
  181. OPT_VERBOSE,
  182. OPT_SHOW_KEYCODES,
  183. OPT_QUIET,
  184. OPT_COMPRESS_MOTION_EVENTS,
  185. };
  186. /* clang-format off */
  187. static struct option opts[] = {
  188. CONFIGURATION_OPTIONS,
  189. { "help", no_argument, 0, 'h' },
  190. { "show-keycodes", no_argument, 0, OPT_SHOW_KEYCODES },
  191. { "device", required_argument, 0, OPT_DEVICE },
  192. { "udev", required_argument, 0, OPT_UDEV },
  193. { "grab", no_argument, 0, OPT_GRAB },
  194. { "verbose", no_argument, 0, OPT_VERBOSE },
  195. { "quiet", no_argument, 0, OPT_QUIET },
  196. { "compress-motion-events", no_argument, 0, OPT_COMPRESS_MOTION_EVENTS },
  197. { 0, 0, 0, 0},
  198. };
  199. /* clang-format on */
  200. c = getopt_long(argc, argv, "h", opts, &option_index);
  201. if (c == -1)
  202. break;
  203. switch (c) {
  204. case '?':
  205. exit(EXIT_INVALID_USAGE);
  206. break;
  207. case 'h':
  208. usage(opts);
  209. exit(EXIT_SUCCESS);
  210. break;
  211. case OPT_SHOW_KEYCODES:
  212. show_keycodes = true;
  213. break;
  214. case OPT_QUIET:
  215. be_quiet = true;
  216. break;
  217. case OPT_DEVICE:
  218. if (backend == BACKEND_UDEV ||
  219. ndevices >= ARRAY_LENGTH(seat_or_devices)) {
  220. usage(NULL);
  221. return EXIT_INVALID_USAGE;
  222. }
  223. backend = BACKEND_DEVICE;
  224. seat_or_devices[ndevices++] = optarg;
  225. break;
  226. case OPT_UDEV:
  227. if (backend == BACKEND_DEVICE ||
  228. ndevices >= ARRAY_LENGTH(seat_or_devices)) {
  229. usage(NULL);
  230. return EXIT_INVALID_USAGE;
  231. }
  232. backend = BACKEND_UDEV;
  233. seat_or_devices[0] = optarg;
  234. ndevices = 1;
  235. break;
  236. case OPT_GRAB:
  237. grab = true;
  238. break;
  239. case OPT_VERBOSE:
  240. verbose = true;
  241. break;
  242. case OPT_COMPRESS_MOTION_EVENTS:
  243. /* We compress by using ansi escape sequences */
  244. compress_motion_events = is_tty;
  245. break;
  246. default:
  247. if (tools_parse_option(c, optarg, &options) != 0) {
  248. usage(NULL);
  249. return EXIT_INVALID_USAGE;
  250. }
  251. break;
  252. }
  253. }
  254. if (optind < argc) {
  255. if (backend == BACKEND_UDEV) {
  256. usage(NULL);
  257. return EXIT_INVALID_USAGE;
  258. }
  259. backend = BACKEND_DEVICE;
  260. do {
  261. if (ndevices >= ARRAY_LENGTH(seat_or_devices)) {
  262. usage(NULL);
  263. return EXIT_INVALID_USAGE;
  264. }
  265. seat_or_devices[ndevices++] = argv[optind];
  266. } while (++optind < argc);
  267. } else if (backend == BACKEND_NONE) {
  268. backend = BACKEND_UDEV;
  269. seat_or_devices[0] = "seat0";
  270. }
  271. memset(&act, 0, sizeof(act));
  272. act.sa_sigaction = sighandler;
  273. act.sa_flags = SA_SIGINFO;
  274. if (sigaction(SIGINT, &act, NULL) == -1) {
  275. fprintf(stderr,
  276. "Failed to set up signal handling (%s)\n",
  277. strerror(errno));
  278. return EXIT_FAILURE;
  279. }
  280. if (verbose)
  281. printf("libinput version: %s\n", LIBINPUT_VERSION);
  282. bool with_plugins = (options.plugins == 1);
  283. li = tools_open_backend(backend,
  284. seat_or_devices,
  285. verbose,
  286. &grab,
  287. with_plugins,
  288. steal(&options.plugin_paths));
  289. if (!li)
  290. return EXIT_FAILURE;
  291. mainloop(li);
  292. libinput_unref(li);
  293. return EXIT_SUCCESS;
  294. }