• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2014 Red Hat, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <config.h>
25 
26 #include <assert.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <fnmatch.h>
30 #include <getopt.h>
31 #include <limits.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <libudev.h>
37 #include <unistd.h>
38 
39 #include <libevdev/libevdev.h>
40 
41 #include "builddir.h"
42 #include "shared.h"
43 #include "util-macros.h"
44 #include "util-strings.h"
45 
46 LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
47 static void
log_handler(struct libinput * li,enum libinput_log_priority priority,const char * format,va_list args)48 log_handler(struct libinput *li,
49 	    enum libinput_log_priority priority,
50 	    const char *format,
51 	    va_list args)
52 {
53 	static int is_tty = -1;
54 
55 	if (is_tty == -1)
56 		is_tty = isatty(STDOUT_FILENO);
57 
58 	if (is_tty) {
59 		if (priority >= LIBINPUT_LOG_PRIORITY_ERROR)
60 			printf(ANSI_RED);
61 		else if (priority >= LIBINPUT_LOG_PRIORITY_INFO)
62 			printf(ANSI_HIGHLIGHT);
63 	}
64 
65 	vprintf(format, args);
66 
67 	if (is_tty && priority >= LIBINPUT_LOG_PRIORITY_INFO)
68 		printf(ANSI_NORMAL);
69 }
70 
71 void
tools_init_options(struct tools_options * options)72 tools_init_options(struct tools_options *options)
73 {
74 	memset(options, 0, sizeof(*options));
75 	options->tapping = -1;
76 	options->tap_map = -1;
77 	options->drag = -1;
78 	options->drag_lock = -1;
79 	options->natural_scroll = -1;
80 	options->left_handed = -1;
81 	options->middlebutton = -1;
82 	options->dwt = -1;
83 	options->click_method = -1;
84 	options->scroll_method = -1;
85 	options->scroll_button = -1;
86 	options->scroll_button_lock = -1;
87 	options->speed = 0.0;
88 	options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
89 }
90 
91 int
tools_parse_option(int option,const char * optarg,struct tools_options * options)92 tools_parse_option(int option,
93 		   const char *optarg,
94 		   struct tools_options *options)
95 {
96 	switch(option) {
97 	case OPT_TAP_ENABLE:
98 		options->tapping = 1;
99 		break;
100 	case OPT_TAP_DISABLE:
101 		options->tapping = 0;
102 		break;
103 	case OPT_TAP_MAP:
104 		if (!optarg)
105 			return 1;
106 
107 		if (streq(optarg, "lrm")) {
108 			options->tap_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
109 		} else if (streq(optarg, "lmr")) {
110 			options->tap_map = LIBINPUT_CONFIG_TAP_MAP_LMR;
111 		} else {
112 			return 1;
113 		}
114 		break;
115 	case OPT_DRAG_ENABLE:
116 		options->drag = 1;
117 		break;
118 	case OPT_DRAG_DISABLE:
119 		options->drag = 0;
120 		break;
121 	case OPT_DRAG_LOCK_ENABLE:
122 		options->drag_lock = 1;
123 		break;
124 	case OPT_DRAG_LOCK_DISABLE:
125 		options->drag_lock = 0;
126 		break;
127 	case OPT_NATURAL_SCROLL_ENABLE:
128 		options->natural_scroll = 1;
129 		break;
130 	case OPT_NATURAL_SCROLL_DISABLE:
131 		options->natural_scroll = 0;
132 		break;
133 	case OPT_LEFT_HANDED_ENABLE:
134 		options->left_handed = 1;
135 		break;
136 	case OPT_LEFT_HANDED_DISABLE:
137 		options->left_handed = 0;
138 		break;
139 	case OPT_MIDDLEBUTTON_ENABLE:
140 		options->middlebutton = 1;
141 		break;
142 	case OPT_MIDDLEBUTTON_DISABLE:
143 		options->middlebutton = 0;
144 		break;
145 	case OPT_DWT_ENABLE:
146 		options->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
147 		break;
148 	case OPT_DWT_DISABLE:
149 		options->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
150 		break;
151 	case OPT_CLICK_METHOD:
152 		if (!optarg)
153 			return 1;
154 
155 		if (streq(optarg, "none")) {
156 			options->click_method =
157 			LIBINPUT_CONFIG_CLICK_METHOD_NONE;
158 		} else if (streq(optarg, "clickfinger")) {
159 			options->click_method =
160 			LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
161 		} else if (streq(optarg, "buttonareas")) {
162 			options->click_method =
163 			LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
164 		} else {
165 			return 1;
166 		}
167 		break;
168 	case OPT_SCROLL_METHOD:
169 		if (!optarg)
170 			return 1;
171 
172 		if (streq(optarg, "none")) {
173 			options->scroll_method =
174 			LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
175 		} else if (streq(optarg, "twofinger")) {
176 			options->scroll_method =
177 			LIBINPUT_CONFIG_SCROLL_2FG;
178 		} else if (streq(optarg, "edge")) {
179 			options->scroll_method =
180 			LIBINPUT_CONFIG_SCROLL_EDGE;
181 		} else if (streq(optarg, "button")) {
182 			options->scroll_method =
183 			LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
184 		} else {
185 			return 1;
186 		}
187 		break;
188 	case OPT_SCROLL_BUTTON:
189 		if (!optarg) {
190 			return 1;
191 		}
192 		options->scroll_button =
193 		libevdev_event_code_from_name(EV_KEY,
194 					      optarg);
195 		if (options->scroll_button == -1) {
196 			fprintf(stderr,
197 				"Invalid button %s\n",
198 				optarg);
199 			return 1;
200 		}
201 		break;
202 	case OPT_SCROLL_BUTTON_LOCK_ENABLE:
203 		options->scroll_button_lock = true;
204 		break;
205 	case OPT_SCROLL_BUTTON_LOCK_DISABLE:
206 		options->scroll_button_lock = false;
207 		break;
208 	case OPT_SPEED:
209 		if (!optarg)
210 			return 1;
211 		options->speed = atof(optarg);
212 		break;
213 	case OPT_PROFILE:
214 		if (!optarg)
215 			return 1;
216 
217 		if (streq(optarg, "adaptive"))
218 			options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
219 		else if (streq(optarg, "flat"))
220 		      options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
221 		else
222 		      return 1;
223 		break;
224 	case OPT_DISABLE_SENDEVENTS:
225 		if (!optarg)
226 			return 1;
227 
228 		snprintf(options->disable_pattern,
229 			 sizeof(options->disable_pattern),
230 			 "%s",
231 			 optarg);
232 		break;
233 	case OPT_APPLY_TO:
234 		if (!optarg)
235 			return 1;
236 
237 		snprintf(options->match,
238 			 sizeof(options->match),
239 			 "%s",
240 			 optarg);
241 		break;
242 	}
243 
244 	return 0;
245 }
246 
247 static int
open_restricted(const char * path,int flags,void * user_data)248 open_restricted(const char *path, int flags, void *user_data)
249 {
250 	bool *grab = user_data;
251 	int fd = open(path, flags);
252 
253 	if (fd < 0)
254 		fprintf(stderr, "Failed to open %s (%s)\n",
255 			path, strerror(errno));
256 	else if (grab && *grab && ioctl(fd, EVIOCGRAB, (void*)1) == -1)
257 		fprintf(stderr, "Grab requested, but failed for %s (%s)\n",
258 			path, strerror(errno));
259 
260 	return fd < 0 ? -errno : fd;
261 }
262 
263 static void
close_restricted(int fd,void * user_data)264 close_restricted(int fd, void *user_data)
265 {
266 	close(fd);
267 }
268 
269 static const struct libinput_interface interface = {
270 	.open_restricted = open_restricted,
271 	.close_restricted = close_restricted,
272 };
273 
274 static struct libinput *
tools_open_udev(const char * seat,bool verbose,bool * grab)275 tools_open_udev(const char *seat, bool verbose, bool *grab)
276 {
277 	struct libinput *li;
278 	struct udev *udev = udev_new();
279 
280 	if (!udev) {
281 		fprintf(stderr, "Failed to initialize udev\n");
282 		return NULL;
283 	}
284 
285 	li = libinput_udev_create_context(&interface, grab, udev);
286 	if (!li) {
287 		fprintf(stderr, "Failed to initialize context from udev\n");
288 		goto out;
289 	}
290 
291 	libinput_log_set_handler(li, log_handler);
292 	if (verbose)
293 		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
294 
295 	if (libinput_udev_assign_seat(li, seat)) {
296 		fprintf(stderr, "Failed to set seat\n");
297 		libinput_unref(li);
298 		li = NULL;
299 		goto out;
300 	}
301 
302 out:
303 	udev_unref(udev);
304 	return li;
305 }
306 
307 static struct libinput *
tools_open_device(const char ** paths,bool verbose,bool * grab)308 tools_open_device(const char **paths, bool verbose, bool *grab)
309 {
310 	struct libinput_device *device;
311 	struct libinput *li;
312 	const char **p = paths;
313 
314 	li = libinput_path_create_context(&interface, grab);
315 	if (!li) {
316 		fprintf(stderr, "Failed to initialize path context\n");
317 		return NULL;
318 	}
319 
320 	if (verbose) {
321 		libinput_log_set_handler(li, log_handler);
322 		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
323 	}
324 
325 	while (*p) {
326 		device = libinput_path_add_device(li, *p);
327 		if (!device) {
328 			fprintf(stderr, "Failed to initialize device %s\n", *p);
329 			libinput_unref(li);
330 			li = NULL;
331 			break;
332 		}
333 		p++;
334 	}
335 
336 	return li;
337 }
338 
339 static void
tools_setenv_quirks_dir(void)340 tools_setenv_quirks_dir(void)
341 {
342 	char *builddir = builddir_lookup();
343 	if (builddir) {
344 		setenv("LIBINPUT_QUIRKS_DIR", LIBINPUT_QUIRKS_SRCDIR, 0);
345 		free(builddir);
346 	}
347 }
348 
349 struct libinput *
tools_open_backend(enum tools_backend which,const char ** seat_or_device,bool verbose,bool * grab)350 tools_open_backend(enum tools_backend which,
351 		   const char **seat_or_device,
352 		   bool verbose,
353 		   bool *grab)
354 {
355 	struct libinput *li;
356 
357 	tools_setenv_quirks_dir();
358 
359 	switch (which) {
360 	case BACKEND_UDEV:
361 		li = tools_open_udev(seat_or_device[0], verbose, grab);
362 		break;
363 	case BACKEND_DEVICE:
364 		li = tools_open_device(seat_or_device, verbose, grab);
365 		break;
366 	default:
367 		abort();
368 	}
369 
370 	return li;
371 }
372 
373 void
tools_device_apply_config(struct libinput_device * device,struct tools_options * options)374 tools_device_apply_config(struct libinput_device *device,
375 			  struct tools_options *options)
376 {
377 	const char *name = libinput_device_get_name(device);
378 
379 	if (libinput_device_config_send_events_get_modes(device) &
380 	      LIBINPUT_CONFIG_SEND_EVENTS_DISABLED &&
381 	    fnmatch(options->disable_pattern, name, 0) != FNM_NOMATCH) {
382 		libinput_device_config_send_events_set_mode(device,
383 					    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
384 	}
385 
386 	if (strlen(options->match) > 0 &&
387 	    fnmatch(options->match, name, 0) == FNM_NOMATCH)
388 		return;
389 
390 	if (options->tapping != -1)
391 		libinput_device_config_tap_set_enabled(device, options->tapping);
392 	if (options->tap_map != (enum libinput_config_tap_button_map)-1)
393 		libinput_device_config_tap_set_button_map(device,
394 							  options->tap_map);
395 	if (options->drag != -1)
396 		libinput_device_config_tap_set_drag_enabled(device,
397 							    options->drag);
398 	if (options->drag_lock != -1)
399 		libinput_device_config_tap_set_drag_lock_enabled(device,
400 								 options->drag_lock);
401 	if (options->natural_scroll != -1)
402 		libinput_device_config_scroll_set_natural_scroll_enabled(device,
403 									 options->natural_scroll);
404 	if (options->left_handed != -1)
405 		libinput_device_config_left_handed_set(device, options->left_handed);
406 	if (options->middlebutton != -1)
407 		libinput_device_config_middle_emulation_set_enabled(device,
408 								    options->middlebutton);
409 
410 	if (options->dwt != -1)
411 		libinput_device_config_dwt_set_enabled(device, options->dwt);
412 
413 	if (options->click_method != (enum libinput_config_click_method)-1)
414 		libinput_device_config_click_set_method(device, options->click_method);
415 
416 	if (options->scroll_method != (enum libinput_config_scroll_method)-1)
417 		libinput_device_config_scroll_set_method(device,
418 							 options->scroll_method);
419 	if (options->scroll_button != -1)
420 		libinput_device_config_scroll_set_button(device,
421 							 options->scroll_button);
422 	if (options->scroll_button_lock != -1)
423 		libinput_device_config_scroll_set_button_lock(device,
424 							      options->scroll_button_lock);
425 
426 
427 	if (libinput_device_config_accel_is_available(device)) {
428 		libinput_device_config_accel_set_speed(device,
429 						       options->speed);
430 		if (options->profile != LIBINPUT_CONFIG_ACCEL_PROFILE_NONE)
431 			libinput_device_config_accel_set_profile(device,
432 								 options->profile);
433 	}
434 }
435 
436 static char*
find_device(const char * udev_tag)437 find_device(const char *udev_tag)
438 {
439 	struct udev *udev;
440 	struct udev_enumerate *e;
441 	struct udev_list_entry *entry = NULL;
442 	struct udev_device *device;
443 	const char *path, *sysname;
444 	char *device_node = NULL;
445 
446 	udev = udev_new();
447 	e = udev_enumerate_new(udev);
448 	udev_enumerate_add_match_subsystem(e, "input");
449 	udev_enumerate_scan_devices(e);
450 
451 	udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
452 		path = udev_list_entry_get_name(entry);
453 		device = udev_device_new_from_syspath(udev, path);
454 		if (!device)
455 			continue;
456 
457 		sysname = udev_device_get_sysname(device);
458 		if (strncmp("event", sysname, 5) != 0) {
459 			udev_device_unref(device);
460 			continue;
461 		}
462 
463 		if (udev_device_get_property_value(device, udev_tag))
464 			device_node = safe_strdup(udev_device_get_devnode(device));
465 
466 		udev_device_unref(device);
467 
468 		if (device_node)
469 			break;
470 	}
471 	udev_enumerate_unref(e);
472 	udev_unref(udev);
473 
474 	return device_node;
475 }
476 
477 bool
find_touchpad_device(char * path,size_t path_len)478 find_touchpad_device(char *path, size_t path_len)
479 {
480 	char *devnode = find_device("ID_INPUT_TOUCHPAD");
481 
482 	if (devnode) {
483 		snprintf(path, path_len, "%s", devnode);
484 		free(devnode);
485 	}
486 
487 	return devnode != NULL;
488 }
489 
490 bool
is_touchpad_device(const char * devnode)491 is_touchpad_device(const char *devnode)
492 {
493 	struct udev *udev;
494 	struct udev_device *dev = NULL;
495 	struct stat st;
496 	bool is_touchpad = false;
497 
498 	if (stat(devnode, &st) < 0)
499 		return false;
500 
501 	udev = udev_new();
502 	dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
503 	if (!dev)
504 		goto out;
505 
506 	is_touchpad = udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD");
507 out:
508 	if (dev)
509 		udev_device_unref(dev);
510 	udev_unref(udev);
511 
512 	return is_touchpad;
513 }
514 
515 static inline void
setup_path(void)516 setup_path(void)
517 {
518 	const char *path = getenv("PATH");
519 	char new_path[PATH_MAX];
520 	const char *extra_path = LIBINPUT_TOOL_PATH;
521 	char *builddir = builddir_lookup();
522 
523 	snprintf(new_path,
524 		 sizeof(new_path),
525 		 "%s:%s",
526 		 builddir ? builddir : extra_path,
527 		 path ? path : "");
528 	setenv("PATH", new_path, 1);
529 	free(builddir);
530 }
531 
532 int
tools_exec_command(const char * prefix,int real_argc,char ** real_argv)533 tools_exec_command(const char *prefix, int real_argc, char **real_argv)
534 {
535 	char *argv[64] = {NULL};
536 	char executable[128];
537 	const char *command;
538 	int rc;
539 
540 	assert((size_t)real_argc < ARRAY_LENGTH(argv));
541 
542 	command = real_argv[0];
543 
544 	rc = snprintf(executable,
545 		      sizeof(executable),
546 		      "%s-%s",
547 		      prefix,
548 		      command);
549 	if (rc >= (int)sizeof(executable)) {
550 		fprintf(stderr, "Failed to assemble command.\n");
551 		return EXIT_FAILURE;
552 	}
553 
554 	argv[0] = executable;
555 	for (int i = 1; i < real_argc; i++)
556 		argv[i] = real_argv[i];
557 
558 	setup_path();
559 
560 	rc = execvp(executable, argv);
561 	if (rc) {
562 		if (errno == ENOENT) {
563 			fprintf(stderr,
564 				"libinput: %s is not installed\n",
565 				command);
566 			return EXIT_INVALID_USAGE;
567 		} else {
568 			fprintf(stderr,
569 				"Failed to execute '%s' (%s)\n",
570 				command,
571 				strerror(errno));
572 		}
573 	}
574 
575 	return EXIT_FAILURE;
576 }
577 
578 static void
sprintf_event_codes(char * buf,size_t sz,struct quirks * quirks)579 sprintf_event_codes(char *buf, size_t sz, struct quirks *quirks)
580 {
581 	const struct quirk_tuples *t;
582 	size_t off = 0;
583 	int printed;
584 	const char *name;
585 
586 	quirks_get_tuples(quirks, QUIRK_ATTR_EVENT_CODE_DISABLE, &t);
587 	name = quirk_get_name(QUIRK_ATTR_EVENT_CODE_DISABLE);
588 	printed = snprintf(buf, sz, "%s=", name);
589 	assert(printed != -1);
590 	off += printed;
591 
592 	for (size_t i = 0; off < sz && i < t->ntuples; i++) {
593 		const char *name = libevdev_event_code_get_name(
594 						t->tuples[i].first,
595 						t->tuples[i].second);
596 
597 		printed = snprintf(buf + off, sz - off, "%s;", name);
598 		assert(printed != -1);
599 		off += printed;
600 	}
601 }
602 
603 void
tools_list_device_quirks(struct quirks_context * ctx,struct udev_device * device,void (* callback)(void * data,const char * str),void * userdata)604 tools_list_device_quirks(struct quirks_context *ctx,
605 			 struct udev_device *device,
606 			 void (*callback)(void *data, const char *str),
607 			 void *userdata)
608 {
609 	char buf[256];
610 
611 	struct quirks *quirks;
612 	enum quirk q;
613 
614 	quirks = quirks_fetch_for_device(ctx, device);
615 	if (!quirks)
616 		return;
617 
618 	q = QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD;
619 	do {
620 		if (quirks_has_quirk(quirks, q)) {
621 			const char *name;
622 			bool b;
623 
624 			name = quirk_get_name(q);
625 			quirks_get_bool(quirks, q, &b);
626 			snprintf(buf, sizeof(buf), "%s=%d", name, b ? 1 : 0);
627 			callback(userdata, buf);
628 		}
629 	} while(++q < _QUIRK_LAST_MODEL_QUIRK_);
630 
631 	q = QUIRK_ATTR_SIZE_HINT;
632 	do {
633 		if (quirks_has_quirk(quirks, q)) {
634 			const char *name;
635 			struct quirk_dimensions dim;
636 			struct quirk_range r;
637 			uint32_t v;
638 			char *s;
639 			double d;
640 
641 			name = quirk_get_name(q);
642 
643 			switch (q) {
644 			case QUIRK_ATTR_SIZE_HINT:
645 			case QUIRK_ATTR_RESOLUTION_HINT:
646 				quirks_get_dimensions(quirks, q, &dim);
647 				snprintf(buf, sizeof(buf), "%s=%zdx%zd", name, dim.x, dim.y);
648 				callback(userdata, buf);
649 				break;
650 			case QUIRK_ATTR_TOUCH_SIZE_RANGE:
651 			case QUIRK_ATTR_PRESSURE_RANGE:
652 				quirks_get_range(quirks, q, &r);
653 				snprintf(buf, sizeof(buf), "%s=%d:%d", name, r.upper, r.lower);
654 				callback(userdata, buf);
655 				break;
656 			case QUIRK_ATTR_PALM_SIZE_THRESHOLD:
657 			case QUIRK_ATTR_PALM_PRESSURE_THRESHOLD:
658 			case QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD:
659 			case QUIRK_ATTR_THUMB_SIZE_THRESHOLD:
660 				quirks_get_uint32(quirks, q, &v);
661 				snprintf(buf, sizeof(buf), "%s=%u", name, v);
662 				callback(userdata, buf);
663 				break;
664 			case QUIRK_ATTR_LID_SWITCH_RELIABILITY:
665 			case QUIRK_ATTR_KEYBOARD_INTEGRATION:
666 			case QUIRK_ATTR_TRACKPOINT_INTEGRATION:
667 			case QUIRK_ATTR_TPKBCOMBO_LAYOUT:
668 			case QUIRK_ATTR_MSC_TIMESTAMP:
669 				quirks_get_string(quirks, q, &s);
670 				snprintf(buf, sizeof(buf), "%s=%s", name, s);
671 				callback(userdata, buf);
672 				break;
673 			case QUIRK_ATTR_TRACKPOINT_MULTIPLIER:
674 				quirks_get_double(quirks, q, &d);
675 				snprintf(buf, sizeof(buf), "%s=%0.2f", name, d);
676 				callback(userdata, buf);
677 				break;
678 			case QUIRK_ATTR_USE_VELOCITY_AVERAGING:
679 				snprintf(buf, sizeof(buf), "%s=1", name);
680 				callback(userdata, buf);
681 				break;
682 			case QUIRK_ATTR_EVENT_CODE_DISABLE:
683 				sprintf_event_codes(buf, sizeof(buf), quirks);
684 				callback(userdata, buf);
685 				break;
686 			default:
687 				abort();
688 				break;
689 			}
690 		}
691 	} while(++q < _QUIRK_LAST_ATTR_QUIRK_);
692 
693 	quirks_unref(quirks);
694 }
695