1 /*
2 * Copyright © 2018 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 <errno.h>
27 #include <inttypes.h>
28 #include <linux/input.h>
29 #include <libevdev/libevdev.h>
30 #include <libudev.h>
31 #include <sys/signalfd.h>
32 #include <sys/utsname.h>
33 #include <sys/stat.h>
34 #include <string.h>
35 #include <dirent.h>
36 #include <fcntl.h>
37 #include <getopt.h>
38 #include <poll.h>
39 #include <unistd.h>
40 #include <signal.h>
41 #include <stdbool.h>
42 #include <time.h>
43
44 #include "libinput-versionsort.h"
45 #include "libinput-version.h"
46 #include "libinput-git-version.h"
47 #include "shared.h"
48 #include "builddir.h"
49 #include "util-list.h"
50 #include "util-time.h"
51 #include "util-input-event.h"
52 #include "util-macros.h"
53
54 static const int FILE_VERSION_NUMBER = 1;
55
56 /* libinput is not designed to keep events past immediate use so we need to
57 * cache our events. Simplest way to do this is to just cache the printf
58 * output */
59 struct li_event {
60 char msg[256];
61 };
62
63 enum event_type {
64 NONE,
65 EVDEV,
66 LIBINPUT,
67 COMMENT,
68 };
69
70 struct event {
71 enum event_type type;
72 uint64_t time;
73 union {
74 struct input_event evdev;
75 struct li_event libinput;
76 char comment[200];
77 } u;
78 };
79
80 struct record_device {
81 struct list link;
82 char *devnode; /* device node of the source device */
83 struct libevdev *evdev;
84 struct libevdev *evdev_prev; /* previous value, used for EV_ABS
85 deltas */
86 struct libinput_device *device;
87
88 struct event *events;
89 size_t nevents;
90 size_t events_sz;
91
92 struct {
93 bool is_touch_device;
94 uint16_t slot_state;
95 uint16_t last_slot_state;
96 } touch;
97 };
98
99 struct record_context {
100 int timeout;
101 bool show_keycodes;
102
103 uint64_t offset;
104
105 struct list devices;
106 int ndevices;
107
108 char *outfile; /* file name given on cmdline */
109 char *output_file; /* full file name with suffix */
110
111 int out_fd;
112 unsigned int indent;
113
114 struct libinput *libinput;
115 };
116
117 static inline bool
obfuscate_keycode(struct input_event * ev)118 obfuscate_keycode(struct input_event *ev)
119 {
120 switch (ev->type) {
121 case EV_KEY:
122 if (ev->code >= KEY_ESC && ev->code < KEY_ZENKAKUHANKAKU) {
123 ev->code = KEY_A;
124 return true;
125 }
126 break;
127 case EV_MSC:
128 if (ev->code == MSC_SCAN) {
129 ev->value = 30; /* KEY_A scancode */
130 return true;
131 }
132 break;
133 }
134
135 return false;
136 }
137
138 static inline void
indent_push(struct record_context * ctx)139 indent_push(struct record_context *ctx)
140 {
141 ctx->indent += 2;
142 }
143
144 static inline void
indent_pop(struct record_context * ctx)145 indent_pop(struct record_context *ctx)
146 {
147 assert(ctx->indent >= 2);
148 ctx->indent -= 2;
149 }
150
151 /**
152 * Indented dprintf, indentation is given as second parameter.
153 */
154 static inline void
iprintf(const struct record_context * ctx,const char * format,...)155 iprintf(const struct record_context *ctx, const char *format, ...)
156 {
157 va_list args;
158 char fmt[1024];
159 static const char space[] = " ";
160 static const size_t len = sizeof(space);
161 unsigned int indent = ctx->indent;
162 int rc;
163
164 assert(indent < len);
165 assert(strlen(format) > 1);
166
167 /* Special case: if we're printing a new list item, we want less
168 * indentation because the '- ' takes up one level of indentation
169 *
170 * This is only needed because I don't want to deal with open/close
171 * lists statements.
172 */
173 if (format[0] == '-')
174 indent -= 2;
175
176 snprintf(fmt, sizeof(fmt), "%s%s", &space[len - indent - 1], format);
177 va_start(args, format);
178 rc = vdprintf(ctx->out_fd, fmt, args);
179 va_end(args);
180
181 assert(rc != -1 && (unsigned int)rc > indent);
182 }
183
184 /**
185 * Normal printf, just wrapped for the context
186 */
187 static inline void
noiprintf(const struct record_context * ctx,const char * format,...)188 noiprintf(const struct record_context *ctx, const char *format, ...)
189 {
190 va_list args;
191 int rc;
192
193 va_start(args, format);
194 rc = vdprintf(ctx->out_fd, format, args);
195 va_end(args);
196 assert(rc != -1 && (unsigned int)rc > 0);
197 }
198
199 static inline uint64_t
time_offset(struct record_context * ctx,uint64_t time)200 time_offset(struct record_context *ctx, uint64_t time)
201 {
202 return ctx->offset ? time - ctx->offset : 0;
203 }
204
205 static inline void
print_evdev_event(struct record_context * ctx,struct record_device * dev,struct input_event * ev)206 print_evdev_event(struct record_context *ctx,
207 struct record_device *dev,
208 struct input_event *ev)
209 {
210 const char *tname, *cname;
211 bool was_modified = false;
212 char desc[1024];
213 uint64_t time = input_event_time(ev) - ctx->offset;
214
215 input_event_set_time(ev, time);
216
217 /* Don't leak passwords unless the user wants to */
218 if (!ctx->show_keycodes)
219 was_modified = obfuscate_keycode(ev);
220
221 tname = libevdev_event_type_get_name(ev->type);
222 cname = libevdev_event_code_get_name(ev->type, ev->code);
223
224 if (ev->type == EV_SYN && ev->code == SYN_MT_REPORT) {
225 snprintf(desc,
226 sizeof(desc),
227 "++++++++++++ %s (%d) ++++++++++",
228 cname,
229 ev->value);
230 } else if (ev->type == EV_SYN) {
231 static unsigned long last_ms = 0;
232 unsigned long time, dt;
233
234 time = us2ms(input_event_time(ev));
235 dt = time - last_ms;
236 last_ms = time;
237
238 snprintf(desc,
239 sizeof(desc),
240 "------------ %s (%d) ---------- %+ldms",
241 cname,
242 ev->value,
243 dt);
244 } else if (ev->type == EV_ABS) {
245 int oldval = 0;
246 enum { DELTA, SLOT_DELTA, NO_DELTA } want = DELTA;
247 int delta = 0;
248
249 /* We want to print deltas for abs axes but there are a few
250 * that we don't care about for actual deltas because
251 * they're meaningless.
252 *
253 * Also, any slotted axis needs to be printed per slot
254 */
255 switch (ev->code) {
256 case ABS_MT_SLOT:
257 libevdev_set_event_value(dev->evdev_prev,
258 ev->type,
259 ev->code,
260 ev->value);
261 want = NO_DELTA;
262 break;
263 case ABS_MT_TRACKING_ID:
264 case ABS_MT_BLOB_ID:
265 want = NO_DELTA;
266 break;
267 case ABS_MT_TOUCH_MAJOR ... ABS_MT_POSITION_Y:
268 case ABS_MT_PRESSURE ... ABS_MT_TOOL_Y:
269 if (libevdev_get_num_slots(dev->evdev_prev) > 0)
270 want = SLOT_DELTA;
271 break;
272 default:
273 break;
274 }
275
276 switch (want) {
277 case DELTA:
278 oldval = libevdev_get_event_value(dev->evdev_prev,
279 ev->type,
280 ev->code);
281 libevdev_set_event_value(dev->evdev_prev,
282 ev->type,
283 ev->code,
284 ev->value);
285 break;
286 case SLOT_DELTA: {
287 int slot = libevdev_get_current_slot(dev->evdev_prev);
288 oldval = libevdev_get_slot_value(dev->evdev_prev,
289 slot,
290 ev->code);
291 libevdev_set_slot_value(dev->evdev_prev,
292 slot,
293 ev->code,
294 ev->value);
295 break;
296 }
297 case NO_DELTA:
298 break;
299
300 }
301
302 delta = ev->value - oldval;
303
304 switch (want) {
305 case DELTA:
306 case SLOT_DELTA:
307 snprintf(desc,
308 sizeof(desc),
309 "%s / %-20s %6d (%+d)",
310 tname,
311 cname,
312 ev->value,
313 delta);
314 break;
315 case NO_DELTA:
316 snprintf(desc,
317 sizeof(desc),
318 "%s / %-20s %6d",
319 tname,
320 cname,
321 ev->value);
322 break;
323 }
324 } else {
325 snprintf(desc,
326 sizeof(desc),
327 "%s / %-20s %6d%s",
328 tname,
329 cname,
330 ev->value,
331 was_modified ? " (obfuscated)" : "");
332 }
333
334 iprintf(ctx,
335 "- [%3lu, %6u, %3d, %3d, %7d] # %s\n",
336 ev->input_event_sec,
337 (unsigned int)ev->input_event_usec,
338 ev->type,
339 ev->code,
340 ev->value,
341 desc);
342 }
343
344 #define resize(array_, sz_) \
345 { \
346 size_t new_size = (sz_) + 1000; \
347 void *tmp = realloc((array_), new_size * sizeof(*(array_))); \
348 assert(tmp); \
349 (array_) = tmp; \
350 (sz_) = new_size; \
351 }
352
353 static inline size_t
handle_evdev_frame(struct record_context * ctx,struct record_device * d)354 handle_evdev_frame(struct record_context *ctx, struct record_device *d)
355 {
356 struct libevdev *evdev = d->evdev;
357 struct input_event e;
358 size_t count = 0;
359 uint32_t last_time = 0;
360 struct event *event;
361
362 while (libevdev_next_event(evdev,
363 LIBEVDEV_READ_FLAG_NORMAL,
364 &e) == LIBEVDEV_READ_STATUS_SUCCESS) {
365 uint64_t time = input_event_time(&e);
366
367 if (ctx->offset == 0)
368 ctx->offset = time;
369 else
370 time = time_offset(ctx, time);
371
372 if (d->nevents == d->events_sz)
373 resize(d->events, d->events_sz);
374
375 event = &d->events[d->nevents++];
376 event->type = EVDEV;
377 event->time = time;
378 event->u.evdev = e;
379 count++;
380
381 if (d->touch.is_touch_device &&
382 e.type == EV_ABS &&
383 e.code == ABS_MT_TRACKING_ID) {
384 unsigned int slot = libevdev_get_current_slot(evdev);
385 assert(slot < sizeof(d->touch.slot_state) * 8);
386
387 if (e.value != -1)
388 d->touch.slot_state |= 1 << slot;
389 else
390 d->touch.slot_state &= ~(1 << slot);
391 }
392
393 last_time = event->time;
394
395 if (e.type == EV_SYN && e.code == SYN_REPORT)
396 break;
397 }
398
399 if (d->touch.slot_state != d->touch.last_slot_state) {
400 d->touch.last_slot_state = d->touch.slot_state;
401 if (d->nevents == d->events_sz)
402 resize(d->events, d->events_sz);
403
404 if (d->touch.slot_state == 0) {
405 event = &d->events[d->nevents++];
406 event->type = COMMENT;
407 event->time = last_time;
408 snprintf(event->u.comment,
409 sizeof(event->u.comment),
410 " # Touch device in neutral state\n");
411 count++;
412 }
413 }
414
415 return count;
416 }
417
418 static void
buffer_device_notify(struct record_context * ctx,struct libinput_event * e,struct event * event)419 buffer_device_notify(struct record_context *ctx,
420 struct libinput_event *e,
421 struct event *event)
422 {
423 struct libinput_device *dev = libinput_event_get_device(e);
424 struct libinput_seat *seat = libinput_device_get_seat(dev);
425 const char *type = NULL;
426
427 switch(libinput_event_get_type(e)) {
428 case LIBINPUT_EVENT_DEVICE_ADDED:
429 type = "DEVICE_ADDED";
430 break;
431 case LIBINPUT_EVENT_DEVICE_REMOVED:
432 type = "DEVICE_REMOVED";
433 break;
434 default:
435 abort();
436 }
437
438 event->time = 0;
439 snprintf(event->u.libinput.msg,
440 sizeof(event->u.libinput.msg),
441 "{type: %s, seat: %5s, logical_seat: %7s}",
442 type,
443 libinput_seat_get_physical_name(seat),
444 libinput_seat_get_logical_name(seat));
445 }
446
447 static void
buffer_key_event(struct record_context * ctx,struct libinput_event * e,struct event * event)448 buffer_key_event(struct record_context *ctx,
449 struct libinput_event *e,
450 struct event *event)
451 {
452 struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(e);
453 enum libinput_key_state state;
454 uint32_t key;
455 uint64_t time;
456 const char *type;
457
458 switch(libinput_event_get_type(e)) {
459 case LIBINPUT_EVENT_KEYBOARD_KEY:
460 type = "KEYBOARD_KEY";
461 break;
462 default:
463 abort();
464 }
465
466 time = time_offset(ctx, libinput_event_keyboard_get_time_usec(k));
467 state = libinput_event_keyboard_get_key_state(k);
468
469 key = libinput_event_keyboard_get_key(k);
470 if (!ctx->show_keycodes &&
471 (key >= KEY_ESC && key < KEY_ZENKAKUHANKAKU))
472 key = -1;
473
474 event->time = time;
475 snprintf(event->u.libinput.msg,
476 sizeof(event->u.libinput.msg),
477 "{time: %ld.%06ld, type: %s, key: %d, state: %s}",
478 (long)(time / (int)1e6),
479 (long)(time % (int)1e6),
480 type,
481 key,
482 state == LIBINPUT_KEY_STATE_PRESSED ? "pressed" : "released");
483 }
484
485 static void
buffer_motion_event(struct record_context * ctx,struct libinput_event * e,struct event * event)486 buffer_motion_event(struct record_context *ctx,
487 struct libinput_event *e,
488 struct event *event)
489 {
490 struct libinput_event_pointer *p = libinput_event_get_pointer_event(e);
491 double x = libinput_event_pointer_get_dx(p),
492 y = libinput_event_pointer_get_dy(p);
493 double uax = libinput_event_pointer_get_dx_unaccelerated(p),
494 uay = libinput_event_pointer_get_dy_unaccelerated(p);
495 uint64_t time;
496 const char *type;
497
498 switch(libinput_event_get_type(e)) {
499 case LIBINPUT_EVENT_POINTER_MOTION:
500 type = "POINTER_MOTION";
501 break;
502 default:
503 abort();
504 }
505
506 time = time_offset(ctx, libinput_event_pointer_get_time_usec(p));
507 event->time = time;
508 snprintf(event->u.libinput.msg,
509 sizeof(event->u.libinput.msg),
510 "{time: %ld.%06ld, type: %s, delta: [%6.2f, %6.2f], unaccel: [%6.2f, %6.2f]}",
511 (long)(time / (int)1e6),
512 (long)(time % (int)1e6),
513 type,
514 x, y,
515 uax, uay);
516 }
517
518 static void
buffer_absmotion_event(struct record_context * ctx,struct libinput_event * e,struct event * event)519 buffer_absmotion_event(struct record_context *ctx,
520 struct libinput_event *e,
521 struct event *event)
522 {
523 struct libinput_event_pointer *p = libinput_event_get_pointer_event(e);
524 double x = libinput_event_pointer_get_absolute_x(p),
525 y = libinput_event_pointer_get_absolute_y(p);
526 double tx = libinput_event_pointer_get_absolute_x_transformed(p, 100),
527 ty = libinput_event_pointer_get_absolute_y_transformed(p, 100);
528 uint64_t time;
529 const char *type;
530
531 switch(libinput_event_get_type(e)) {
532 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
533 type = "POINTER_MOTION_ABSOLUTE";
534 break;
535 default:
536 abort();
537 }
538
539 time = time_offset(ctx, libinput_event_pointer_get_time_usec(p));
540
541 event->time = time;
542 snprintf(event->u.libinput.msg,
543 sizeof(event->u.libinput.msg),
544 "{time: %ld.%06ld, type: %s, point: [%6.2f, %6.2f], transformed: [%6.2f, %6.2f]}",
545 (long)(time / (int)1e6),
546 (long)(time % (int)1e6),
547 type,
548 x, y,
549 tx, ty);
550 }
551
552 static void
buffer_pointer_button_event(struct record_context * ctx,struct libinput_event * e,struct event * event)553 buffer_pointer_button_event(struct record_context *ctx,
554 struct libinput_event *e,
555 struct event *event)
556 {
557 struct libinput_event_pointer *p = libinput_event_get_pointer_event(e);
558 enum libinput_button_state state;
559 int button;
560 uint64_t time;
561 const char *type;
562
563 switch(libinput_event_get_type(e)) {
564 case LIBINPUT_EVENT_POINTER_BUTTON:
565 type = "POINTER_BUTTON";
566 break;
567 default:
568 abort();
569 }
570
571 time = time_offset(ctx, libinput_event_pointer_get_time_usec(p));
572 button = libinput_event_pointer_get_button(p);
573 state = libinput_event_pointer_get_button_state(p);
574
575 event->time = time;
576 snprintf(event->u.libinput.msg,
577 sizeof(event->u.libinput.msg),
578 "{time: %ld.%06ld, type: %s, button: %d, state: %s, seat_count: %u}",
579 (long)(time / (int)1e6),
580 (long)(time % (int)1e6),
581 type,
582 button,
583 state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
584 libinput_event_pointer_get_seat_button_count(p));
585 }
586
587 static void
buffer_pointer_axis_event(struct record_context * ctx,struct libinput_event * e,struct event * event)588 buffer_pointer_axis_event(struct record_context *ctx,
589 struct libinput_event *e,
590 struct event *event)
591 {
592 struct libinput_event_pointer *p = libinput_event_get_pointer_event(e);
593 uint64_t time;
594 const char *type, *source;
595 double h = 0, v = 0;
596 int hd = 0, vd = 0;
597
598 switch(libinput_event_get_type(e)) {
599 case LIBINPUT_EVENT_POINTER_AXIS:
600 type = "POINTER_AXIS";
601 break;
602 default:
603 abort();
604 }
605
606 time = time_offset(ctx, libinput_event_pointer_get_time_usec(p));
607 if (libinput_event_pointer_has_axis(p,
608 LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
609 h = libinput_event_pointer_get_axis_value(p,
610 LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
611 hd = libinput_event_pointer_get_axis_value_discrete(p,
612 LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
613 }
614 if (libinput_event_pointer_has_axis(p,
615 LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
616 v = libinput_event_pointer_get_axis_value(p,
617 LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
618 vd = libinput_event_pointer_get_axis_value_discrete(p,
619 LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
620 }
621 switch(libinput_event_pointer_get_axis_source(p)) {
622 case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: source = "wheel"; break;
623 case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: source = "finger"; break;
624 case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: source = "continuous"; break;
625 case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: source = "wheel-tilt"; break;
626 default:
627 source = "unknown";
628 break;
629 }
630
631 event->time = time;
632 snprintf(event->u.libinput.msg,
633 sizeof(event->u.libinput.msg),
634 "{time: %ld.%06ld, type: %s, axes: [%2.2f, %2.2f], discrete: [%d, %d], source: %s}",
635 (long)(time / (int)1e6),
636 (long)(time % (int)1e6),
637 type,
638 h, v,
639 hd, vd,
640 source);
641 }
642
643 static void
buffer_touch_event(struct record_context * ctx,struct libinput_event * e,struct event * event)644 buffer_touch_event(struct record_context *ctx,
645 struct libinput_event *e,
646 struct event *event)
647 {
648 enum libinput_event_type etype = libinput_event_get_type(e);
649 struct libinput_event_touch *t = libinput_event_get_touch_event(e);
650 const char *type;
651 double x, y;
652 double tx, ty;
653 uint64_t time;
654 int32_t slot, seat_slot;
655
656 switch(etype) {
657 case LIBINPUT_EVENT_TOUCH_DOWN:
658 type = "TOUCH_DOWN";
659 break;
660 case LIBINPUT_EVENT_TOUCH_UP:
661 type = "TOUCH_UP";
662 break;
663 case LIBINPUT_EVENT_TOUCH_MOTION:
664 type = "TOUCH_MOTION";
665 break;
666 case LIBINPUT_EVENT_TOUCH_CANCEL:
667 type = "TOUCH_CANCEL";
668 break;
669 case LIBINPUT_EVENT_TOUCH_FRAME:
670 type = "TOUCH_FRAME";
671 break;
672 default:
673 abort();
674 }
675
676 time = time_offset(ctx, libinput_event_touch_get_time_usec(t));
677
678 if (etype != LIBINPUT_EVENT_TOUCH_FRAME) {
679 slot = libinput_event_touch_get_slot(t);
680 seat_slot = libinput_event_touch_get_seat_slot(t);
681 }
682 event->time = time;
683
684 switch (etype) {
685 case LIBINPUT_EVENT_TOUCH_FRAME:
686 snprintf(event->u.libinput.msg,
687 sizeof(event->u.libinput.msg),
688 "{time: %ld.%06ld, type: %s}",
689 (long)(time / (int)1e6),
690 (long)(time % (int)1e6),
691 type);
692 break;
693 case LIBINPUT_EVENT_TOUCH_DOWN:
694 case LIBINPUT_EVENT_TOUCH_MOTION:
695 x = libinput_event_touch_get_x(t);
696 y = libinput_event_touch_get_y(t);
697 tx = libinput_event_touch_get_x_transformed(t, 100);
698 ty = libinput_event_touch_get_y_transformed(t, 100);
699 snprintf(event->u.libinput.msg,
700 sizeof(event->u.libinput.msg),
701 "{time: %ld.%06ld, type: %s, slot: %d, seat_slot: %d, point: [%6.2f, %6.2f], transformed: [%6.2f, %6.2f]}",
702 (long)(time / (int)1e6),
703 (long)(time % (int)1e6),
704 type,
705 slot,
706 seat_slot,
707 x, y,
708 tx, ty);
709 break;
710 case LIBINPUT_EVENT_TOUCH_UP:
711 case LIBINPUT_EVENT_TOUCH_CANCEL:
712 snprintf(event->u.libinput.msg,
713 sizeof(event->u.libinput.msg),
714 "{time: %ld.%06ld, type: %s, slot: %d, seat_slot: %d}",
715 (long)(time / (int)1e6),
716 (long)(time % (int)1e6),
717 type,
718 slot,
719 seat_slot);
720 break;
721 default:
722 abort();
723 }
724 }
725
726 static void
buffer_gesture_event(struct record_context * ctx,struct libinput_event * e,struct event * event)727 buffer_gesture_event(struct record_context *ctx,
728 struct libinput_event *e,
729 struct event *event)
730 {
731 enum libinput_event_type etype = libinput_event_get_type(e);
732 struct libinput_event_gesture *g = libinput_event_get_gesture_event(e);
733 const char *type;
734 uint64_t time;
735
736 switch(etype) {
737 case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
738 type = "GESTURE_PINCH_BEGIN";
739 break;
740 case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
741 type = "GESTURE_PINCH_UPDATE";
742 break;
743 case LIBINPUT_EVENT_GESTURE_PINCH_END:
744 type = "GESTURE_PINCH_END";
745 break;
746 case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
747 type = "GESTURE_SWIPE_BEGIN";
748 break;
749 case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
750 type = "GESTURE_SWIPE_UPDATE";
751 break;
752 case LIBINPUT_EVENT_GESTURE_SWIPE_END:
753 type = "GESTURE_SWIPE_END";
754 break;
755 default:
756 abort();
757 }
758
759 time = time_offset(ctx, libinput_event_gesture_get_time_usec(g));
760 event->time = time;
761
762 switch (etype) {
763 case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
764 case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
765 case LIBINPUT_EVENT_GESTURE_PINCH_END:
766 snprintf(event->u.libinput.msg,
767 sizeof(event->u.libinput.msg),
768 "{time: %ld.%06ld, type: %s, nfingers: %d, "
769 "delta: [%6.2f, %6.2f], unaccel: [%6.2f, %6.2f], "
770 "angle_delta: %6.2f, scale: %6.2f}",
771 (long)(time / (int)1e6),
772 (long)(time % (int)1e6),
773 type,
774 libinput_event_gesture_get_finger_count(g),
775 libinput_event_gesture_get_dx(g),
776 libinput_event_gesture_get_dy(g),
777 libinput_event_gesture_get_dx_unaccelerated(g),
778 libinput_event_gesture_get_dy_unaccelerated(g),
779 libinput_event_gesture_get_angle_delta(g),
780 libinput_event_gesture_get_scale(g)
781 );
782 break;
783 case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
784 case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
785 case LIBINPUT_EVENT_GESTURE_SWIPE_END:
786 snprintf(event->u.libinput.msg,
787 sizeof(event->u.libinput.msg),
788 "{time: %ld.%06ld, type: %s, nfingers: %d, "
789 "delta: [%6.2f, %6.2f], unaccel: [%6.2f, %6.2f]}",
790 (long)(time / (int)1e6),
791 (long)(time % (int)1e6),
792 type,
793 libinput_event_gesture_get_finger_count(g),
794 libinput_event_gesture_get_dx(g),
795 libinput_event_gesture_get_dy(g),
796 libinput_event_gesture_get_dx_unaccelerated(g),
797 libinput_event_gesture_get_dy_unaccelerated(g)
798 );
799 break;
800 default:
801 abort();
802 }
803 }
804
805 static char *
buffer_tablet_axes(struct libinput_event_tablet_tool * t)806 buffer_tablet_axes(struct libinput_event_tablet_tool *t)
807 {
808 const int MAX_AXES = 10;
809 struct libinput_tablet_tool *tool;
810 char *s = NULL;
811 int idx = 0;
812 int len;
813 double x, y;
814 char **strv;
815
816 tool = libinput_event_tablet_tool_get_tool(t);
817
818 strv = zalloc(MAX_AXES * sizeof *strv);
819
820 x = libinput_event_tablet_tool_get_x(t);
821 y = libinput_event_tablet_tool_get_y(t);
822 len = xasprintf(&strv[idx++], "point: [%.2f, %.2f]", x, y);
823 if (len <= 0)
824 goto out;
825
826 if (libinput_tablet_tool_has_tilt(tool)) {
827 x = libinput_event_tablet_tool_get_tilt_x(t);
828 y = libinput_event_tablet_tool_get_tilt_y(t);
829 len = xasprintf(&strv[idx++], "tilt: [%.2f, %.2f]", x, y);
830 if (len <= 0)
831 goto out;
832 }
833
834 if (libinput_tablet_tool_has_distance(tool) ||
835 libinput_tablet_tool_has_pressure(tool)) {
836 double dist, pressure;
837
838 dist = libinput_event_tablet_tool_get_distance(t);
839 pressure = libinput_event_tablet_tool_get_pressure(t);
840 if (dist)
841 len = xasprintf(&strv[idx++], "distance: %.2f", dist);
842 else
843 len = xasprintf(&strv[idx++], "pressure: %.2f", pressure);
844 if (len <= 0)
845 goto out;
846 }
847
848 if (libinput_tablet_tool_has_rotation(tool)) {
849 double rotation;
850
851 rotation = libinput_event_tablet_tool_get_rotation(t);
852 len = xasprintf(&strv[idx++], "rotation: %.2f", rotation);
853 if (len <= 0)
854 goto out;
855 }
856
857 if (libinput_tablet_tool_has_slider(tool)) {
858 double slider;
859
860 slider = libinput_event_tablet_tool_get_slider_position(t);
861 len = xasprintf(&strv[idx++], "slider: %.2f", slider);
862 if (len <= 0)
863 goto out;
864
865 }
866
867 if (libinput_tablet_tool_has_wheel(tool)) {
868 double wheel;
869 int delta;
870
871 wheel = libinput_event_tablet_tool_get_wheel_delta(t);
872 len = xasprintf(&strv[idx++], "wheel: %.2f", wheel);
873 if (len <= 0)
874 goto out;
875
876 delta = libinput_event_tablet_tool_get_wheel_delta_discrete(t);
877 len = xasprintf(&strv[idx++], "wheel-discrete: %d", delta);
878 if (len <= 0)
879 goto out;
880 }
881
882 assert(idx < MAX_AXES);
883
884 s = strv_join(strv, ", ");
885 out:
886 strv_free(strv);
887 return s;
888 }
889
890 static void
buffer_tablet_tool_proximity_event(struct record_context * ctx,struct libinput_event * e,struct event * event)891 buffer_tablet_tool_proximity_event(struct record_context *ctx,
892 struct libinput_event *e,
893 struct event *event)
894 {
895 struct libinput_event_tablet_tool *t =
896 libinput_event_get_tablet_tool_event(e);
897 struct libinput_tablet_tool *tool =
898 libinput_event_tablet_tool_get_tool(t);
899 uint64_t time;
900 const char *type, *tool_type;
901 char *axes;
902 char caps[10] = {0};
903 enum libinput_tablet_tool_proximity_state prox;
904 size_t idx;
905
906 switch (libinput_event_get_type(e)) {
907 case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
908 type = "TABLET_TOOL_PROXIMITY";
909 break;
910 default:
911 abort();
912 }
913
914 switch (libinput_tablet_tool_get_type(tool)) {
915 case LIBINPUT_TABLET_TOOL_TYPE_PEN:
916 tool_type = "pen";
917 break;
918 case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
919 tool_type = "eraser";
920 break;
921 case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
922 tool_type = "brush";
923 break;
924 case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
925 tool_type = "brush";
926 break;
927 case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
928 tool_type = "airbrush";
929 break;
930 case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
931 tool_type = "mouse";
932 break;
933 case LIBINPUT_TABLET_TOOL_TYPE_LENS:
934 tool_type = "lens";
935 break;
936 default:
937 tool_type = "unknown";
938 break;
939 }
940
941 prox = libinput_event_tablet_tool_get_proximity_state(t);
942 time = time_offset(ctx, libinput_event_tablet_tool_get_time_usec(t));
943 axes = buffer_tablet_axes(t);
944
945 idx = 0;
946 if (libinput_tablet_tool_has_pressure(tool))
947 caps[idx++] = 'p';
948 if (libinput_tablet_tool_has_distance(tool))
949 caps[idx++] = 'd';
950 if (libinput_tablet_tool_has_tilt(tool))
951 caps[idx++] = 't';
952 if (libinput_tablet_tool_has_rotation(tool))
953 caps[idx++] = 'r';
954 if (libinput_tablet_tool_has_slider(tool))
955 caps[idx++] = 's';
956 if (libinput_tablet_tool_has_wheel(tool))
957 caps[idx++] = 'w';
958 assert(idx <= ARRAY_LENGTH(caps));
959
960 event->time = time;
961 snprintf(event->u.libinput.msg,
962 sizeof(event->u.libinput.msg),
963 "{time: %ld.%06ld, type: %s, proximity: %s, tool-type: %s, serial: %" PRIu64 ", axes: %s, %s}",
964 (long)(time / (int)1e6),
965 (long)(time % (int)1e6),
966 type,
967 prox ? "in" : "out",
968 tool_type,
969 libinput_tablet_tool_get_serial(tool),
970 caps,
971 axes);
972 free(axes);
973 }
974
975 static void
buffer_tablet_tool_button_event(struct record_context * ctx,struct libinput_event * e,struct event * event)976 buffer_tablet_tool_button_event(struct record_context *ctx,
977 struct libinput_event *e,
978 struct event *event)
979 {
980 struct libinput_event_tablet_tool *t =
981 libinput_event_get_tablet_tool_event(e);
982 uint64_t time;
983 const char *type;
984 uint32_t button;
985 enum libinput_button_state state;
986
987 switch(libinput_event_get_type(e)) {
988 case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
989 type = "TABLET_TOOL_BUTTON";
990 break;
991 default:
992 abort();
993 }
994
995
996 button = libinput_event_tablet_tool_get_button(t);
997 state = libinput_event_tablet_tool_get_button_state(t);
998 time = time_offset(ctx, libinput_event_tablet_tool_get_time_usec(t));
999
1000 event->time = time;
1001 snprintf(event->u.libinput.msg,
1002 sizeof(event->u.libinput.msg),
1003 "{time: %ld.%06ld, type: %s, button: %d, state: %s}",
1004 (long)(time / (int)1e6),
1005 (long)(time % (int)1e6),
1006 type,
1007 button,
1008 state ? "pressed" : "released");
1009 }
1010
1011 static void
buffer_tablet_tool_event(struct record_context * ctx,struct libinput_event * e,struct event * event)1012 buffer_tablet_tool_event(struct record_context *ctx,
1013 struct libinput_event *e,
1014 struct event *event)
1015 {
1016 struct libinput_event_tablet_tool *t =
1017 libinput_event_get_tablet_tool_event(e);
1018 uint64_t time;
1019 const char *type;
1020 char *axes;
1021 enum libinput_tablet_tool_tip_state tip;
1022 char btn_buffer[30] = {0};
1023
1024 switch(libinput_event_get_type(e)) {
1025 case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
1026 type = "TABLET_TOOL_AXIS";
1027 break;
1028 case LIBINPUT_EVENT_TABLET_TOOL_TIP:
1029 type = "TABLET_TOOL_TIP";
1030 break;
1031 case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
1032 type = "TABLET_TOOL_BUTTON";
1033 break;
1034 default:
1035 abort();
1036 }
1037
1038 if (libinput_event_get_type(e) == LIBINPUT_EVENT_TABLET_TOOL_BUTTON) {
1039 uint32_t button;
1040 enum libinput_button_state state;
1041
1042 button = libinput_event_tablet_tool_get_button(t);
1043 state = libinput_event_tablet_tool_get_button_state(t);
1044 snprintf(btn_buffer, sizeof(btn_buffer),
1045 ", button: %d, state: %s\n",
1046 button,
1047 state ? "pressed" : "released");
1048 }
1049
1050 tip = libinput_event_tablet_tool_get_tip_state(t);
1051 time = time_offset(ctx, libinput_event_tablet_tool_get_time_usec(t));
1052 axes = buffer_tablet_axes(t);
1053
1054 event->time = time;
1055 snprintf(event->u.libinput.msg,
1056 sizeof(event->u.libinput.msg),
1057 "{time: %ld.%06ld, type: %s%s, tip: %s, %s}",
1058 (long)(time / (int)1e6),
1059 (long)(time % (int)1e6),
1060 type,
1061 btn_buffer, /* may be empty string */
1062 tip ? "down" : "up",
1063 axes);
1064 free(axes);
1065 }
1066
1067 static void
buffer_tablet_pad_button_event(struct record_context * ctx,struct libinput_event * e,struct event * event)1068 buffer_tablet_pad_button_event(struct record_context *ctx,
1069 struct libinput_event *e,
1070 struct event *event)
1071 {
1072 struct libinput_event_tablet_pad *p =
1073 libinput_event_get_tablet_pad_event(e);
1074 struct libinput_tablet_pad_mode_group *group;
1075 enum libinput_button_state state;
1076 unsigned int button, mode;
1077 const char *type;
1078 uint64_t time;
1079
1080 switch(libinput_event_get_type(e)) {
1081 case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
1082 type = "TABLET_PAD_BUTTON";
1083 break;
1084 default:
1085 abort();
1086 }
1087
1088 time = time_offset(ctx, libinput_event_tablet_pad_get_time_usec(p));
1089 button = libinput_event_tablet_pad_get_button_number(p),
1090 state = libinput_event_tablet_pad_get_button_state(p);
1091 mode = libinput_event_tablet_pad_get_mode(p);
1092 group = libinput_event_tablet_pad_get_mode_group(p);
1093
1094 event->time = time;
1095 snprintf(event->u.libinput.msg,
1096 sizeof(event->u.libinput.msg),
1097 "{time: %ld.%06ld, type: %s, button: %d, state: %s, mode: %d, is-toggle: %s}",
1098 (long)(time / (int)1e6),
1099 (long)(time % (int)1e6),
1100 type,
1101 button,
1102 state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
1103 mode,
1104 libinput_tablet_pad_mode_group_button_is_toggle(group, button) ? "true" : "false"
1105 );
1106
1107
1108 }
1109
1110 static void
buffer_tablet_pad_ringstrip_event(struct record_context * ctx,struct libinput_event * e,struct event * event)1111 buffer_tablet_pad_ringstrip_event(struct record_context *ctx,
1112 struct libinput_event *e,
1113 struct event *event)
1114 {
1115 struct libinput_event_tablet_pad *p =
1116 libinput_event_get_tablet_pad_event(e);
1117 const char *source = NULL;
1118 unsigned int mode, number;
1119 const char *type;
1120 uint64_t time;
1121 double pos;
1122
1123 switch(libinput_event_get_type(e)) {
1124 case LIBINPUT_EVENT_TABLET_PAD_RING:
1125 type = "TABLET_PAD_RING";
1126 number = libinput_event_tablet_pad_get_ring_number(p);
1127 pos = libinput_event_tablet_pad_get_ring_position(p);
1128
1129 switch (libinput_event_tablet_pad_get_ring_source(p)) {
1130 case LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER:
1131 source = "finger";
1132 break;
1133 case LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN:
1134 source = "unknown";
1135 break;
1136 }
1137 break;
1138 case LIBINPUT_EVENT_TABLET_PAD_STRIP:
1139 type = "TABLET_PAD_STRIP";
1140 number = libinput_event_tablet_pad_get_strip_number(p);
1141 pos = libinput_event_tablet_pad_get_strip_position(p);
1142
1143 switch (libinput_event_tablet_pad_get_strip_source(p)) {
1144 case LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER:
1145 source = "finger";
1146 break;
1147 case LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN:
1148 source = "unknown";
1149 break;
1150 }
1151 break;
1152 default:
1153 abort();
1154 }
1155
1156 time = time_offset(ctx, libinput_event_tablet_pad_get_time_usec(p));
1157 mode = libinput_event_tablet_pad_get_mode(p);
1158
1159 event->time = time;
1160 snprintf(event->u.libinput.msg,
1161 sizeof(event->u.libinput.msg),
1162 "{time: %ld.%06ld, type: %s, number: %d, position: %.2f, source: %s, mode: %d}",
1163 (long)(time / (int)1e6),
1164 (long)(time % (int)1e6),
1165 type,
1166 number,
1167 pos,
1168 source,
1169 mode);
1170 }
1171
1172 static void
buffer_switch_event(struct record_context * ctx,struct libinput_event * e,struct event * event)1173 buffer_switch_event(struct record_context *ctx,
1174 struct libinput_event *e,
1175 struct event *event)
1176 {
1177 struct libinput_event_switch *s = libinput_event_get_switch_event(e);
1178 enum libinput_switch_state state;
1179 uint32_t sw;
1180 const char *type;
1181 uint64_t time;
1182
1183 switch(libinput_event_get_type(e)) {
1184 case LIBINPUT_EVENT_SWITCH_TOGGLE:
1185 type = "SWITCH_TOGGLE";
1186 break;
1187 default:
1188 abort();
1189 }
1190
1191 time = time_offset(ctx, libinput_event_switch_get_time_usec(s));
1192 sw = libinput_event_switch_get_switch(s);
1193 state = libinput_event_switch_get_switch_state(s);
1194
1195 event->time = time;
1196 snprintf(event->u.libinput.msg,
1197 sizeof(event->u.libinput.msg),
1198 "{time: %ld.%06ld, type: %s, switch: %d, state: %s}",
1199 (long)(time / (int)1e6),
1200 (long)(time % (int)1e6),
1201 type,
1202 sw,
1203 state == LIBINPUT_SWITCH_STATE_ON ? "on" : "off");
1204 }
1205
1206 static void
buffer_libinput_event(struct record_context * ctx,struct libinput_event * e,struct event * event)1207 buffer_libinput_event(struct record_context *ctx,
1208 struct libinput_event *e,
1209 struct event *event)
1210 {
1211 switch (libinput_event_get_type(e)) {
1212 case LIBINPUT_EVENT_NONE:
1213 abort();
1214 case LIBINPUT_EVENT_DEVICE_ADDED:
1215 case LIBINPUT_EVENT_DEVICE_REMOVED:
1216 buffer_device_notify(ctx, e, event);
1217 break;
1218 case LIBINPUT_EVENT_KEYBOARD_KEY:
1219 buffer_key_event(ctx, e, event);
1220 break;
1221 case LIBINPUT_EVENT_POINTER_MOTION:
1222 buffer_motion_event(ctx, e, event);
1223 break;
1224 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
1225 buffer_absmotion_event(ctx, e, event);
1226 break;
1227 case LIBINPUT_EVENT_POINTER_BUTTON:
1228 buffer_pointer_button_event(ctx, e, event);
1229 break;
1230 case LIBINPUT_EVENT_POINTER_AXIS:
1231 buffer_pointer_axis_event(ctx, e, event);
1232 break;
1233 case LIBINPUT_EVENT_TOUCH_DOWN:
1234 case LIBINPUT_EVENT_TOUCH_UP:
1235 case LIBINPUT_EVENT_TOUCH_MOTION:
1236 case LIBINPUT_EVENT_TOUCH_CANCEL:
1237 case LIBINPUT_EVENT_TOUCH_FRAME:
1238 buffer_touch_event(ctx, e, event);
1239 break;
1240 case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
1241 case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
1242 case LIBINPUT_EVENT_GESTURE_PINCH_END:
1243 case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
1244 case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
1245 case LIBINPUT_EVENT_GESTURE_SWIPE_END:
1246 buffer_gesture_event(ctx, e, event);
1247 break;
1248 case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
1249 buffer_tablet_tool_proximity_event(ctx, e, event);
1250 break;
1251 case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
1252 case LIBINPUT_EVENT_TABLET_TOOL_TIP:
1253 buffer_tablet_tool_event(ctx, e, event);
1254 break;
1255 case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
1256 buffer_tablet_tool_button_event(ctx, e, event);
1257 break;
1258 case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
1259 buffer_tablet_pad_button_event(ctx, e, event);
1260 break;
1261 case LIBINPUT_EVENT_TABLET_PAD_RING:
1262 case LIBINPUT_EVENT_TABLET_PAD_STRIP:
1263 buffer_tablet_pad_ringstrip_event(ctx, e, event);
1264 break;
1265 case LIBINPUT_EVENT_SWITCH_TOGGLE:
1266 buffer_switch_event(ctx, e, event);
1267 break;
1268 default:
1269 break;
1270 }
1271 }
1272
1273 static void
print_cached_events(struct record_context * ctx,struct record_device * d,unsigned int offset,int len)1274 print_cached_events(struct record_context *ctx,
1275 struct record_device *d,
1276 unsigned int offset,
1277 int len)
1278 {
1279 unsigned int idx;
1280 enum event_type last_type;
1281 uint64_t last_time;
1282
1283 if (len == -1)
1284 len = d->nevents - offset;
1285 assert(offset + len <= d->nevents);
1286
1287 if (offset == 0) {
1288 last_type = NONE;
1289 last_time = 0;
1290 } else {
1291 last_type = d->events[offset - 1].type;
1292 last_time = d->events[offset - 1].time;
1293 }
1294
1295 idx = offset;
1296 indent_push(ctx);
1297 while (idx < offset + len) {
1298 struct event *e;
1299
1300 e = &d->events[idx++];
1301 if (e->type != last_type || e->time != last_time) {
1302 bool new_frame = false;
1303
1304 if (last_time == 0 || e->time != last_time)
1305 new_frame = true;
1306
1307 indent_pop(ctx);
1308
1309 switch(e->type) {
1310 case EVDEV:
1311 if (new_frame)
1312 iprintf(ctx, "- evdev:\n");
1313 else
1314 iprintf(ctx, "evdev:\n");
1315 break;
1316 case LIBINPUT:
1317 if (new_frame)
1318 iprintf(ctx, "- libinput:\n");
1319 else
1320 iprintf(ctx, "libinput:\n");
1321 break;
1322 case COMMENT:
1323 break;
1324 default:
1325 abort();
1326 }
1327 indent_push(ctx);
1328
1329 last_type = e->type;
1330 }
1331
1332 switch (e->type) {
1333 case EVDEV:
1334 print_evdev_event(ctx, d, &e->u.evdev);
1335 break;
1336 case LIBINPUT:
1337 iprintf(ctx, "- %s\n", e->u.libinput.msg);
1338 break;
1339 case COMMENT:
1340 iprintf(ctx, "%s", e->u.comment);
1341 break;
1342 default:
1343 abort();
1344 }
1345
1346 last_time = e->time;
1347 }
1348 indent_pop(ctx);
1349 }
1350
1351 static inline size_t
handle_libinput_events(struct record_context * ctx,struct record_device * d)1352 handle_libinput_events(struct record_context *ctx,
1353 struct record_device *d)
1354 {
1355 struct libinput_event *e;
1356 size_t count = 0;
1357 struct record_device *current = d;
1358
1359 libinput_dispatch(ctx->libinput);
1360
1361 while ((e = libinput_get_event(ctx->libinput)) != NULL) {
1362 struct libinput_device *device = libinput_event_get_device(e);
1363 struct event *event;
1364
1365 if (device != current->device) {
1366 struct record_device *tmp;
1367 bool found = false;
1368 list_for_each(tmp, &ctx->devices, link) {
1369 if (device == tmp->device) {
1370 current = tmp;
1371 found = true;
1372 break;
1373 }
1374 }
1375 assert(found);
1376 }
1377
1378 if (current->nevents == current->events_sz)
1379 resize(current->events, current->events_sz);
1380
1381 event = ¤t->events[current->nevents++];
1382 event->type = LIBINPUT;
1383 buffer_libinput_event(ctx, e, event);
1384
1385 if (current == d)
1386 count++;
1387 libinput_event_destroy(e);
1388 }
1389 return count;
1390 }
1391
1392 static inline void
handle_events(struct record_context * ctx,struct record_device * d,bool print)1393 handle_events(struct record_context *ctx, struct record_device *d, bool print)
1394 {
1395 while(true) {
1396 size_t first_idx = d->nevents;
1397 size_t evcount = 0,
1398 licount = 0;
1399
1400 evcount = handle_evdev_frame(ctx, d);
1401
1402 if (ctx->libinput)
1403 licount = handle_libinput_events(ctx, d);
1404
1405 if (evcount == 0 && licount == 0)
1406 break;
1407
1408 if (!print)
1409 continue;
1410
1411 print_cached_events(ctx, d, first_idx, evcount + licount);
1412 }
1413 }
1414
1415 static inline void
print_libinput_header(struct record_context * ctx)1416 print_libinput_header(struct record_context *ctx)
1417 {
1418 iprintf(ctx, "libinput:\n");
1419 indent_push(ctx);
1420 iprintf(ctx, "version: \"%s\"\n", LIBINPUT_VERSION);
1421 iprintf(ctx, "git: \"%s\"\n", LIBINPUT_GIT_VERSION);
1422 if (ctx->timeout > 0)
1423 iprintf(ctx, "autorestart: %d\n", ctx->timeout);
1424 indent_pop(ctx);
1425 }
1426
1427 static inline void
print_system_header(struct record_context * ctx)1428 print_system_header(struct record_context *ctx)
1429 {
1430 struct utsname u;
1431 const char *kernel = "unknown";
1432 FILE *dmi, *osrelease;
1433 char dmistr[2048] = "unknown";
1434
1435 iprintf(ctx, "system:\n");
1436 indent_push(ctx);
1437
1438 /* /etc/os-release version and distribution name */
1439 osrelease = fopen("/etc/os-release", "r");
1440 if (!osrelease)
1441 osrelease = fopen("/usr/lib/os-release", "r");
1442 if (osrelease) {
1443 char *distro = NULL, *version = NULL;
1444 char osrstr[256] = "unknown";
1445
1446 while (fgets(osrstr, sizeof(osrstr), osrelease)) {
1447 osrstr[strlen(osrstr) - 1] = '\0'; /* linebreak */
1448
1449 if (!distro && strneq(osrstr, "ID=", 3))
1450 distro = strstrip(&osrstr[3], "\"'");
1451 else if (!version && strneq(osrstr, "VERSION_ID=", 11))
1452 version = strstrip(&osrstr[11], "\"'");
1453
1454 if (distro && version) {
1455 iprintf(ctx, "os: \"%s:%s\"\n", distro, version);
1456 break;
1457 }
1458 }
1459 free(distro);
1460 free(version);
1461 fclose(osrelease);
1462 }
1463
1464 /* kernel version */
1465 if (uname(&u) != -1)
1466 kernel = u.release;
1467 iprintf(ctx, "kernel: \"%s\"\n", kernel);
1468
1469 /* dmi modalias */
1470 dmi = fopen("/sys/class/dmi/id/modalias", "r");
1471 if (dmi) {
1472 if (fgets(dmistr, sizeof(dmistr), dmi)) {
1473 dmistr[strlen(dmistr) - 1] = '\0'; /* linebreak */
1474 } else {
1475 sprintf(dmistr, "unknown");
1476 }
1477 fclose(dmi);
1478 }
1479 iprintf(ctx, "dmi: \"%s\"\n", dmistr);
1480 indent_pop(ctx);
1481 }
1482
1483 static inline void
print_header(struct record_context * ctx)1484 print_header(struct record_context *ctx)
1485 {
1486 iprintf(ctx, "version: %d\n", FILE_VERSION_NUMBER);
1487 iprintf(ctx, "ndevices: %d\n", ctx->ndevices);
1488 print_libinput_header(ctx);
1489 print_system_header(ctx);
1490 }
1491
1492 static inline void
print_description_abs(struct record_context * ctx,struct libevdev * dev,unsigned int code)1493 print_description_abs(struct record_context *ctx,
1494 struct libevdev *dev,
1495 unsigned int code)
1496 {
1497 const struct input_absinfo *abs;
1498
1499 abs = libevdev_get_abs_info(dev, code);
1500 assert(abs);
1501
1502 iprintf(ctx, "# Value %6d\n", abs->value);
1503 iprintf(ctx, "# Min %6d\n", abs->minimum);
1504 iprintf(ctx, "# Max %6d\n", abs->maximum);
1505 iprintf(ctx, "# Fuzz %6d\n", abs->fuzz);
1506 iprintf(ctx, "# Flat %6d\n", abs->flat);
1507 iprintf(ctx, "# Resolution %6d\n", abs->resolution);
1508 }
1509
1510 static inline void
print_description_state(struct record_context * ctx,struct libevdev * dev,unsigned int type,unsigned int code)1511 print_description_state(struct record_context *ctx,
1512 struct libevdev *dev,
1513 unsigned int type,
1514 unsigned int code)
1515 {
1516 int state = libevdev_get_event_value(dev, type, code);
1517 iprintf(ctx, "# State %d\n", state);
1518 }
1519
1520 static inline void
print_description_codes(struct record_context * ctx,struct libevdev * dev,unsigned int type)1521 print_description_codes(struct record_context *ctx,
1522 struct libevdev *dev,
1523 unsigned int type)
1524 {
1525 int max;
1526
1527 max = libevdev_event_type_get_max(type);
1528 if (max == -1)
1529 return;
1530
1531 iprintf(ctx,
1532 "# Event type %d (%s)\n",
1533 type,
1534 libevdev_event_type_get_name(type));
1535
1536 if (type == EV_SYN)
1537 return;
1538
1539 for (unsigned int code = 0; code <= (unsigned int)max; code++) {
1540 if (!libevdev_has_event_code(dev, type, code))
1541 continue;
1542
1543 iprintf(ctx,
1544 "# Event code %d (%s)\n",
1545 code,
1546 libevdev_event_code_get_name(type,
1547 code));
1548
1549 switch (type) {
1550 case EV_ABS:
1551 print_description_abs(ctx, dev, code);
1552 break;
1553 case EV_LED:
1554 case EV_SW:
1555 print_description_state(ctx, dev, type, code);
1556 break;
1557 }
1558 }
1559 }
1560
1561 static inline void
print_description(struct record_context * ctx,struct libevdev * dev)1562 print_description(struct record_context *ctx, struct libevdev *dev)
1563 {
1564 const struct input_absinfo *x, *y;
1565
1566 iprintf(ctx, "# Name: %s\n", libevdev_get_name(dev));
1567 iprintf(ctx,
1568 "# ID: bus %#02x vendor %#02x product %#02x version %#02x\n",
1569 libevdev_get_id_bustype(dev),
1570 libevdev_get_id_vendor(dev),
1571 libevdev_get_id_product(dev),
1572 libevdev_get_id_version(dev));
1573
1574 x = libevdev_get_abs_info(dev, ABS_X);
1575 y = libevdev_get_abs_info(dev, ABS_Y);
1576 if (x && y) {
1577 if (x->resolution && y->resolution) {
1578 int w, h;
1579
1580 w = (x->maximum - x->minimum)/x->resolution;
1581 h = (y->maximum - y->minimum)/y->resolution;
1582 iprintf(ctx, "# Size in mm: %dx%d\n", w, h);
1583 } else {
1584 iprintf(ctx,
1585 "# Size in mm: unknown, missing resolution\n");
1586 }
1587 }
1588
1589 iprintf(ctx, "# Supported Events:\n");
1590
1591 for (unsigned int type = 0; type < EV_CNT; type++) {
1592 if (!libevdev_has_event_type(dev, type))
1593 continue;
1594
1595 print_description_codes(ctx, dev, type);
1596 }
1597
1598 iprintf(ctx, "# Properties:\n");
1599
1600 for (unsigned int prop = 0; prop < INPUT_PROP_CNT; prop++) {
1601 if (libevdev_has_property(dev, prop)) {
1602 iprintf(ctx,
1603 "# Property %d (%s)\n",
1604 prop,
1605 libevdev_property_get_name(prop));
1606 }
1607 }
1608 }
1609
1610 static inline void
print_bits_info(struct record_context * ctx,struct libevdev * dev)1611 print_bits_info(struct record_context *ctx, struct libevdev *dev)
1612 {
1613 iprintf(ctx, "name: \"%s\"\n", libevdev_get_name(dev));
1614 iprintf(ctx,
1615 "id: [%d, %d, %d, %d]\n",
1616 libevdev_get_id_bustype(dev),
1617 libevdev_get_id_vendor(dev),
1618 libevdev_get_id_product(dev),
1619 libevdev_get_id_version(dev));
1620 }
1621
1622 static inline void
print_bits_absinfo(struct record_context * ctx,struct libevdev * dev)1623 print_bits_absinfo(struct record_context *ctx, struct libevdev *dev)
1624 {
1625 const struct input_absinfo *abs;
1626
1627 if (!libevdev_has_event_type(dev, EV_ABS))
1628 return;
1629
1630 iprintf(ctx, "absinfo:\n");
1631 indent_push(ctx);
1632
1633 for (unsigned int code = 0; code < ABS_CNT; code++) {
1634 abs = libevdev_get_abs_info(dev, code);
1635 if (!abs)
1636 continue;
1637
1638 iprintf(ctx,
1639 "%d: [%d, %d, %d, %d, %d]\n",
1640 code,
1641 abs->minimum,
1642 abs->maximum,
1643 abs->fuzz,
1644 abs->flat,
1645 abs->resolution);
1646 }
1647 indent_pop(ctx);
1648 }
1649
1650 static inline void
print_bits_codes(struct record_context * ctx,struct libevdev * dev,unsigned int type)1651 print_bits_codes(struct record_context *ctx,
1652 struct libevdev *dev,
1653 unsigned int type)
1654 {
1655 int max;
1656 bool first = true;
1657
1658 max = libevdev_event_type_get_max(type);
1659 if (max == -1)
1660 return;
1661
1662 iprintf(ctx, "%d: [", type);
1663
1664 for (unsigned int code = 0; code <= (unsigned int)max; code++) {
1665 if (!libevdev_has_event_code(dev, type, code))
1666 continue;
1667
1668 noiprintf(ctx, "%s%d", first ? "" : ", ", code);
1669 first = false;
1670 }
1671
1672 noiprintf(ctx, "] # %s\n", libevdev_event_type_get_name(type));
1673 }
1674
1675 static inline void
print_bits_types(struct record_context * ctx,struct libevdev * dev)1676 print_bits_types(struct record_context *ctx, struct libevdev *dev)
1677 {
1678 iprintf(ctx, "codes:\n");
1679 indent_push(ctx);
1680 for (unsigned int type = 0; type < EV_CNT; type++) {
1681 if (!libevdev_has_event_type(dev, type))
1682 continue;
1683 print_bits_codes(ctx, dev, type);
1684 }
1685 indent_pop(ctx);
1686 }
1687
1688 static inline void
print_bits_props(struct record_context * ctx,struct libevdev * dev)1689 print_bits_props(struct record_context *ctx, struct libevdev *dev)
1690 {
1691 bool first = true;
1692
1693 iprintf(ctx, "properties: [");
1694 for (unsigned int prop = 0; prop < INPUT_PROP_CNT; prop++) {
1695 if (libevdev_has_property(dev, prop)) {
1696 noiprintf(ctx, "%s%d", first ? "" : ", ", prop);
1697 first = false;
1698 }
1699 }
1700 noiprintf(ctx, "]\n"); /* last entry, no comma */
1701 }
1702
1703 static inline void
print_evdev_description(struct record_context * ctx,struct record_device * dev)1704 print_evdev_description(struct record_context *ctx, struct record_device *dev)
1705 {
1706 struct libevdev *evdev = dev->evdev;
1707
1708 iprintf(ctx, "evdev:\n");
1709 indent_push(ctx);
1710
1711 print_description(ctx, evdev);
1712 print_bits_info(ctx, evdev);
1713 print_bits_types(ctx, evdev);
1714 print_bits_absinfo(ctx, evdev);
1715 print_bits_props(ctx, evdev);
1716
1717 indent_pop(ctx);
1718 }
1719
1720 static inline void
print_hid_report_descriptor(struct record_context * ctx,struct record_device * dev)1721 print_hid_report_descriptor(struct record_context *ctx,
1722 struct record_device *dev)
1723 {
1724 const char *prefix = "/dev/input/event";
1725 const char *node;
1726 char syspath[PATH_MAX];
1727 unsigned char buf[1024];
1728 int len;
1729 int fd;
1730 bool first = true;
1731
1732 /* we take the shortcut rather than the proper udev approach, the
1733 report_descriptor is available in sysfs and two devices up from
1734 our device. 2 digits for the event number should be enough.
1735 This approach won't work for /dev/input/by-id devices. */
1736 if (!strstartswith(dev->devnode, prefix) ||
1737 strlen(dev->devnode) > strlen(prefix) + 2)
1738 return;
1739
1740 node = &dev->devnode[strlen(prefix)];
1741 len = snprintf(syspath,
1742 sizeof(syspath),
1743 "/sys/class/input/event%s/device/device/report_descriptor",
1744 node);
1745 if (len < 55 || len > 56)
1746 return;
1747
1748 fd = open(syspath, O_RDONLY);
1749 if (fd == -1)
1750 return;
1751
1752 iprintf(ctx, "hid: [");
1753
1754 while ((len = read(fd, buf, sizeof(buf))) > 0) {
1755 for (int i = 0; i < len; i++) {
1756 /* YAML requires decimal */
1757 noiprintf(ctx, "%s%u",first ? "" : ", ", buf[i]);
1758 first = false;
1759 }
1760 }
1761 noiprintf(ctx, " ]\n");
1762
1763 close(fd);
1764 }
1765
1766 static inline void
print_udev_properties(struct record_context * ctx,struct record_device * dev)1767 print_udev_properties(struct record_context *ctx, struct record_device *dev)
1768 {
1769 struct udev *udev = NULL;
1770 struct udev_device *udev_device = NULL;
1771 struct udev_list_entry *entry;
1772 struct stat st;
1773
1774 if (stat(dev->devnode, &st) < 0)
1775 return;
1776
1777 udev = udev_new();
1778 if (!udev)
1779 goto out;
1780
1781 udev_device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
1782 if (!udev_device)
1783 goto out;
1784
1785 iprintf(ctx, "udev:\n");
1786 indent_push(ctx);
1787
1788 iprintf(ctx, "properties:\n");
1789 indent_push(ctx);
1790
1791 entry = udev_device_get_properties_list_entry(udev_device);
1792 while (entry) {
1793 const char *key, *value;
1794
1795 key = udev_list_entry_get_name(entry);
1796
1797 if (strneq(key, "ID_INPUT", 8) ||
1798 strneq(key, "LIBINPUT", 8) ||
1799 strneq(key, "EVDEV_ABS", 9) ||
1800 strneq(key, "MOUSE_DPI", 9) ||
1801 strneq(key, "POINTINGSTICK_", 14)) {
1802 value = udev_list_entry_get_value(entry);
1803 iprintf(ctx, "- %s=%s\n", key, value);
1804 }
1805
1806 entry = udev_list_entry_get_next(entry);
1807 }
1808
1809 indent_pop(ctx);
1810 indent_pop(ctx);
1811 out:
1812 udev_device_unref(udev_device);
1813 udev_unref(udev);
1814 }
1815
1816 static void
quirks_log_handler(struct libinput * this_is_null,enum libinput_log_priority priority,const char * format,va_list args)1817 quirks_log_handler(struct libinput *this_is_null,
1818 enum libinput_log_priority priority,
1819 const char *format,
1820 va_list args)
1821 {
1822 }
1823
1824 static void
list_print(void * userdata,const char * val)1825 list_print(void *userdata, const char *val)
1826 {
1827 struct record_context *ctx = userdata;
1828
1829 iprintf(ctx, "- %s\n", val);
1830 }
1831
1832 static inline void
print_device_quirks(struct record_context * ctx,struct record_device * dev)1833 print_device_quirks(struct record_context *ctx, struct record_device *dev)
1834 {
1835 struct udev *udev = NULL;
1836 struct udev_device *udev_device = NULL;
1837 struct stat st;
1838 struct quirks_context *quirks;
1839 const char *data_path = LIBINPUT_QUIRKS_DIR;
1840 const char *override_file = LIBINPUT_QUIRKS_OVERRIDE_FILE;
1841 char *builddir = NULL;
1842
1843 if (stat(dev->devnode, &st) < 0)
1844 return;
1845
1846 if ((builddir = builddir_lookup())) {
1847 setenv("LIBINPUT_QUIRKS_DIR", LIBINPUT_QUIRKS_SRCDIR, 0);
1848 data_path = LIBINPUT_QUIRKS_SRCDIR;
1849 override_file = NULL;
1850 }
1851
1852 free(builddir);
1853
1854 quirks = quirks_init_subsystem(data_path,
1855 override_file,
1856 quirks_log_handler,
1857 NULL,
1858 QLOG_CUSTOM_LOG_PRIORITIES);
1859 if (!quirks) {
1860 fprintf(stderr,
1861 "Failed to initialize the device quirks. "
1862 "Please see the above errors "
1863 "and/or re-run with --verbose for more details\n");
1864 return;
1865 }
1866
1867 udev = udev_new();
1868 if (!udev)
1869 goto out;
1870
1871 udev_device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
1872 if (!udev_device)
1873 goto out;
1874
1875 iprintf(ctx, "quirks:\n");
1876 indent_push(ctx);
1877
1878 tools_list_device_quirks(quirks, udev_device, list_print, ctx);
1879
1880 indent_pop(ctx);
1881 out:
1882 udev_device_unref(udev_device);
1883 udev_unref(udev);
1884 quirks_context_unref(quirks);
1885 }
1886 static inline void
print_libinput_description(struct record_context * ctx,struct record_device * dev)1887 print_libinput_description(struct record_context *ctx,
1888 struct record_device *dev)
1889 {
1890 struct libinput_device *device = dev->device;
1891 double w, h;
1892 struct cap {
1893 enum libinput_device_capability cap;
1894 const char *name;
1895 } caps[] = {
1896 {LIBINPUT_DEVICE_CAP_KEYBOARD, "keyboard"},
1897 {LIBINPUT_DEVICE_CAP_POINTER, "pointer"},
1898 {LIBINPUT_DEVICE_CAP_TOUCH, "touch"},
1899 {LIBINPUT_DEVICE_CAP_TABLET_TOOL, "tablet"},
1900 {LIBINPUT_DEVICE_CAP_TABLET_PAD, "pad"},
1901 {LIBINPUT_DEVICE_CAP_GESTURE, "gesture"},
1902 {LIBINPUT_DEVICE_CAP_SWITCH, "switch"},
1903 };
1904 struct cap *cap;
1905 bool is_first;
1906
1907 if (!device)
1908 return;
1909
1910 iprintf(ctx, "libinput:\n");
1911 indent_push(ctx);
1912
1913 if (libinput_device_get_size(device, &w, &h) == 0)
1914 iprintf(ctx, "size: [%.f, %.f]\n", w, h);
1915
1916 iprintf(ctx, "capabilities: [");
1917 is_first = true;
1918 ARRAY_FOR_EACH(caps, cap) {
1919 if (!libinput_device_has_capability(device, cap->cap))
1920 continue;
1921 noiprintf(ctx, "%s%s", is_first ? "" : ", ", cap->name);
1922 is_first = false;
1923 }
1924 noiprintf(ctx, "]\n");
1925
1926 /* Configuration options should be printed here, but since they
1927 * don't reflect the user-configured ones their usefulness is
1928 * questionable. We need the ability to specify the options like in
1929 * debug-events.
1930 */
1931 indent_pop(ctx);
1932 }
1933
1934 static inline void
print_device_description(struct record_context * ctx,struct record_device * dev)1935 print_device_description(struct record_context *ctx, struct record_device *dev)
1936 {
1937 iprintf(ctx, "- node: %s\n", dev->devnode);
1938
1939 print_evdev_description(ctx, dev);
1940 print_hid_report_descriptor(ctx, dev);
1941 print_udev_properties(ctx, dev);
1942 print_device_quirks(ctx, dev);
1943 print_libinput_description(ctx, dev);
1944 }
1945
is_event_node(const struct dirent * dir)1946 static int is_event_node(const struct dirent *dir) {
1947 return strneq(dir->d_name, "event", 5);
1948 }
1949
1950 static inline char *
select_device(void)1951 select_device(void)
1952 {
1953 struct dirent **namelist;
1954 int ndev, selected_device;
1955 int rc;
1956 char *device_path;
1957 bool has_eaccess = false;
1958 int available_devices = 0;
1959
1960 ndev = scandir("/dev/input", &namelist, is_event_node, versionsort);
1961 if (ndev <= 0)
1962 return NULL;
1963
1964 fprintf(stderr, "Available devices:\n");
1965 for (int i = 0; i < ndev; i++) {
1966 struct libevdev *device;
1967 char path[PATH_MAX];
1968 int fd = -1;
1969
1970 snprintf(path,
1971 sizeof(path),
1972 "/dev/input/%s",
1973 namelist[i]->d_name);
1974 fd = open(path, O_RDONLY);
1975 if (fd < 0) {
1976 if (errno == EACCES)
1977 has_eaccess = true;
1978 continue;
1979 }
1980
1981 rc = libevdev_new_from_fd(fd, &device);
1982 close(fd);
1983 if (rc != 0)
1984 continue;
1985
1986 fprintf(stderr, "%s: %s\n", path, libevdev_get_name(device));
1987 libevdev_free(device);
1988 available_devices++;
1989 }
1990
1991 for (int i = 0; i < ndev; i++)
1992 free(namelist[i]);
1993 free(namelist);
1994
1995 if (available_devices == 0) {
1996 fprintf(stderr, "No devices available. ");
1997 if (has_eaccess)
1998 fprintf(stderr, "Please re-run as root.");
1999 fprintf(stderr, "\n");
2000 return NULL;
2001 }
2002
2003 fprintf(stderr, "Select the device event number: ");
2004 rc = scanf("%d", &selected_device);
2005
2006 if (rc != 1 || selected_device < 0)
2007 return NULL;
2008
2009 rc = xasprintf(&device_path, "/dev/input/event%d", selected_device);
2010 if (rc == -1)
2011 return NULL;
2012
2013 return device_path;
2014 }
2015
2016 static inline char **
all_devices(void)2017 all_devices(void)
2018 {
2019 struct dirent **namelist;
2020 int ndev;
2021 int rc;
2022 char **devices = NULL;
2023
2024 ndev = scandir("/dev/input", &namelist, is_event_node, versionsort);
2025 if (ndev <= 0)
2026 return NULL;
2027
2028 devices = zalloc((ndev + 1)* sizeof *devices); /* NULL-terminated */
2029 for (int i = 0; i < ndev; i++) {
2030 char *device_path;
2031
2032 rc = xasprintf(&device_path,
2033 "/dev/input/%s",
2034 namelist[i]->d_name);
2035 if (rc == -1)
2036 goto error;
2037
2038 devices[i] = device_path;
2039 }
2040
2041 return devices;
2042
2043 error:
2044 if (devices)
2045 strv_free(devices);
2046 return NULL;
2047 }
2048
2049 static char *
init_output_file(const char * file,bool is_prefix)2050 init_output_file(const char *file, bool is_prefix)
2051 {
2052 char name[PATH_MAX];
2053
2054 assert(file != NULL);
2055
2056 if (is_prefix) {
2057 struct tm *tm;
2058 time_t t;
2059 char suffix[64];
2060
2061 t = time(NULL);
2062 tm = localtime(&t);
2063 strftime(suffix, sizeof(suffix), "%F-%T", tm);
2064 snprintf(name,
2065 sizeof(name),
2066 "%s.%s",
2067 file,
2068 suffix);
2069 } else {
2070 snprintf(name, sizeof(name), "%s", file);
2071 }
2072
2073 return strdup(name);
2074 }
2075
2076 static bool
open_output_file(struct record_context * ctx,bool is_prefix)2077 open_output_file(struct record_context *ctx, bool is_prefix)
2078 {
2079 int out_fd;
2080
2081 if (ctx->outfile) {
2082 char *fname = init_output_file(ctx->outfile, is_prefix);
2083 ctx->output_file = fname;
2084 out_fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
2085 if (out_fd < 0)
2086 return false;
2087 } else {
2088 ctx->output_file = safe_strdup("stdout");
2089 out_fd = STDOUT_FILENO;
2090 }
2091
2092 ctx->out_fd = out_fd;
2093
2094 return true;
2095 }
2096
2097 static inline void
print_progress_bar(void)2098 print_progress_bar(void)
2099 {
2100 static uint8_t foo = 0;
2101
2102 if (!isatty(STDERR_FILENO))
2103 return;
2104
2105 if (++foo > 20)
2106 foo = 1;
2107 fprintf(stderr, "\rReceiving events: [%*s%*s]", foo, "*", 21 - foo, " ");
2108 }
2109
2110 static int
mainloop(struct record_context * ctx)2111 mainloop(struct record_context *ctx)
2112 {
2113 bool autorestart = (ctx->timeout > 0);
2114 struct pollfd fds[ctx->ndevices + 2];
2115 unsigned int nfds = 0;
2116 struct record_device *d = NULL;
2117 struct record_device *first_device = NULL;
2118 struct timespec ts;
2119 sigset_t mask;
2120
2121 assert(ctx->timeout != 0);
2122 assert(!list_empty(&ctx->devices));
2123
2124 sigemptyset(&mask);
2125 sigaddset(&mask, SIGINT);
2126 sigaddset(&mask, SIGQUIT);
2127 sigprocmask(SIG_BLOCK, &mask, NULL);
2128
2129 fds[0].fd = signalfd(-1, &mask, SFD_NONBLOCK);
2130 fds[0].events = POLLIN;
2131 fds[0].revents = 0;
2132 assert(fds[0].fd != -1);
2133 nfds++;
2134
2135 if (ctx->libinput) {
2136 fds[1].fd = libinput_get_fd(ctx->libinput);
2137 fds[1].events = POLLIN;
2138 fds[1].revents = 0;
2139 nfds++;
2140 assert(nfds == 2);
2141 }
2142
2143 list_for_each(d, &ctx->devices, link) {
2144 fds[nfds].fd = libevdev_get_fd(d->evdev);
2145 fds[nfds].events = POLLIN;
2146 fds[nfds].revents = 0;
2147 assert(fds[nfds].fd != -1);
2148 nfds++;
2149 }
2150
2151 /* If we have more than one device, the time starts at recording
2152 * start time. Otherwise, the first event starts the recording time.
2153 */
2154 if (ctx->ndevices > 1) {
2155 clock_gettime(CLOCK_MONOTONIC, &ts);
2156 ctx->offset = s2us(ts.tv_sec) + ns2us(ts.tv_nsec);
2157 }
2158
2159 do {
2160 int rc;
2161 bool had_events = false; /* we delete files without events */
2162
2163 if (!open_output_file(ctx, autorestart)) {
2164 fprintf(stderr,
2165 "Failed to open '%s'\n",
2166 ctx->output_file);
2167 break;
2168 }
2169 fprintf(stderr, "Recording to '%s'.\n", ctx->output_file);
2170
2171 print_header(ctx);
2172 if (autorestart)
2173 iprintf(ctx,
2174 "# Autorestart timeout: %d\n",
2175 ctx->timeout);
2176
2177 iprintf(ctx, "devices:\n");
2178 indent_push(ctx);
2179
2180 /* we only print the first device's description, the
2181 * rest is assembled after CTRL+C */
2182 first_device = list_first_entry(&ctx->devices,
2183 first_device,
2184 link);
2185 print_device_description(ctx, first_device);
2186
2187 iprintf(ctx, "events:\n");
2188 indent_push(ctx);
2189
2190 if (ctx->libinput) {
2191 size_t count;
2192 libinput_dispatch(ctx->libinput);
2193 count = handle_libinput_events(ctx, first_device);
2194 print_cached_events(ctx, first_device, 0, count);
2195 }
2196
2197 while (true) {
2198 rc = poll(fds, nfds, ctx->timeout);
2199 if (rc == -1) { /* error */
2200 fprintf(stderr, "Error: %m\n");
2201 autorestart = false;
2202 break;
2203 } else if (rc == 0) {
2204 fprintf(stderr,
2205 " ... timeout%s\n",
2206 had_events ? "" : " (file is empty)");
2207 break;
2208 } else if (fds[0].revents != 0) { /* signal */
2209 autorestart = false;
2210 break;
2211 }
2212
2213 /* Pull off the evdev events first since they cause
2214 * libinput events.
2215 * handle_events de-queues libinput events so by the
2216 * time we finish that, we hopefully have all evdev
2217 * events and libinput events roughly in sync.
2218 */
2219 had_events = true;
2220 list_for_each(d, &ctx->devices, link)
2221 handle_events(ctx, d, d == first_device);
2222
2223 /* This shouldn't pull any events off unless caused
2224 * by libinput-internal timeouts (e.g. tapping) */
2225 if (ctx->libinput && fds[1].revents) {
2226 size_t count, offset;
2227
2228 libinput_dispatch(ctx->libinput);
2229 offset = first_device->nevents;
2230 count = handle_libinput_events(ctx,
2231 first_device);
2232 if (count) {
2233 print_cached_events(ctx,
2234 first_device,
2235 offset,
2236 count);
2237 }
2238 rc--;
2239 }
2240
2241 if (ctx->out_fd != STDOUT_FILENO)
2242 print_progress_bar();
2243
2244 }
2245 indent_pop(ctx); /* events: */
2246
2247 if (autorestart) {
2248 noiprintf(ctx,
2249 "# Closing after %ds inactivity",
2250 ctx->timeout/1000);
2251 }
2252
2253 /* First device is printed, now append all the data from the
2254 * other devices, if any */
2255 list_for_each(d, &ctx->devices, link) {
2256 if (d == list_first_entry(&ctx->devices, d, link))
2257 continue;
2258
2259 print_device_description(ctx, d);
2260 iprintf(ctx, "events:\n");
2261 indent_push(ctx);
2262 print_cached_events(ctx, d, 0, -1);
2263 indent_pop(ctx);
2264 }
2265
2266 indent_pop(ctx); /* devices: */
2267 assert(ctx->indent == 0);
2268
2269 fsync(ctx->out_fd);
2270
2271 /* If we didn't have events, delete the file. */
2272 if (!isatty(ctx->out_fd)) {
2273 if (!had_events && ctx->output_file) {
2274 fprintf(stderr, "No events recorded, deleting '%s'\n", ctx->output_file);
2275 unlink(ctx->output_file);
2276 }
2277
2278 close(ctx->out_fd);
2279 ctx->out_fd = -1;
2280 }
2281 free(ctx->output_file);
2282 ctx->output_file = NULL;
2283 } while (autorestart);
2284
2285 close(fds[0].fd);
2286
2287 sigprocmask(SIG_UNBLOCK, &mask, NULL);
2288
2289 return 0;
2290 }
2291
2292 static inline bool
init_device(struct record_context * ctx,char * path)2293 init_device(struct record_context *ctx, char *path)
2294 {
2295 struct record_device *d;
2296 int fd, rc;
2297
2298 d = zalloc(sizeof(*d));
2299 d->devnode = path;
2300 d->nevents = 0;
2301 d->events_sz = 5000;
2302 d->events = zalloc(d->events_sz * sizeof(*d->events));
2303
2304 fd = open(d->devnode, O_RDONLY|O_NONBLOCK);
2305 if (fd < 0) {
2306 fprintf(stderr,
2307 "Failed to open device %s (%m)\n",
2308 d->devnode);
2309 goto error;
2310 }
2311
2312 rc = libevdev_new_from_fd(fd, &d->evdev);
2313 if (rc == 0)
2314 rc = libevdev_new_from_fd(fd, &d->evdev_prev);
2315 if (rc != 0) {
2316 fprintf(stderr,
2317 "Failed to create context for %s (%s)\n",
2318 d->devnode,
2319 strerror(-rc));
2320 goto error;
2321 }
2322
2323 libevdev_set_clock_id(d->evdev, CLOCK_MONOTONIC);
2324
2325 if (libevdev_get_num_slots(d->evdev) > 0)
2326 d->touch.is_touch_device = true;
2327
2328 list_insert(&ctx->devices, &d->link);
2329 ctx->ndevices++;
2330
2331 return true;
2332 error:
2333 close(fd);
2334 free(d);
2335 return false;
2336
2337 }
2338 static int
open_restricted(const char * path,int flags,void * user_data)2339 open_restricted(const char *path, int flags, void *user_data)
2340 {
2341 int fd = open(path, flags);
2342 return fd == -1 ? -errno : fd;
2343 }
2344
close_restricted(int fd,void * user_data)2345 static void close_restricted(int fd, void *user_data)
2346 {
2347 close(fd);
2348 }
2349
2350 const struct libinput_interface interface = {
2351 .open_restricted = open_restricted,
2352 .close_restricted = close_restricted,
2353 };
2354
2355 static inline bool
init_libinput(struct record_context * ctx)2356 init_libinput(struct record_context *ctx)
2357 {
2358 struct record_device *dev;
2359 struct libinput *li;
2360
2361 li = libinput_path_create_context(&interface, NULL);
2362 if (li == NULL) {
2363 fprintf(stderr,
2364 "Failed to create libinput context\n");
2365 return false;
2366 }
2367
2368 ctx->libinput = li;
2369
2370 list_for_each(dev, &ctx->devices, link) {
2371 struct libinput_device *d;
2372
2373 d = libinput_path_add_device(li, dev->devnode);
2374 if (!d) {
2375 fprintf(stderr,
2376 "Failed to add device %s\n",
2377 dev->devnode);
2378 continue;
2379 }
2380 dev->device = libinput_device_ref(d);
2381 /* FIXME: this needs to be a commandline option */
2382 libinput_device_config_tap_set_enabled(d,
2383 LIBINPUT_CONFIG_TAP_ENABLED);
2384 }
2385
2386 return true;
2387 }
2388
2389 static inline void
usage(void)2390 usage(void)
2391 {
2392 printf("Usage: %s [--help] [--all] [--autorestart] [--output-file filename] [/dev/input/event0] [...]\n"
2393 "Common use-cases:\n"
2394 "\n"
2395 " sudo %s -o recording.yml\n"
2396 " Then select the device to record and it Ctrl+C to stop.\n"
2397 " The recorded data is in recording.yml and can be attached to a bug report.\n"
2398 "\n"
2399 " sudo %s -o recording.yml --autorestart 2\n"
2400 " As above, but restarts after 2s of inactivity on the device.\n"
2401 " Note, the output file is only the prefix.\n"
2402 "\n"
2403 " sudo %s -o recording.yml /dev/input/event3 /dev/input/event4\n"
2404 " Records the two devices into the same recordings file.\n"
2405 "\n"
2406 "For more information, see the %s(1) man page\n",
2407 program_invocation_short_name,
2408 program_invocation_short_name,
2409 program_invocation_short_name,
2410 program_invocation_short_name,
2411 program_invocation_short_name);
2412 }
2413
2414 enum ftype {
2415 F_FILE = 8,
2416 F_DEVICE,
2417 F_NOEXIST,
2418 };
2419
is_char_dev(const char * path)2420 static inline enum ftype is_char_dev(const char *path)
2421 {
2422 struct stat st;
2423
2424 if (strneq(path, "/dev", 4))
2425 return F_DEVICE;
2426
2427 if (stat(path, &st) != 0) {
2428 if (errno == ENOENT)
2429 return F_NOEXIST;
2430 return F_FILE;
2431 }
2432
2433 return S_ISCHR(st.st_mode) ? F_DEVICE : F_FILE;
2434 }
2435
2436 enum options {
2437 OPT_AUTORESTART,
2438 OPT_HELP,
2439 OPT_OUTFILE,
2440 OPT_KEYCODES,
2441 OPT_MULTIPLE,
2442 OPT_ALL,
2443 OPT_LIBINPUT,
2444 };
2445
2446 int
main(int argc,char ** argv)2447 main(int argc, char **argv)
2448 {
2449 struct record_context ctx = {
2450 .timeout = -1,
2451 .show_keycodes = false,
2452 };
2453 struct option opts[] = {
2454 { "autorestart", required_argument, 0, OPT_AUTORESTART },
2455 { "output-file", required_argument, 0, OPT_OUTFILE },
2456 { "show-keycodes", no_argument, 0, OPT_KEYCODES },
2457 { "multiple", no_argument, 0, OPT_MULTIPLE },
2458 { "all", no_argument, 0, OPT_ALL },
2459 { "help", no_argument, 0, OPT_HELP },
2460 { "with-libinput", no_argument, 0, OPT_LIBINPUT },
2461 { 0, 0, 0, 0 },
2462 };
2463 struct record_device *d, *tmp;
2464 const char *output_arg = NULL;
2465 bool all = false, with_libinput = false;
2466 int ndevices;
2467 int rc = EXIT_FAILURE;
2468
2469 list_init(&ctx.devices);
2470
2471 while (1) {
2472 int c;
2473 int option_index = 0;
2474
2475 c = getopt_long(argc, argv, "ho:", opts, &option_index);
2476 if (c == -1)
2477 break;
2478
2479 switch (c) {
2480 case 'h':
2481 case OPT_HELP:
2482 usage();
2483 rc = EXIT_SUCCESS;
2484 goto out;
2485 case OPT_AUTORESTART:
2486 if (!safe_atoi(optarg, &ctx.timeout) ||
2487 ctx.timeout <= 0) {
2488 usage();
2489 rc = EXIT_INVALID_USAGE;
2490 goto out;
2491 }
2492 ctx.timeout = ctx.timeout * 1000;
2493 break;
2494 case 'o':
2495 case OPT_OUTFILE:
2496 output_arg = optarg;
2497 break;
2498 case OPT_KEYCODES:
2499 ctx.show_keycodes = true;
2500 break;
2501 case OPT_MULTIPLE: /* deprecated */
2502 break;
2503 case OPT_ALL:
2504 all = true;
2505 break;
2506 case OPT_LIBINPUT:
2507 with_libinput = true;
2508 break;
2509 default:
2510 usage();
2511 rc = EXIT_INVALID_USAGE;
2512 goto out;
2513 }
2514 }
2515
2516 ndevices = argc - optind;
2517
2518 /* We allow for multiple arguments after the options, *one* of which
2519 * may be the output file. That one must be the first or the last to
2520 * prevent users from running
2521 * libinput record /dev/input/event0 output.yml /dev/input/event1
2522 * because this will only backfire anyway.
2523 */
2524 if (ndevices >= 1 && output_arg == NULL) {
2525 char *first, *last;
2526 enum ftype ftype_first;
2527
2528 first = argv[optind];
2529 last = argv[argc - 1];
2530
2531 ftype_first = is_char_dev(first);
2532 if (ndevices == 1) {
2533 /* arg is *not* a char device, so let's assume it's
2534 * the output file */
2535 if (ftype_first != F_DEVICE) {
2536 output_arg = first;
2537 optind++;
2538 ndevices--;
2539 }
2540 /* multiple arguments, yay */
2541 } else {
2542 enum ftype ftype_last = is_char_dev(last);
2543 /*
2544 first is device, last is file -> last
2545 first is device, last is device -> noop
2546 first is device, last !exist -> last
2547 first is file, last is device -> first
2548 first is file, last is file -> error
2549 first is file, last !exist -> error
2550 first !exist, last is device -> first
2551 first !exist, last is file -> error
2552 first !exit, last !exist -> error
2553 */
2554 #define _m(f, l) (((f) << 8) | (l))
2555 switch (_m(ftype_first, ftype_last)) {
2556 case _m(F_FILE, F_DEVICE):
2557 case _m(F_FILE, F_NOEXIST):
2558 case _m(F_NOEXIST, F_DEVICE):
2559 output_arg = first;
2560 optind++;
2561 ndevices--;
2562 break;
2563 case _m(F_DEVICE, F_FILE):
2564 case _m(F_DEVICE, F_NOEXIST):
2565 output_arg = last;
2566 ndevices--;
2567 break;
2568 case _m(F_DEVICE, F_DEVICE):
2569 break;
2570 case _m(F_FILE, F_FILE):
2571 case _m(F_NOEXIST, F_FILE):
2572 case _m(F_NOEXIST, F_NOEXIST):
2573 fprintf(stderr, "Ambiguous device vs output file list. Please use --output-file.\n");
2574 rc = EXIT_INVALID_USAGE;
2575 goto out;
2576 }
2577 #undef _m
2578 }
2579 }
2580
2581
2582 if (ctx.timeout > 0 && output_arg == NULL) {
2583 fprintf(stderr,
2584 "Option --autorestart requires --output-file\n");
2585 rc = EXIT_INVALID_USAGE;
2586 goto out;
2587 }
2588
2589 ctx.outfile = safe_strdup(output_arg);
2590
2591 if (all) {
2592 char **devices; /* NULL-terminated */
2593 char **d;
2594
2595 if (output_arg == NULL) {
2596 fprintf(stderr,
2597 "Option --all requires an output file\n");
2598 rc = EXIT_INVALID_USAGE;
2599 goto out;
2600 }
2601
2602 devices = all_devices();
2603 d = devices;
2604
2605 while (*d) {
2606 if (!init_device(&ctx, safe_strdup(*d))) {
2607 strv_free(devices);
2608 goto out;
2609 }
2610 d++;
2611 }
2612
2613 strv_free(devices);
2614 } else if (ndevices > 1) {
2615 if (ndevices > 1 && output_arg == NULL) {
2616 fprintf(stderr,
2617 "Recording multiple devices requires an output file\n");
2618 rc = EXIT_INVALID_USAGE;
2619 goto out;
2620 }
2621
2622 for (int i = ndevices; i > 0; i -= 1) {
2623 char *devnode = safe_strdup(argv[optind + i - 1]);
2624
2625 if (!init_device(&ctx, devnode))
2626 goto out;
2627 }
2628 } else {
2629 char *path;
2630
2631 path = ndevices <= 0 ? select_device() : safe_strdup(argv[optind++]);
2632 if (path == NULL) {
2633 goto out;
2634 }
2635
2636 if (!init_device(&ctx, path))
2637 goto out;
2638 }
2639
2640 if (with_libinput && !init_libinput(&ctx))
2641 goto out;
2642
2643 rc = mainloop(&ctx);
2644 out:
2645 list_for_each_safe(d, tmp, &ctx.devices, link) {
2646 if (d->device)
2647 libinput_device_unref(d->device);
2648 free(d->events);
2649 free(d->devnode);
2650 libevdev_free(d->evdev);
2651 }
2652
2653 libinput_unref(ctx.libinput);
2654
2655 return rc;
2656 }
2657