コード例 #1
0
ファイル: test-common.c プロジェクト: AndrewSmart/libevdev
void test_create_device(struct uinput_device **uidev_return,
			struct libevdev **dev_return,
			...)
{
	int rc, fd;
	struct uinput_device *uidev;
	struct libevdev *dev;
	va_list args;

	va_start(args, dev_return);

	rc = uinput_device_new_with_events_v(&uidev, TEST_DEVICE_NAME, DEFAULT_IDS, args);
	va_end(args);

	ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc));

	fd = uinput_device_get_fd(uidev);

	rc = libevdev_new_from_fd(fd, &dev);
	ck_assert_msg(rc == 0, "Failed to init device device: %s", strerror(-rc));
	rc = fcntl(fd, F_SETFL, O_NONBLOCK);
	ck_assert_msg(rc == 0, "fcntl failed: %s", strerror(errno));

	*uidev_return = uidev;
	*dev_return = dev;
}
コード例 #2
0
ファイル: adhoc-internal.c プロジェクト: sattlerc/sb680
struct adhoc* adhoc_init(int fd) {
  struct adhoc *adhoc = calloc(1, sizeof(struct adhoc));
  int r = libevdev_new_from_fd(fd, &adhoc->dev);
  if (r) {
    error(0, -r, "libevdev_new_from_fd failed");
    goto creation_error;
  }

  const char *name = libevdev_get_name(adhoc->dev);
  if (strcmp(name, "SMART SB680 Interactive Whiteboard") == 0)
    adhoc->internal_calls = internal_calls_sb680;
  else if (strcmp(name, "SMART SB885 Interactive Whiteboard") == 0)
    adhoc->internal_calls = internal_calls_sb885;
  else {
    fprintf(stderr, "unknown device: %s\n", name);
    goto device_error;
  }

#ifdef DEBUG
  fprintf(stderr, "device: %s\n", name);
#endif
  
  if (adhoc->internal_calls.init(adhoc))
    goto device_error;

  return adhoc;

 device_error:
  libevdev_free(adhoc->dev);
  
 creation_error:
  free(adhoc);
  return NULL;
}
コード例 #3
0
ファイル: mouse-dpi-tool.c プロジェクト: chengyake/karch
int
main (int argc, char **argv) {
	int rc;
	int fd;
	const char *path;
	struct libevdev *dev;
	struct measurements measurements = {0};

	if (argc < 2)
		return usage();

	path = argv[1];
	if (path[0] == '-')
		return usage();

	fd = open(path, O_RDONLY|O_NONBLOCK);
	if (fd < 0) {
		fprintf(stderr, "Error opening the device: %s\n", strerror(errno));
		return 1;
	}

	rc = libevdev_new_from_fd(fd, &dev);
	if (rc != 0) {
		fprintf(stderr, "Error fetching the device info: %s\n", strerror(-rc));
		return 1;
	}

	if (libevdev_grab(dev, LIBEVDEV_GRAB) != 0) {
		fprintf(stderr, "Error: cannot grab the device, something else is grabbing it.\n");
		fprintf(stderr, "Use 'fuser -v %s' to find processes with an open fd\n", path);
		return 1;
	}
	libevdev_grab(dev, LIBEVDEV_UNGRAB);

	printf("Mouse %s on %s\n", libevdev_get_name(dev), path);
	printf("Move the device 250mm/10in or more along the x-axis.\n");
	printf("Pause 3 seconds before movement to reset, Ctrl+C to exit.\n");
	setbuf(stdout, NULL);

	rc = mainloop(dev, &measurements);

	printf("\n");

	print_summary(&measurements);

	printf("\n");
	printf("Entry for hwdb match (replace XXX with the resolution in DPI):\n"
	       "mouse:%s:v%04xp%04x:name:%s:\n"
	       " MOUSE_DPI=XXX@%d\n",
	       bustype(libevdev_get_id_bustype(dev)),
	       libevdev_get_id_vendor(dev),
	       libevdev_get_id_product(dev),
	       libevdev_get_name(dev),
	       (int)measurements.max_frequency);

	libevdev_free(dev);
	close(fd);

	return rc;
}
コード例 #4
0
ファイル: joystick_linux.cpp プロジェクト: Scrik/godot
void joystick_linux::open_joystick(const char *p_path) {

	int joy_num = get_free_joy_slot();
	int fd = open(p_path, O_RDONLY | O_NONBLOCK);
	if (fd != -1 && joy_num != -1) {

		int rc = libevdev_new_from_fd(fd, &joysticks[joy_num].dev);
		if (rc < 0) {

			fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc));
			return;
		}

		libevdev *dev = joysticks[joy_num].dev;

		//check if the device supports basic gamepad events, prevents certain keyboards from
		//being detected as joysticks
		if (libevdev_has_event_type(dev, EV_ABS) && libevdev_has_event_type(dev, EV_KEY) &&
				(libevdev_has_event_code(dev, EV_KEY, BTN_A) || libevdev_has_event_code(dev, EV_KEY, BTN_THUMBL) || libevdev_has_event_code(dev, EV_KEY, BTN_TOP))) {

			char uid[128];
			String name = libevdev_get_name(dev);
			uint16_t bus = __bswap_16(libevdev_get_id_bustype(dev));
			uint16_t vendor = __bswap_16(libevdev_get_id_vendor(dev));
			uint16_t product = __bswap_16(libevdev_get_id_product(dev));
			uint16_t version = __bswap_16(libevdev_get_id_version(dev));

			joysticks[joy_num].reset();

			Joystick &joy = joysticks[joy_num];
			joy.fd = fd;
			joy.devpath = String(p_path);
			setup_joystick_properties(joy_num);
			sprintf(uid, "%04x%04x", bus, 0);
			if (vendor && product && version) {

				sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor,0,product,0,version,0);
				input->joy_connection_changed(joy_num, true, name, uid);
			}
			else {
				String uidname = uid;
				int uidlen = MIN(name.length(), 11);
				for (int i=0; i<uidlen; i++) {

					uidname = uidname + _hex_str(name[i]);
				}
				uidname += "00";
				input->joy_connection_changed(joy_num, true, name, uidname);

			}
		}
		else {
			//device is not a gamepad, clean up
			libevdev_free(dev);
			close(fd);
		}
	}
}
コード例 #5
0
ファイル: libevdev.cpp プロジェクト: azraelly/luacxx
int _libevdev_new_from_fd(lua_State* const state)
{
    libevdev* dev;
    lua::push(state, libevdev_new_from_fd(
        lua::get<int>(state, 1),
        &dev
    ));
    lua::push(state, dev);
    return 2;
}
コード例 #6
0
ファイル: test-uinput.c プロジェクト: AndrewSmart/libevdev
END_TEST

START_TEST(test_uinput_create_device_from_fd)
{
	struct libevdev *dev, *dev2;
	struct libevdev_uinput *uidev;
	int fd, fd2;
	unsigned int type, code;
	int rc;
	const char *devnode;

	dev = libevdev_new();
	ck_assert(dev != NULL);
	libevdev_set_name(dev, TEST_DEVICE_NAME);
	libevdev_enable_event_type(dev, EV_SYN);
	libevdev_enable_event_type(dev, EV_REL);
	libevdev_enable_event_code(dev, EV_REL, REL_X, NULL);
	libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL);

	fd = open(UINPUT_NODE, O_RDWR);
	ck_assert_int_gt(fd, -1);

	rc = libevdev_uinput_create_from_device(dev, fd, &uidev);
	ck_assert_int_eq(rc, 0);
	ck_assert(uidev != NULL);

	ck_assert_int_eq(libevdev_uinput_get_fd(uidev), fd);

	devnode = libevdev_uinput_get_devnode(uidev);
	ck_assert(devnode != NULL);

	fd2 = open(devnode, O_RDONLY);
	ck_assert_int_gt(fd2, -1);
	rc = libevdev_new_from_fd(fd2, &dev2);
	ck_assert_int_eq(rc, 0);

	for (type = 0; type < EV_CNT; type++) {
		int max = libevdev_event_type_get_max(type);
		if (max == -1)
			continue;

		for (code = 0; code < max; code++) {
			ck_assert_int_eq(libevdev_has_event_code(dev, type, code),
					 libevdev_has_event_code(dev2, type, code));
		}
	}

	libevdev_free(dev);
	libevdev_free(dev2);
	libevdev_uinput_destroy(uidev);
	close(fd);
	close(fd2);
}
コード例 #7
0
int power_key_resume_handler_init(void)
{
	const char *node_path;
    char *full_path;
	GDir *input_dir;
	int rc = 1;
	struct libevdev *dev = NULL;

	input_dir = g_dir_open("/dev/input", 0, NULL);
	if (!input_dir) {
		g_warning("Failed to reach /dev/input directory");
		return -ENODEV;
	}

	while ((node_path = g_dir_read_name(input_dir)) != NULL) {
		full_path = g_strdup_printf("/dev/input/%s", node_path);

		if (g_file_test(node_path, G_FILE_TEST_IS_DIR))
			continue;

		input_source_fd = open(full_path, O_RDONLY|O_NONBLOCK);
		g_free(full_path);

		if (input_source_fd < 0)
			continue;

		rc = libevdev_new_from_fd(input_source_fd, &dev);
		if (rc < 0) {
			fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc));
			close(input_source_fd);
			input_source_fd = -1;
			continue;
		}

		if (libevdev_has_event_code(dev, EV_KEY, KEY_POWER)) {
			libevdev_free(dev);
			break;
		}

		libevdev_free(dev);
		close(input_source_fd);
		input_source_fd = -1;
	}

	if (input_source_fd < 0)
		return -ENODEV;

	channel = g_io_channel_unix_new(input_source_fd);
	g_io_channel_set_encoding(channel, NULL, NULL);
	readwatch = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_NVAL, _handle_input_event, NULL);

	return 0;
}
コード例 #8
0
ファイル: evdev.cpp プロジェクト: MikeRavenelle/dolphin
evdevDevice::evdevDevice(const std::string& devnode) : m_devfile(devnode)
{
  // The device file will be read on one of the main threads, so we open in non-blocking mode.
  m_fd = open(devnode.c_str(), O_RDWR | O_NONBLOCK);
  int ret = libevdev_new_from_fd(m_fd, &m_dev);

  if (ret != 0)
  {
    // This useally fails because the device node isn't an evdev device, such as /dev/input/js0
    m_initialized = false;
    close(m_fd);
    return;
  }

  m_name = StripSpaces(libevdev_get_name(m_dev));

  // Controller buttons (and keyboard keys)
  int num_buttons = 0;
  for (int key = 0; key < KEY_MAX; key++)
    if (libevdev_has_event_code(m_dev, EV_KEY, key))
      AddInput(new Button(num_buttons++, key, m_dev));

  // Absolute axis (thumbsticks)
  int num_axis = 0;
  for (int axis = 0; axis < 0x100; axis++)
    if (libevdev_has_event_code(m_dev, EV_ABS, axis))
    {
      AddAnalogInputs(new Axis(num_axis, axis, false, m_dev),
                      new Axis(num_axis, axis, true, m_dev));
      num_axis++;
    }

  // Force feedback
  if (libevdev_has_event_code(m_dev, EV_FF, FF_PERIODIC))
  {
    for (auto type : {FF_SINE, FF_SQUARE, FF_TRIANGLE, FF_SAW_UP, FF_SAW_DOWN})
      if (libevdev_has_event_code(m_dev, EV_FF, type))
        AddOutput(new ForceFeedback(type, m_dev));
  }
  if (libevdev_has_event_code(m_dev, EV_FF, FF_RUMBLE))
  {
    AddOutput(new ForceFeedback(FF_RUMBLE, m_dev));
  }

  // TODO: Add leds as output devices

  m_initialized = true;
  m_interesting = num_axis >= 2 || num_buttons >= 8;
}
コード例 #9
0
ファイル: evdev.cpp プロジェクト: MikeRavenelle/dolphin
bool evdevDevice::IsValid() const
{
  int current_fd = libevdev_get_fd(m_dev);
  if (current_fd == -1)
    return false;

  libevdev* device;
  if (libevdev_new_from_fd(current_fd, &device) != 0)
  {
    close(current_fd);
    return false;
  }
  libevdev_free(device);
  return true;
}
コード例 #10
0
ファイル: test-common.c プロジェクト: AndrewSmart/libevdev
void test_create_abs_device(struct uinput_device **uidev_return,
			    struct libevdev **dev_return,
			    int nabs,
			    const struct input_absinfo *abs,
			    ...)
{
	int rc, fd;
	struct uinput_device *uidev;
	struct libevdev *dev;
	va_list args;

	uidev = uinput_device_new(TEST_DEVICE_NAME);
	ck_assert(uidev != NULL);

	va_start(args, abs);
	rc = uinput_device_set_event_bits_v(uidev, args);
	va_end(args);

	while (--nabs >= 0) {
		int code;
		struct input_absinfo a;

		code = abs[nabs].value;
		a = abs[nabs];
		a.value = 0;

		rc = uinput_device_set_abs_bit(uidev, code, &a);
		ck_assert_msg(rc == 0, "for abs field %d\n", nabs);
	}

	rc = uinput_device_create(uidev);
	ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc));

	fd = uinput_device_get_fd(uidev);

	rc = libevdev_new_from_fd(fd, &dev);
	ck_assert_msg(rc == 0, "Failed to init device device: %s", strerror(-rc));
	rc = fcntl(fd, F_SETFL, O_NONBLOCK);
	ck_assert_msg(rc == 0, "fcntl failed: %s", strerror(errno));

	*uidev_return = uidev;
	*dev_return = dev;
}
コード例 #11
0
ファイル: test-uinput.c プロジェクト: AndrewSmart/libevdev
END_TEST

START_TEST(test_uinput_properties)
{
	struct libevdev *dev, *dev2;
	struct libevdev_uinput *uidev;
	int fd;
	int rc;
	const char *devnode;

	dev = libevdev_new();
	ck_assert(dev != NULL);
	libevdev_set_name(dev, TEST_DEVICE_NAME);
	libevdev_enable_event_type(dev, EV_SYN);
	libevdev_enable_event_type(dev, EV_REL);
	libevdev_enable_event_type(dev, EV_KEY);
	libevdev_enable_event_code(dev, EV_REL, REL_X, NULL);
	libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL);
	libevdev_enable_event_code(dev, EV_KEY, BTN_LEFT, NULL);
	libevdev_enable_property(dev, INPUT_PROP_BUTTONPAD);
	libevdev_enable_property(dev, INPUT_PROP_MAX);

	rc = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev);
	ck_assert_int_eq(rc, 0);
	ck_assert(uidev != NULL);

	devnode = libevdev_uinput_get_devnode(uidev);
	ck_assert(devnode != NULL);

	fd = open(devnode, O_RDONLY);
	ck_assert_int_gt(fd, -1);
	rc = libevdev_new_from_fd(fd, &dev2);
	ck_assert_int_eq(rc, 0);

	ck_assert(libevdev_has_property(dev2, INPUT_PROP_BUTTONPAD));
	ck_assert(libevdev_has_property(dev2, INPUT_PROP_MAX));

	libevdev_free(dev);
	libevdev_free(dev2);
	libevdev_uinput_destroy(uidev);
	close(fd);
}
コード例 #12
0
int main(int argc, char **argv)
{
	struct libevdev *dev = NULL;
	struct libevdev_uinput *uidev = NULL;
	int fd, ret;

	if (argc < 2){
		fprintf(stderr, "Usage: %s <event device>\n", argv[0]);
		return 1;
	}
	
	//open the event device file descriptor
	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {perror("Failed to open device"); return fd;}

	//create a libevdev device from the event file descriptor
	ret = libevdev_new_from_fd(fd, &dev);
	if (ret < 0) {perror("Failed to create libevdev device"); return ret;}

	//create a device connected to the virtual device interface /dev/uinput
	ret = libevdev_uinput_create_from_device(
		dev,
		LIBEVDEV_UINPUT_OPEN_MANAGED,
		&uidev);
	if (ret < 0) {perror("Failed to create uinput device"); return ret;}

	sleep(1);
	//EVIOCGRAB the event, this means no other client will receive the events from the device, including your window manager.
	// "This is generally a bad idea. Don't do this." - libevdev documentation
	//We need to do this to avoid douplicate keyboard events.
	ret = libevdev_grab(dev, LIBEVDEV_GRAB);

	//start the interceptor and never return
	intercept(uidev, dev);

	libevdev_uinput_destroy(uidev);
	libevdev_grab(dev, LIBEVDEV_UNGRAB);
	libevdev_free(dev);
	return ret;
}
コード例 #13
0
int
main(int argc, char **argv)
{
	struct libevdev *dev = NULL;
	int fd = -1;
	int rc = EXIT_FAILURE;
	enum mode mode;
	const char *path;
	struct input_absinfo absinfo;
	int axis = -1;
	int led = -1;
	int led_state = -1;
	unsigned int changes = 0; /* bitmask of changes */
	int xres = 0,
	    yres = 0;

	mode = parse_options_mode(argc, argv);
	switch (mode) {
		case MODE_HELP:
			rc = EXIT_SUCCESS;
			/* fallthrough */
		case MODE_NONE:
			usage();
			goto out;
		case MODE_ABS:
			rc = parse_options_abs(argc, argv, &changes, &axis,
					       &absinfo);
			break;
		case MODE_LED:
			rc = parse_options_led(argc, argv, &led, &led_state);
			break;
		case MODE_RESOLUTION:
			rc = parse_options_resolution(argc, argv, &xres,
						      &yres);
			break;
		default:
			fprintf(stderr,
				"++?????++ Out of Cheese Error. Redo From Start.\n");
			goto out;
	}

	if (rc != EXIT_SUCCESS)
		goto out;

	if (optind >= argc) {
		rc = EXIT_FAILURE;
		usage();
		goto out;
	}

	path = argv[optind];

	fd = open(path, O_RDWR);
	if (fd < 0) {
		rc = EXIT_FAILURE;
		perror("Failed to open device");
		goto out;
	}

	rc = libevdev_new_from_fd(fd, &dev);
	if (rc < 0) {
		fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc));
		goto out;
	}

	switch (mode) {
		case MODE_ABS:
			set_abs(dev, changes, axis, &absinfo);
			break;
		case MODE_LED:
			set_led(dev, led, led_state);
			break;
		case MODE_RESOLUTION:
			set_resolution(dev, xres, yres);
			break;
		default:
			break;
	}

out:
	libevdev_free(dev);
	if (fd != -1)
		close(fd);

	return rc;
}
コード例 #14
0
ファイル: evmpd.c プロジェクト: hawx/evmpd
int main(int argc, char const *argv[])
{
  if (argc < 5) {
    printf("Usage: evmpd HOSTNAME PORT DEVICE HALT_COMMAND\n");
    return EXIT_FAILURE;
  }

  struct mpd_connection *client = connect(argv[1], strtol(argv[2], NULL, 10));

  struct libevdev *dev = NULL;
  int fd;
  int rc = 1;

  fd = open(argv[3], O_RDONLY | O_NONBLOCK);
  rc = libevdev_new_from_fd(fd, &dev);
  if (rc < 0) {
    fprintf(stderr, "Failed to init libevdev (%d)\n", strerror(-rc));
    return EXIT_FAILURE;
  }
  printf("Input device name: \"%s\"\n", libevdev_get_name(dev));
  printf("Input device ID: bus %#x vendor %#x product %#x\n",
         libevdev_get_id_bustype(dev),
         libevdev_get_id_vendor(dev), libevdev_get_id_product(dev));

  do {
    struct input_event ev;

    rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);

    if (rc == 0 && ev.value == 0) {
      if (libevdev_event_is_code(&ev, EV_KEY, KEY_F4)) {
        system(argv[4]);
        printf("shutdown\n");

      } else if (libevdev_event_is_code(&ev, EV_KEY, KEY_PLAYPAUSE)) {
        struct mpd_status *status = mpd_run_status(client);
        enum mpd_state state = mpd_status_get_state(status);

        if (state != MPD_STATE_PLAY && state != MPD_STATE_PAUSE) {
          mpd_run_play(client);
          printf("play\n");
        } else {
          mpd_run_toggle_pause(client);
          printf("toggle-pause\n");
        }

      } else if (libevdev_event_is_code(&ev, EV_KEY, KEY_STOPCD)) {
        mpd_run_stop(client);
        printf("stop\n");

      } else if (libevdev_event_is_code(&ev, EV_KEY, KEY_PREVIOUSSONG)) {
        mpd_run_previous(client);
        printf("previous\n");

      } else if (libevdev_event_is_code(&ev, EV_KEY, KEY_NEXTSONG)) {
        mpd_run_next(client);
        printf("next\n");

      } else if (libevdev_event_is_code(&ev, EV_KEY, KEY_VOLUMEUP)) {
        struct mpd_status *status = mpd_run_status(client);
        int volume = mpd_status_get_volume(status);

        volume += 5;
        if (volume > 100) {
          volume = 100;
        }

        mpd_run_set_volume(client, volume);
        printf("set-volume %d\n", volume);

      } else if (libevdev_event_is_code(&ev, EV_KEY, KEY_VOLUMEDOWN)) {
        struct mpd_status *status = mpd_run_status(client);
        int volume = mpd_status_get_volume(status);

        volume -= 5;
        if (volume < 0) {
          volume = 0;
        }

        mpd_run_set_volume(client, volume);
        printf("set-volume %d\n", volume);
      } else {
        printf("Event: %s %s %d\n",
               libevdev_event_type_get_name(ev.type),
               libevdev_event_code_get_name(ev.type, ev.code),
               ev.value);
      }
    }

  }
  while (rc == LIBEVDEV_READ_STATUS_SUCCESS ||
         rc == LIBEVDEV_READ_STATUS_SYNC ||
         rc == -EAGAIN);

  printf("rc: %d\n", rc);

  if (client != NULL) {
    mpd_connection_free(client);
  }
  return EXIT_SUCCESS;
}
コード例 #15
0
ファイル: minitouch.c プロジェクト: openstf/minitouch
static int consider_device(const char* devpath, internal_state_t* state)
{
  int fd = -1;
  struct libevdev* evdev = NULL;

  if (!is_character_device(devpath))
  {
    goto mismatch;
  }

  if ((fd = open(devpath, O_RDWR)) < 0)
  {
    perror("open");
    fprintf(stderr, "Unable to open device %s for inspection", devpath);
    goto mismatch;
  }

  if (libevdev_new_from_fd(fd, &evdev) < 0)
  {
    fprintf(stderr, "Note: device %s is not supported by libevdev\n", devpath);
    goto mismatch;
  }

  if (!is_multitouch_device(evdev))
  {
    goto mismatch;
  }

  int score = 10000;

  if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_TOOL_TYPE))
  {
    int tool_min = libevdev_get_abs_minimum(evdev, ABS_MT_TOOL_TYPE);
    int tool_max = libevdev_get_abs_maximum(evdev, ABS_MT_TOOL_TYPE);

    if (tool_min > MT_TOOL_FINGER || tool_max < MT_TOOL_FINGER)
    {
      fprintf(stderr, "Note: device %s is a touch device, but doesn't"
        " support fingers\n", devpath);
      goto mismatch;
    }

    score -= tool_max - MT_TOOL_FINGER;
  }

  if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT))
  {
    score += 1000;

    // Some devices, e.g. Blackberry PRIV (STV100) have more than one surface
    // you can touch. On the PRIV, the keypad also acts as a touch screen
    // that you can swipe and scroll with. The only differences between the
    // touch devices are that one is named "touch_display" and the other
    // "touch_keypad", the keypad only supports 3 contacts and the display
    // up to 9, and the keypad has a much lower resolution. Therefore
    // increasing the score by the number of contacts should be a relatively
    // safe bet, though we may also want to decrease the score by, say, 1,
    // if the device name contains "key" just in case they decide to start
    // supporting more contacts on both touch surfaces in the future.
    int num_slots = libevdev_get_abs_maximum(evdev, ABS_MT_SLOT);
    score += num_slots;
  }

  // For Blackberry devices, see above.
  const char* name = libevdev_get_name(evdev);
  if (strstr(name, "key") != NULL)
  {
    score -= 1;
  }

  // Alcatel OneTouch Idol 3 has an `input_mt_wrapper` device in addition
  // to direct input. It seems to be related to accessibility, as it shows
  // a touchpoint that you can move around, and then tap to activate whatever
  // is under the point. That wrapper device lacks the direct property.
  if (libevdev_has_property(evdev, INPUT_PROP_DIRECT))
  {
    score += 10000;
  }

  // Some devices may have an additional screen. For example, Meizu Pro7 Plus
  // has a small screen on the back side of the device called sub_touch, while
  // the boring screen in the front is called main_touch. The resolution on
  // the sub_touch device is much much lower. It seems like a safe bet
  // to always prefer the larger device, as long as the score adjustment is
  // likely to be lower than the adjustment we do for INPUT_PROP_DIRECT.
  if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X))
  {
    int x = libevdev_get_abs_maximum(evdev, ABS_MT_POSITION_X);
    int y = libevdev_get_abs_maximum(evdev, ABS_MT_POSITION_Y);
    score += sqrt(x * y);
  }

  if (state->evdev != NULL)
  {
    if (state->score >= score)
    {
      fprintf(stderr, "Note: device %s was outscored by %s (%d >= %d)\n",
        devpath, state->path, state->score, score);
      goto mismatch;
    }
    else
    {
      fprintf(stderr, "Note: device %s was outscored by %s (%d >= %d)\n",
        state->path, devpath, score, state->score);
    }
  }

  libevdev_free(state->evdev);

  state->fd = fd;
  state->score = score;
  strncpy(state->path, devpath, sizeof(state->path));
  state->evdev = evdev;

  return 1;

mismatch:
  libevdev_free(evdev);

  if (fd >= 0)
  {
    close(fd);
  }

  return 0;
}