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