• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = &current->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