shared.c 17.9 KB
Newer Older
1
2
3
/*
 * Copyright © 2014 Red Hat, Inc.
 *
4
5
6
7
8
9
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
10
 *
11
12
13
14
15
16
17
18
19
20
21
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
22
23
24
25
26
 */

#include <config.h>

#include <errno.h>
27
#include <fcntl.h>
28
#include <fnmatch.h>
29
30
31
32
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
33
#include <sys/stat.h>
34
#include <libudev.h>
35

36
#include <libevdev/libevdev.h>
37
#include <libinput-util.h>
38

39
40
#include "shared.h"

41
LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
42
43
44
45
46
47
static void
log_handler(struct libinput *li,
	    enum libinput_log_priority priority,
	    const char *format,
	    va_list args)
{
48
49
50
51
52
53
54
55
56
57
58
59
	static int is_tty = -1;

	if (is_tty == -1)
		is_tty = isatty(STDOUT_FILENO);

	if (is_tty) {
		if (priority >= LIBINPUT_LOG_PRIORITY_ERROR)
			printf(ANSI_RED);
		else if (priority >= LIBINPUT_LOG_PRIORITY_INFO)
			printf(ANSI_HIGHLIGHT);
	}

60
	vprintf(format, args);
61
62
63

	if (is_tty && priority >= LIBINPUT_LOG_PRIORITY_INFO)
		printf(ANSI_NORMAL);
64
65
}

66
void
67
tools_init_options(struct tools_options *options)
68
69
70
{
	memset(options, 0, sizeof(*options));
	options->tapping = -1;
71
	options->tap_map = -1;
72
	options->drag = -1;
73
	options->drag_lock = -1;
74
	options->natural_scroll = -1;
75
	options->left_handed = -1;
76
	options->middlebutton = -1;
77
	options->dwt = -1;
78
	options->click_method = -1;
79
	options->scroll_method = -1;
80
	options->scroll_button = -1;
81
	options->speed = 0.0;
82
	options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
83
84
85
}

int
86
87
88
tools_parse_option(int option,
		   const char *optarg,
		   struct tools_options *options)
89
{
90
	switch(option) {
91
92
93
94
95
96
97
98
99
	case OPT_TAP_ENABLE:
		options->tapping = 1;
		break;
	case OPT_TAP_DISABLE:
		options->tapping = 0;
		break;
	case OPT_TAP_MAP:
		if (!optarg)
			return 1;
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
		if (streq(optarg, "lrm")) {
			options->tap_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
		} else if (streq(optarg, "lmr")) {
			options->tap_map = LIBINPUT_CONFIG_TAP_MAP_LMR;
		} else {
			return 1;
		}
		break;
	case OPT_DRAG_ENABLE:
		options->drag = 1;
		break;
	case OPT_DRAG_DISABLE:
		options->drag = 0;
		break;
	case OPT_DRAG_LOCK_ENABLE:
		options->drag_lock = 1;
		break;
	case OPT_DRAG_LOCK_DISABLE:
		options->drag_lock = 0;
		break;
	case OPT_NATURAL_SCROLL_ENABLE:
		options->natural_scroll = 1;
		break;
	case OPT_NATURAL_SCROLL_DISABLE:
		options->natural_scroll = 0;
		break;
	case OPT_LEFT_HANDED_ENABLE:
		options->left_handed = 1;
		break;
	case OPT_LEFT_HANDED_DISABLE:
		options->left_handed = 0;
		break;
	case OPT_MIDDLEBUTTON_ENABLE:
		options->middlebutton = 1;
		break;
	case OPT_MIDDLEBUTTON_DISABLE:
		options->middlebutton = 0;
		break;
	case OPT_DWT_ENABLE:
		options->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
		break;
	case OPT_DWT_DISABLE:
		options->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
		break;
	case OPT_CLICK_METHOD:
		if (!optarg)
			return 1;
148

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
		if (streq(optarg, "none")) {
			options->click_method =
			LIBINPUT_CONFIG_CLICK_METHOD_NONE;
		} else if (streq(optarg, "clickfinger")) {
			options->click_method =
			LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
		} else if (streq(optarg, "buttonareas")) {
			options->click_method =
			LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
		} else {
			return 1;
		}
		break;
	case OPT_SCROLL_METHOD:
		if (!optarg)
			return 1;
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
		if (streq(optarg, "none")) {
			options->scroll_method =
			LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
		} else if (streq(optarg, "twofinger")) {
			options->scroll_method =
			LIBINPUT_CONFIG_SCROLL_2FG;
		} else if (streq(optarg, "edge")) {
			options->scroll_method =
			LIBINPUT_CONFIG_SCROLL_EDGE;
		} else if (streq(optarg, "button")) {
			options->scroll_method =
			LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
		} else {
			return 1;
		}
		break;
	case OPT_SCROLL_BUTTON:
		if (!optarg) {
			return 1;
		}
		options->scroll_button =
		libevdev_event_code_from_name(EV_KEY,
					      optarg);
		if (options->scroll_button == -1) {
			fprintf(stderr,
				"Invalid button %s\n",
				optarg);
			return 1;
		}
		break;
	case OPT_SPEED:
		if (!optarg)
			return 1;
		options->speed = atof(optarg);
		break;
	case OPT_PROFILE:
		if (!optarg)
			return 1;
204

205
206
207
208
209
210
211
212
213
214
		if (streq(optarg, "adaptive"))
			options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
		else if (streq(optarg, "flat"))
		      options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
		else
		      return 1;
		break;
	case OPT_DISABLE_SENDEVENTS:
		if (!optarg)
			return 1;
215

216
217
218
219
220
		snprintf(options->disable_pattern,
			 sizeof(options->disable_pattern),
			 "%s",
			 optarg);
		break;
221
222
223
224
	}

	return 0;
}
225

226
227
228
229
230
231
232
233
234
static int
open_restricted(const char *path, int flags, void *user_data)
{
	bool *grab = user_data;
	int fd = open(path, flags);

	if (fd < 0)
		fprintf(stderr, "Failed to open %s (%s)\n",
			path, strerror(errno));
235
	else if (grab && *grab && ioctl(fd, EVIOCGRAB, (void*)1) == -1)
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
		fprintf(stderr, "Grab requested, but failed for %s (%s)\n",
			path, strerror(errno));

	return fd < 0 ? -errno : fd;
}

static void
close_restricted(int fd, void *user_data)
{
	close(fd);
}

static const struct libinput_interface interface = {
	.open_restricted = open_restricted,
	.close_restricted = close_restricted,
};

253
static struct libinput *
254
tools_open_udev(const char *seat, bool verbose, bool *grab)
255
256
257
258
259
260
261
262
263
{
	struct libinput *li;
	struct udev *udev = udev_new();

	if (!udev) {
		fprintf(stderr, "Failed to initialize udev\n");
		return NULL;
	}

264
	li = libinput_udev_create_context(&interface, grab, udev);
265
266
267
268
269
	if (!li) {
		fprintf(stderr, "Failed to initialize context from udev\n");
		goto out;
	}

270
271
	libinput_log_set_handler(li, log_handler);
	if (verbose)
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);

	if (libinput_udev_assign_seat(li, seat)) {
		fprintf(stderr, "Failed to set seat\n");
		libinput_unref(li);
		li = NULL;
		goto out;
	}

out:
	udev_unref(udev);
	return li;
}

static struct libinput *
287
tools_open_device(const char *path, bool verbose, bool *grab)
288
289
290
291
{
	struct libinput_device *device;
	struct libinput *li;

292
	li = libinput_path_create_context(&interface, grab);
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
	if (!li) {
		fprintf(stderr, "Failed to initialize context from %s\n", path);
		return NULL;
	}

	if (verbose) {
		libinput_log_set_handler(li, log_handler);
		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
	}

	device = libinput_path_add_device(li, path);
	if (!device) {
		fprintf(stderr, "Failed to initialized device %s\n", path);
		libinput_unref(li);
		li = NULL;
	}

	return li;
}

313
314
315
316
317
318
319
static void
tools_setenv_quirks_dir(void)
{
	if (tools_execdir_is_builddir(NULL, 0))
		setenv("LIBINPUT_QUIRKS_DIR", LIBINPUT_QUIRKS_SRCDIR, 0);
}

320
struct libinput *
321
322
323
tools_open_backend(enum tools_backend which,
		   const char *seat_or_device,
		   bool verbose,
324
		   bool *grab)
325
{
326
327
	struct libinput *li;

328
329
	tools_setenv_quirks_dir();

330
331
332
333
334
335
336
337
	switch (which) {
	case BACKEND_UDEV:
		li = tools_open_udev(seat_or_device, verbose, grab);
		break;
	case BACKEND_DEVICE:
		li = tools_open_device(seat_or_device, verbose, grab);
		break;
	default:
338
		abort();
339
	}
340
341
342

	return li;
}
343
344
345
346
347
348
349

void
tools_device_apply_config(struct libinput_device *device,
			  struct tools_options *options)
{
	if (options->tapping != -1)
		libinput_device_config_tap_set_enabled(device, options->tapping);
350
	if (options->tap_map != (enum libinput_config_tap_button_map)-1)
351
352
		libinput_device_config_tap_set_button_map(device,
							  options->tap_map);
353
354
355
	if (options->drag != -1)
		libinput_device_config_tap_set_drag_enabled(device,
							    options->drag);
356
357
358
	if (options->drag_lock != -1)
		libinput_device_config_tap_set_drag_lock_enabled(device,
								 options->drag_lock);
359
360
361
	if (options->natural_scroll != -1)
		libinput_device_config_scroll_set_natural_scroll_enabled(device,
									 options->natural_scroll);
362
	if (options->left_handed != -1)
363
		libinput_device_config_left_handed_set(device, options->left_handed);
364
365
366
	if (options->middlebutton != -1)
		libinput_device_config_middle_emulation_set_enabled(device,
								    options->middlebutton);
367

368
369
370
	if (options->dwt != -1)
		libinput_device_config_dwt_set_enabled(device, options->dwt);

371
	if (options->click_method != (enum libinput_config_click_method)-1)
372
		libinput_device_config_click_set_method(device, options->click_method);
373

374
	if (options->scroll_method != (enum libinput_config_scroll_method)-1)
375
376
		libinput_device_config_scroll_set_method(device,
							 options->scroll_method);
377
378
379
	if (options->scroll_button != -1)
		libinput_device_config_scroll_set_button(device,
							 options->scroll_button);
380

381
	if (libinput_device_config_accel_is_available(device)) {
382
383
		libinput_device_config_accel_set_speed(device,
						       options->speed);
384
385
386
387
		if (options->profile != LIBINPUT_CONFIG_ACCEL_PROFILE_NONE)
			libinput_device_config_accel_set_profile(device,
								 options->profile);
	}
388
389
390
391
392
393
394
395
396

	if (libinput_device_config_send_events_get_modes(device) &
	      LIBINPUT_CONFIG_SEND_EVENTS_DISABLED &&
	    fnmatch(options->disable_pattern,
		    libinput_device_get_name(device),
		   0) !=  FNM_NOMATCH) {
		libinput_device_config_send_events_set_mode(device,
					    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
	}
397
}
398
399
400
401
402
403

static char*
find_device(const char *udev_tag)
{
	struct udev *udev;
	struct udev_enumerate *e;
404
	struct udev_list_entry *entry = NULL;
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
	struct udev_device *device;
	const char *path, *sysname;
	char *device_node = NULL;

	udev = udev_new();
	e = udev_enumerate_new(udev);
	udev_enumerate_add_match_subsystem(e, "input");
	udev_enumerate_scan_devices(e);

	udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
		path = udev_list_entry_get_name(entry);
		device = udev_device_new_from_syspath(udev, path);
		if (!device)
			continue;

		sysname = udev_device_get_sysname(device);
		if (strncmp("event", sysname, 5) != 0) {
			udev_device_unref(device);
			continue;
		}

		if (udev_device_get_property_value(device, udev_tag))
Peter Hutterer's avatar
Peter Hutterer committed
427
			device_node = safe_strdup(udev_device_get_devnode(device));
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477

		udev_device_unref(device);

		if (device_node)
			break;
	}
	udev_enumerate_unref(e);
	udev_unref(udev);

	return device_node;
}

bool
find_touchpad_device(char *path, size_t path_len)
{
	char *devnode = find_device("ID_INPUT_TOUCHPAD");

	if (devnode) {
		snprintf(path, path_len, "%s", devnode);
		free(devnode);
	}

	return devnode != NULL;
}

bool
is_touchpad_device(const char *devnode)
{
	struct udev *udev;
	struct udev_device *dev = NULL;
	struct stat st;
	bool is_touchpad = false;

	if (stat(devnode, &st) < 0)
		return false;

	udev = udev_new();
	dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
	if (!dev)
		goto out;

	is_touchpad = udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD");
out:
	if (dev)
		udev_device_unref(dev);
	udev_unref(udev);

	return is_touchpad;
}

478
479
480
481
482
483
484
485
486
487
/**
 * Try to read the directory we're executing from and if it matches the
 * builddir, return it.
 *
 * @param execdir_out If not NULL, set to the exec directory
 * @param sz Size of execdir_out
 *
 * @return true if the execdir is the builddir, false otherwise.
 *
 * If execdir_out is NULL and szt is 0, it merely returns true/false.
488
 */
489
490
bool
tools_execdir_is_builddir(char *execdir_out, size_t sz)
491
492
493
{
	char execdir[PATH_MAX] = {0};
	char *pathsep;
494
	ssize_t nread;
495

496
497
498
	/* In the case of release builds, the builddir is
	   the empty string */
	if (streq(MESON_BUILD_ROOT, ""))
499
		return false;
500

501
502
503
	nread = readlink("/proc/self/exe", execdir, sizeof(execdir) - 1);
	if (nread <= 0 || nread == sizeof(execdir) - 1)
		return false;
504
505
506

	/* readlink doesn't terminate the string and readlink says
	   anything past sz is undefined */
507
	execdir[++nread] = '\0';
508
509
510

	pathsep = strrchr(execdir, '/');
	if (!pathsep)
511
		return false;
512
513
514

	*pathsep = '\0';
	if (!streq(execdir, MESON_BUILD_ROOT))
515
		return false;
516

517
518
519
520
521
522
	if (sz > 0) {
		assert(execdir_out != NULL);
		assert(sz >= (size_t)nread);
		snprintf(execdir_out, nread, "%s", execdir);
	}
	return true;
523
524
}

525
526
527
528
529
static inline void
setup_path(void)
{
	const char *path = getenv("PATH");
	char new_path[PATH_MAX];
530
531
	char builddir[PATH_MAX];
	const char *extra_path = LIBINPUT_TOOL_PATH;
532

533
534
	if (tools_execdir_is_builddir(builddir, sizeof(builddir)))
		extra_path = builddir;
535
536
537
538

	snprintf(new_path,
		 sizeof(new_path),
		 "%s:%s",
539
		 extra_path,
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
		 path ? path : "");
	setenv("PATH", new_path, 1);
}

int
tools_exec_command(const char *prefix, int real_argc, char **real_argv)
{
	char *argv[64] = {NULL};
	char executable[128];
	const char *command;
	int rc;

	assert((size_t)real_argc < ARRAY_LENGTH(argv));

	command = real_argv[0];

	rc = snprintf(executable,
		      sizeof(executable),
		      "%s-%s",
		      prefix,
		      command);
	if (rc >= (int)sizeof(executable)) {
		fprintf(stderr, "Failed to assemble command.\n");
		return EXIT_FAILURE;
	}

	argv[0] = executable;
	for (int i = 1; i < real_argc; i++)
		argv[i] = real_argv[i];

	setup_path();

	rc = execvp(executable, argv);
573
574
575
576
577
578
579
580
581
582
583
584
585
586
	if (rc) {
		if (errno == ENOENT) {
			fprintf(stderr,
				"libinput: %s is not a libinput command or not installed. "
				"See 'libinput --help'\n",
				command);

		} else {
			fprintf(stderr,
				"Failed to execute '%s' (%s)\n",
				command,
				strerror(errno));
		}
	}
587
588
589

	return EXIT_FAILURE;
}
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639

void
tools_list_device_quirks(struct quirks_context *ctx,
			 struct udev_device *device,
			 void (*callback)(void *data, const char *str),
			 void *userdata)
{
	char buf[256];

	struct quirks *quirks;
	enum quirk qlist[] = {
		QUIRK_MODEL_ALPS_TOUCHPAD,
		QUIRK_MODEL_APPLE_TOUCHPAD,
		QUIRK_MODEL_APPLE_MAGICMOUSE,
		QUIRK_MODEL_TABLET_NO_TILT,
		QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON,
		QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER,
		QUIRK_MODEL_CYBORG_RAT,
		QUIRK_MODEL_CHROMEBOOK,
		QUIRK_MODEL_HP6910_TOUCHPAD,
		QUIRK_MODEL_HP8510_TOUCHPAD,
		QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD,
		QUIRK_MODEL_HP_STREAM11_TOUCHPAD,
		QUIRK_MODEL_HP_ZBOOK_STUDIO_G3,
		QUIRK_MODEL_TABLET_NO_PROXIMITY_OUT,
		QUIRK_MODEL_LENOVO_SCROLLPOINT,
		QUIRK_MODEL_LENOVO_X230,
		QUIRK_MODEL_LENOVO_T450_TOUCHPAD,
		QUIRK_MODEL_TABLET_MODE_NO_SUSPEND,
		QUIRK_MODEL_LENOVO_CARBON_X1_6TH,
		QUIRK_MODEL_TRACKBALL,
		QUIRK_MODEL_LOGITECH_MARBLE_MOUSE,
		QUIRK_MODEL_BOUNCING_KEYS,
		QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD,
		QUIRK_MODEL_SYSTEM76_BONOBO,
		QUIRK_MODEL_CLEVO_W740SU,
		QUIRK_MODEL_SYSTEM76_GALAGO,
		QUIRK_MODEL_SYSTEM76_KUDU,
		QUIRK_MODEL_WACOM_TOUCHPAD,


		QUIRK_ATTR_SIZE_HINT,
		QUIRK_ATTR_TOUCH_SIZE_RANGE,
		QUIRK_ATTR_PALM_SIZE_THRESHOLD,
		QUIRK_ATTR_LID_SWITCH_RELIABILITY,
		QUIRK_ATTR_KEYBOARD_INTEGRATION,
		QUIRK_ATTR_TPKBCOMBO_LAYOUT,
		QUIRK_ATTR_PRESSURE_RANGE,
		QUIRK_ATTR_PALM_PRESSURE_THRESHOLD,
		QUIRK_ATTR_RESOLUTION_HINT,
640
		QUIRK_ATTR_TRACKPOINT_MULTIPLIER,
641
		QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
642
		QUIRK_ATTR_USE_VELOCITY_AVERAGING,
643
		QUIRK_ATTR_THUMB_SIZE_THRESHOLD,
644
		QUIRK_ATTR_MSC_TIMESTAMP,
645
646
647
648
649
650
651
652
653
654
655
656
657
	};
	enum quirk *q;

	quirks = quirks_fetch_for_device(ctx, device);
	if (!quirks)
		return;

	ARRAY_FOR_EACH(qlist, q) {
		const char *name;
		struct quirk_dimensions dim;
		struct quirk_range r;
		uint32_t v;
		char *s;
658
		double d;
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711

		if (!quirks_has_quirk(quirks, *q))
			continue;

		name = quirk_get_name(*q);

		switch (*q) {
		case QUIRK_MODEL_ALPS_TOUCHPAD:
		case QUIRK_MODEL_APPLE_TOUCHPAD:
		case QUIRK_MODEL_APPLE_MAGICMOUSE:
		case QUIRK_MODEL_TABLET_NO_TILT:
		case QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON:
		case QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER:
		case QUIRK_MODEL_CYBORG_RAT:
		case QUIRK_MODEL_CHROMEBOOK:
		case QUIRK_MODEL_HP6910_TOUCHPAD:
		case QUIRK_MODEL_HP8510_TOUCHPAD:
		case QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD:
		case QUIRK_MODEL_HP_STREAM11_TOUCHPAD:
		case QUIRK_MODEL_HP_ZBOOK_STUDIO_G3:
		case QUIRK_MODEL_TABLET_NO_PROXIMITY_OUT:
		case QUIRK_MODEL_LENOVO_SCROLLPOINT:
		case QUIRK_MODEL_LENOVO_X230:
		case QUIRK_MODEL_LENOVO_T450_TOUCHPAD:
		case QUIRK_MODEL_TABLET_MODE_NO_SUSPEND:
		case QUIRK_MODEL_LENOVO_CARBON_X1_6TH:
		case QUIRK_MODEL_TRACKBALL:
		case QUIRK_MODEL_LOGITECH_MARBLE_MOUSE:
		case QUIRK_MODEL_BOUNCING_KEYS:
		case QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD:
		case QUIRK_MODEL_SYSTEM76_BONOBO:
		case QUIRK_MODEL_CLEVO_W740SU:
		case QUIRK_MODEL_SYSTEM76_GALAGO:
		case QUIRK_MODEL_SYSTEM76_KUDU:
		case QUIRK_MODEL_WACOM_TOUCHPAD:
			snprintf(buf, sizeof(buf), "%s=1", name);
			callback(userdata, buf);
			break;
		case QUIRK_ATTR_SIZE_HINT:
		case QUIRK_ATTR_RESOLUTION_HINT:
			quirks_get_dimensions(quirks, *q, &dim);
			snprintf(buf, sizeof(buf), "%s=%ldx%ld", name, dim.x, dim.y);
			callback(userdata, buf);
			break;
		case QUIRK_ATTR_TOUCH_SIZE_RANGE:
		case QUIRK_ATTR_PRESSURE_RANGE:
			quirks_get_range(quirks, *q, &r);
			snprintf(buf, sizeof(buf), "%s=%d:%d", name, r.upper, r.lower);
			callback(userdata, buf);
			break;
		case QUIRK_ATTR_PALM_SIZE_THRESHOLD:
		case QUIRK_ATTR_PALM_PRESSURE_THRESHOLD:
		case QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD:
712
		case QUIRK_ATTR_THUMB_SIZE_THRESHOLD:
713
714
715
716
717
718
719
			quirks_get_uint32(quirks, *q, &v);
			snprintf(buf, sizeof(buf), "%s=%u", name, v);
			callback(userdata, buf);
			break;
		case QUIRK_ATTR_LID_SWITCH_RELIABILITY:
		case QUIRK_ATTR_KEYBOARD_INTEGRATION:
		case QUIRK_ATTR_TPKBCOMBO_LAYOUT:
720
		case QUIRK_ATTR_MSC_TIMESTAMP:
721
722
723
724
			quirks_get_string(quirks, *q, &s);
			snprintf(buf, sizeof(buf), "%s=%s", name, s);
			callback(userdata, buf);
			break;
725
726
727
728
729
		case QUIRK_ATTR_TRACKPOINT_MULTIPLIER:
			quirks_get_double(quirks, *q, &d);
			snprintf(buf, sizeof(buf), "%s=%0.2f\n", name, d);
			callback(userdata, buf);
			break;
730
731
732
733
		case QUIRK_ATTR_USE_VELOCITY_AVERAGING:
			snprintf(buf, sizeof(buf), "%s=1", name);
			callback(userdata, buf);
			break;
734
735
736
737
738
		}
	}

	quirks_unref(quirks);
}