evdev_configure_device(struct evdev_device *device)
{
	struct libevdev *evdev = device->evdev;
	enum evdev_device_udev_tags udev_tags;
	unsigned int tablet_tags;
	struct evdev_dispatch *dispatch;

	udev_tags = evdev_device_get_udev_tags(device, device->udev_device);

	if ((udev_tags & EVDEV_UDEV_TAG_INPUT) == 0 ||
	    (udev_tags & ~EVDEV_UDEV_TAG_INPUT) == 0) {
		evdev_log_info(device,
			       "not tagged as supported input device\n");
		return NULL;
	}

	evdev_log_info(device,
		 "is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n",
		 udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "",
		 udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "",
		 udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "",
		 udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "",
		 udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "",
		 udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "",
		 udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "",
		 udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "",
		 udev_tags & EVDEV_UDEV_TAG_TABLET_PAD ? " TabletPad" : "",
		 udev_tags & EVDEV_UDEV_TAG_TRACKBALL ? " Trackball" : "",
		 udev_tags & EVDEV_UDEV_TAG_SWITCH ? " Switch" : "");

	/* Ignore pure accelerometers, but accept devices that are
	 * accelerometers with other axes */
	if (udev_tags == (EVDEV_UDEV_TAG_INPUT|EVDEV_UDEV_TAG_ACCELEROMETER)) {
		evdev_log_info(device,
			 "device is an accelerometer, ignoring\n");
		return NULL;
	}

	if (udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER) {
		evdev_disable_accelerometer_axes(device);
	}

	if (udev_tags == (EVDEV_UDEV_TAG_INPUT|EVDEV_UDEV_TAG_JOYSTICK)) {
		evdev_log_info(device,
			       "device is a joystick, ignoring\n");
		return NULL;
	}

	if (evdev_reject_device(device)) {
		evdev_log_info(device, "was rejected\n");
		return NULL;
	}

	if (!evdev_is_fake_mt_device(device))
		evdev_fix_android_mt(device);

	if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) {
		evdev_extract_abs_axes(device, udev_tags);

		if (evdev_is_fake_mt_device(device))
			udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN;
	}

	if (evdev_device_has_model_quirk(device,
					 QUIRK_MODEL_DELL_CANVAS_TOTEM)) {
		dispatch = evdev_totem_create(device);
		device->seat_caps |= EVDEV_DEVICE_TABLET;
		evdev_log_info(device, "device is a totem\n");
		return dispatch;
	}

	/* libwacom assigns touchpad (or touchscreen) _and_ tablet to the
	   tablet touch bits, so make sure we don't initialize the tablet
	   interface for the touch device */
	tablet_tags = EVDEV_UDEV_TAG_TABLET |
		      EVDEV_UDEV_TAG_TOUCHPAD |
		      EVDEV_UDEV_TAG_TOUCHSCREEN;

	/* libwacom assigns tablet _and_ tablet_pad to the pad devices */
	if (udev_tags & EVDEV_UDEV_TAG_TABLET_PAD) {
		dispatch = evdev_tablet_pad_create(device);
		device->seat_caps |= EVDEV_DEVICE_TABLET_PAD;
		evdev_log_info(device, "device is a tablet pad\n");
		return dispatch;

	}

	if ((udev_tags & tablet_tags) == EVDEV_UDEV_TAG_TABLET) {
		dispatch = evdev_tablet_create(device);
		device->seat_caps |= EVDEV_DEVICE_TABLET;
		evdev_log_info(device, "device is a tablet\n");
		return dispatch;
	}

	if (udev_tags & EVDEV_UDEV_TAG_TOUCHPAD) {
		if (udev_tags & EVDEV_UDEV_TAG_TABLET)
			evdev_tag_tablet_touchpad(device);
		/* whether velocity should be averaged, false by default */
		device->use_velocity_averaging = evdev_need_velocity_averaging(device);
		dispatch = evdev_mt_touchpad_create(device);
		evdev_log_info(device, "device is a touchpad\n");
		return dispatch;
	}

	if (udev_tags & EVDEV_UDEV_TAG_MOUSE ||
	    udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK) {
		evdev_tag_external_mouse(device, device->udev_device);
		evdev_tag_trackpoint(device, device->udev_device);
		device->dpi = evdev_read_dpi_prop(device);
		device->trackpoint_multiplier = evdev_get_trackpoint_multiplier(device);
		/* whether velocity should be averaged, false by default */
		device->use_velocity_averaging = evdev_need_velocity_averaging(device);

		device->seat_caps |= EVDEV_DEVICE_POINTER;

		evdev_log_info(device, "device is a pointer\n");

		/* want left-handed config option */
		device->left_handed.want_enabled = true;
		/* want natural-scroll config option */
		device->scroll.natural_scrolling_enabled = true;
		/* want button scrolling config option */
		if (libevdev_has_event_code(evdev, EV_REL, REL_X) ||
		    libevdev_has_event_code(evdev, EV_REL, REL_Y))
			device->scroll.want_button = 1;
	}

	if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) {
		device->seat_caps |= EVDEV_DEVICE_KEYBOARD;
		evdev_log_info(device, "device is a keyboard\n");

		/* want natural-scroll config option */
		if (libevdev_has_event_code(evdev, EV_REL, REL_WHEEL) ||
		    libevdev_has_event_code(evdev, EV_REL, REL_HWHEEL)) {
			device->scroll.natural_scrolling_enabled = true;
			device->seat_caps |= EVDEV_DEVICE_POINTER;
		}

		evdev_tag_keyboard(device, device->udev_device);
	}

	if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) {
		device->seat_caps |= EVDEV_DEVICE_TOUCH;
		evdev_log_info(device, "device is a touch device\n");
	}

	if (udev_tags & EVDEV_UDEV_TAG_SWITCH) {
		if (libevdev_has_event_code(evdev, EV_SW, SW_LID)) {
			device->seat_caps |= EVDEV_DEVICE_SWITCH;
			device->tags |= EVDEV_TAG_LID_SWITCH;
			evdev_log_info(device, "device is a switch device\n");
		}

		if (libevdev_has_event_code(evdev, EV_SW, SW_TABLET_MODE)) {
		    if (evdev_device_has_model_quirk(device,
				 QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE)) {
			    evdev_log_info(device,
				"device is an unreliable tablet mode switch, filtering events.\n");
			    libevdev_disable_event_code(device->evdev,
							EV_SW,
							SW_TABLET_MODE);
		    } else {
			    device->tags |= EVDEV_TAG_TABLET_MODE_SWITCH;
			    device->seat_caps |= EVDEV_DEVICE_SWITCH;
		    }
		}

		if (device->seat_caps & EVDEV_DEVICE_SWITCH)
		    evdev_log_info(device, "device is a switch device\n");
	}

	if (device->seat_caps & EVDEV_DEVICE_POINTER &&
	    libevdev_has_event_code(evdev, EV_REL, REL_X) &&
	    libevdev_has_event_code(evdev, EV_REL, REL_Y) &&
	    !evdev_init_accel(device, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE)) {
		evdev_log_error(device,
				"failed to initialize pointer acceleration\n");
		return NULL;
	}

	if (evdev_device_has_model_quirk(device, QUIRK_MODEL_INVERT_HORIZONTAL_SCROLLING)) {
		device->scroll.invert_horizontal_scrolling = true;
	}

	return fallback_dispatch_create(&device->base);
}
evdev_configure_device(struct evdev_device *device)
{
	struct libevdev *evdev = device->evdev;
	enum evdev_device_udev_tags udev_tags;
	unsigned int tablet_tags;
	struct evdev_dispatch *dispatch;

	udev_tags = evdev_device_get_udev_tags(device, device->udev_device);

	if ((udev_tags & EVDEV_UDEV_TAG_INPUT) == 0 ||
	    (udev_tags & ~EVDEV_UDEV_TAG_INPUT) == 0) {
		evdev_log_info(device,
			       "not tagged as supported input device\n");
		return NULL;
	}

	evdev_log_info(device,
		 "is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n",
		 udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "",
		 udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "",
		 udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "",
		 udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "",
		 udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "",
		 udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "",
		 udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "",
		 udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "",
		 udev_tags & EVDEV_UDEV_TAG_TABLET_PAD ? " TabletPad" : "",
		 udev_tags & EVDEV_UDEV_TAG_TRACKBALL ? " Trackball" : "",
		 udev_tags & EVDEV_UDEV_TAG_SWITCH ? " Switch" : "");

	 
	if (udev_tags == (EVDEV_UDEV_TAG_INPUT|EVDEV_UDEV_TAG_ACCELEROMETER)) {
		evdev_log_info(device,
			 "device is an accelerometer, ignoring\n");
		return NULL;
	}

	if (udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER) {
		evdev_disable_accelerometer_axes(device);
	}

	if (udev_tags == (EVDEV_UDEV_TAG_INPUT|EVDEV_UDEV_TAG_JOYSTICK)) {
		evdev_log_info(device,
			       "device is a joystick, ignoring\n");
		return NULL;
	}

	if (evdev_reject_device(device)) {
		evdev_log_info(device, "was rejected\n");
		return NULL;
	}

	if (!evdev_is_fake_mt_device(device))
		evdev_fix_android_mt(device);

	if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) {
		evdev_extract_abs_axes(device, udev_tags);

		if (evdev_is_fake_mt_device(device))
			udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN;
	}

	if (evdev_device_has_model_quirk(device,
					 QUIRK_MODEL_DELL_CANVAS_TOTEM)) {
		dispatch = evdev_totem_create(device);
		device->seat_caps |= EVDEV_DEVICE_TABLET;
		evdev_log_info(device, "device is a totem\n");
		return dispatch;
	}

	 
	tablet_tags = EVDEV_UDEV_TAG_TABLET |
		      EVDEV_UDEV_TAG_TOUCHPAD |
		      EVDEV_UDEV_TAG_TOUCHSCREEN;

	 
	if (udev_tags & EVDEV_UDEV_TAG_TABLET_PAD) {
		dispatch = evdev_tablet_pad_create(device);
		device->seat_caps |= EVDEV_DEVICE_TABLET_PAD;
		evdev_log_info(device, "device is a tablet pad\n");
		return dispatch;

	}

	if ((udev_tags & tablet_tags) == EVDEV_UDEV_TAG_TABLET) {
		dispatch = evdev_tablet_create(device);
		device->seat_caps |= EVDEV_DEVICE_TABLET;
		evdev_log_info(device, "device is a tablet\n");
		return dispatch;
	}

	if (udev_tags & EVDEV_UDEV_TAG_TOUCHPAD) {
		if (udev_tags & EVDEV_UDEV_TAG_TABLET)
			evdev_tag_tablet_touchpad(device);
		 
		device->use_velocity_averaging = evdev_need_velocity_averaging(device);
		dispatch = evdev_mt_touchpad_create(device);
		evdev_log_info(device, "device is a touchpad\n");
		return dispatch;
	}

	if (udev_tags & EVDEV_UDEV_TAG_MOUSE ||
	    udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK) {
		evdev_tag_external_mouse(device, device->udev_device);
		evdev_tag_trackpoint(device, device->udev_device);
		device->dpi = evdev_read_dpi_prop(device);
		device->trackpoint_multiplier = evdev_get_trackpoint_multiplier(device);
		 
		device->use_velocity_averaging = evdev_need_velocity_averaging(device);

		device->seat_caps |= EVDEV_DEVICE_POINTER;

		evdev_log_info(device, "device is a pointer\n");

		 
		device->left_handed.want_enabled = true;
		 
		device->scroll.natural_scrolling_enabled = true;
		 
		if (libevdev_has_event_code(evdev, EV_REL, REL_X) ||
		    libevdev_has_event_code(evdev, EV_REL, REL_Y))
			device->scroll.want_button = 1;
	}

	if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) {
		device->seat_caps |= EVDEV_DEVICE_KEYBOARD;
		evdev_log_info(device, "device is a keyboard\n");

		 
		if (libevdev_has_event_code(evdev, EV_REL, REL_WHEEL) ||
		    libevdev_has_event_code(evdev, EV_REL, REL_HWHEEL)) {
			device->scroll.natural_scrolling_enabled = true;
			device->seat_caps |= EVDEV_DEVICE_POINTER;
		}

		evdev_tag_keyboard(device, device->udev_device);
	}

	if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) {
		device->seat_caps |= EVDEV_DEVICE_TOUCH;
		evdev_log_info(device, "device is a touch device\n");
	}

	if (udev_tags & EVDEV_UDEV_TAG_SWITCH) {
		if (libevdev_has_event_code(evdev, EV_SW, SW_LID)) {
			device->seat_caps |= EVDEV_DEVICE_SWITCH;
			device->tags |= EVDEV_TAG_LID_SWITCH;
			evdev_log_info(device, "device is a switch device\n");
		}

		if (libevdev_has_event_code(evdev, EV_SW, SW_TABLET_MODE)) {
		    if (evdev_device_has_model_quirk(device,
				 QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE)) {
			    evdev_log_info(device,
				"device is an unreliable tablet mode switch, filtering events.\n");
			    libevdev_disable_event_code(device->evdev,
							EV_SW,
							SW_TABLET_MODE);
		    } else {
			    device->tags |= EVDEV_TAG_TABLET_MODE_SWITCH;
			    device->seat_caps |= EVDEV_DEVICE_SWITCH;
		    }
		}

		if (device->seat_caps & EVDEV_DEVICE_SWITCH)
		    evdev_log_info(device, "device is a switch device\n");
	}

	if (device->seat_caps & EVDEV_DEVICE_POINTER &&
	    libevdev_has_event_code(evdev, EV_REL, REL_X) &&
	    libevdev_has_event_code(evdev, EV_REL, REL_Y) &&
	    !evdev_init_accel(device, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE)) {
		evdev_log_error(device,
				"failed to initialize pointer acceleration\n");
		return NULL;
	}

	if (evdev_device_has_model_quirk(device, QUIRK_MODEL_INVERT_HORIZONTAL_SCROLLING)) {
		device->scroll.invert_horizontal_scrolling = true;
	}

	return fallback_dispatch_create(&device->base);
}