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); } } }
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); }
int tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device) { struct libinput *libinput = tp_libinput_context(tp); struct tp_touch *t; const struct input_absinfo *absinfo_x, *absinfo_y; tp->buttons.is_clickpad = libevdev_has_property(device->evdev, INPUT_PROP_BUTTONPAD); tp->buttons.has_topbuttons = libevdev_has_property(device->evdev, INPUT_PROP_TOPBUTTONPAD); if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE) || libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT)) { if (tp->buttons.is_clickpad) log_bug_kernel(libinput, "%s: clickpad advertising right button\n", device->devname); } else if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_LEFT) && !tp->buttons.is_clickpad && libevdev_get_id_vendor(device->evdev) != VENDOR_ID_APPLE) { log_bug_kernel(libinput, "%s: non clickpad without right button?\n", device->devname); } absinfo_x = device->abs.absinfo_x; absinfo_y = device->abs.absinfo_y; /* pinned-finger motion threshold, see tp_unpin_finger. */ tp->buttons.motion_dist.x_scale_coeff = 1.0/absinfo_x->resolution; tp->buttons.motion_dist.y_scale_coeff = 1.0/absinfo_y->resolution; tp->buttons.config_method.get_methods = tp_button_config_click_get_methods; tp->buttons.config_method.set_method = tp_button_config_click_set_method; tp->buttons.config_method.get_method = tp_button_config_click_get_method; tp->buttons.config_method.get_default_method = tp_button_config_click_get_default_method; tp->device->base.config.click_method = &tp->buttons.config_method; tp->buttons.click_method = tp_click_get_default_method(tp); tp_switch_click_method(tp); tp_init_top_softbuttons(tp, device, 1.0); tp_init_middlebutton_emulation(tp, device); tp_for_each_touch(tp, t) { t->button.state = BUTTON_STATE_NONE; libinput_timer_init(&t->button.timer, tp_libinput_context(tp), tp_button_handle_timeout, t); }
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; }
static inline void tp_init_middlebutton_emulation(struct tp_dispatch *tp, struct evdev_device *device) { bool enable_by_default, want_config_option; if (tp->buttons.is_clickpad) return; /* init middle button emulation on non-clickpads, but only if we * don't have a middle button. Exception: ALPS touchpads don't know * if they have a middle button, so we always want the option there * and enabled by default. */ if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) { enable_by_default = true; want_config_option = false; } else if (device->model == EVDEV_MODEL_ALPS_TOUCHPAD) { enable_by_default = true; want_config_option = true; } else return; evdev_init_middlebutton(tp->device, enable_by_default, want_config_option); }
static void set_resolution(struct libevdev *dev, int xres, int yres) { struct input_absinfo abs; abs.resolution = xres; if (libevdev_has_event_code(dev, EV_ABS, ABS_X)) set_abs(dev, OPT_RES, ABS_X, &abs); if (libevdev_has_event_code(dev, EV_ABS, ABS_MT_POSITION_X)) set_abs(dev, OPT_RES, ABS_MT_POSITION_X, &abs); abs.resolution = yres; if (libevdev_has_event_code(dev, EV_ABS, ABS_Y)) set_abs(dev, OPT_RES, ABS_Y, &abs); if (libevdev_has_event_code(dev, EV_ABS, ABS_MT_POSITION_Y)) set_abs(dev, OPT_RES, ABS_MT_POSITION_Y, &abs); }
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; }
static void set_led(struct libevdev *dev, unsigned int led, int led_state) { int rc; enum libevdev_led_value state = led_state ? LIBEVDEV_LED_ON : LIBEVDEV_LED_OFF; if (!libevdev_has_event_code(dev, EV_LED, led)) { fprintf(stderr, "Device '%s' doesn't have %s\n", libevdev_get_name(dev), libevdev_event_code_get_name(EV_LED, led)); return; } rc = libevdev_kernel_set_led_value(dev, led, state); if (rc != 0) fprintf(stderr, "Failed to set LED %s: %s", libevdev_event_code_get_name(EV_LED, led), strerror(-rc)); }
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; }
static int is_multitouch_device(struct libevdev* evdev) { return libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X); }
int main(int argc, char* argv[]) { const char* pname = argv[0]; const char* devroot = "/dev/input"; char* device = NULL; char* sockname = DEFAULT_SOCKET_NAME; char* stdin_file = NULL; int use_stdin = 0; int opt; while ((opt = getopt(argc, argv, "d:n:vif:h")) != -1) { switch (opt) { case 'd': device = optarg; break; case 'n': sockname = optarg; break; case 'v': g_verbose = 1; break; case 'i': use_stdin = 1; break; case 'f': stdin_file = optarg; break; case '?': usage(pname); return EXIT_FAILURE; case 'h': usage(pname); return EXIT_SUCCESS; } } internal_state_t state = {0}; if (device != NULL) { if (!consider_device(device, &state)) { fprintf(stderr, "%s is not a supported touch device\n", device); return EXIT_FAILURE; } } else { if (walk_devices(devroot, &state) != 0) { fprintf(stderr, "Unable to crawl %s for touch devices\n", devroot); return EXIT_FAILURE; } } if (state.evdev == NULL) { fprintf(stderr, "Unable to find a suitable touch device\n"); return EXIT_FAILURE; } state.has_mtslot = libevdev_has_event_code(state.evdev, EV_ABS, ABS_MT_SLOT); state.has_tracking_id = libevdev_has_event_code(state.evdev, EV_ABS, ABS_MT_TRACKING_ID); state.has_key_btn_touch = libevdev_has_event_code(state.evdev, EV_KEY, BTN_TOUCH); state.has_touch_major = libevdev_has_event_code(state.evdev, EV_ABS, ABS_MT_TOUCH_MAJOR); state.has_width_major = libevdev_has_event_code(state.evdev, EV_ABS, ABS_MT_WIDTH_MAJOR); state.has_pressure = libevdev_has_event_code(state.evdev, EV_ABS, ABS_MT_PRESSURE); state.min_pressure = state.has_pressure ? libevdev_get_abs_minimum(state.evdev, ABS_MT_PRESSURE) : 0; state.max_pressure= state.has_pressure ? libevdev_get_abs_maximum(state.evdev, ABS_MT_PRESSURE) : 0; state.max_x = libevdev_get_abs_maximum(state.evdev, ABS_MT_POSITION_X); state.max_y = libevdev_get_abs_maximum(state.evdev, ABS_MT_POSITION_Y); state.max_tracking_id = state.has_tracking_id ? libevdev_get_abs_maximum(state.evdev, ABS_MT_TRACKING_ID) : INT_MAX; if (!state.has_mtslot && state.max_tracking_id == 0) { // The touch device reports incorrect values. There would be no point // in supporting ABS_MT_TRACKING_ID at all if the maximum value was 0 // (i.e. one contact). This happens on Lenovo Yoga Tablet B6000-F, // which actually seems to support ~10 contacts. So, we'll just go with // as many as we can and hope that the system will ignore extra contacts. state.max_tracking_id = MAX_SUPPORTED_CONTACTS - 1; fprintf(stderr, "Note: type A device reports a max value of 0 for ABS_MT_TRACKING_ID. " "This means that the device is most likely reporting incorrect " "information. Guessing %d.\n", state.max_tracking_id ); } state.max_contacts = state.has_mtslot ? libevdev_get_abs_maximum(state.evdev, ABS_MT_SLOT) + 1 : (state.has_tracking_id ? state.max_tracking_id + 1 : 2); state.tracking_id = 0; int contact; for (contact = 0; contact < MAX_SUPPORTED_CONTACTS; ++contact) { state.contacts[contact].enabled = 0; } fprintf(stderr, "%s touch device %s (%dx%d with %d contacts) detected on %s (score %d)\n", state.has_mtslot ? "Type B" : "Type A", libevdev_get_name(state.evdev), state.max_x, state.max_y, state.max_contacts, state.path, state.score ); if (state.max_contacts > MAX_SUPPORTED_CONTACTS) { fprintf(stderr, "Note: hard-limiting maximum number of contacts to %d\n", MAX_SUPPORTED_CONTACTS); state.max_contacts = MAX_SUPPORTED_CONTACTS; } FILE* input; FILE* output; if (use_stdin || stdin_file != NULL) { if (stdin_file != NULL) { // Reading from a file input = fopen(stdin_file, "r"); if (NULL == input) { fprintf(stderr, "Unable to open '%s': %s\n", stdin_file, strerror(errno)); exit(EXIT_FAILURE); } else { fprintf(stderr, "Reading commands from '%s'\n", stdin_file); } } else { // Reading from terminal input = stdin; fprintf(stderr, "Reading from STDIN\n"); } output = stderr; io_handler(input, output, &state); fclose(input); fclose(output); exit(EXIT_SUCCESS); } struct sockaddr_un client_addr; socklen_t client_addr_length = sizeof(client_addr); int server_fd = start_server(sockname); if (server_fd < 0) { fprintf(stderr, "Unable to start server on %s\n", sockname); return EXIT_FAILURE; } while (1) { int client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &client_addr_length); if (client_fd < 0) { perror("accepting client"); exit(1); } fprintf(stderr, "Connection established\n"); input = fdopen(client_fd, "r"); if (input == NULL) { fprintf(stderr, "%s: fdopen(client_fd,'r')\n", strerror(errno)); exit(1); } output = fdopen(dup(client_fd), "w"); if (output == NULL) { fprintf(stderr, "%s: fdopen(client_fd,'w')\n", strerror(errno)); exit(1); } io_handler(input, output, &state); fprintf(stderr, "Connection closed\n"); fclose(input); fclose(output); close(client_fd); } close(server_fd); libevdev_free(state.evdev); close(state.fd); return EXIT_SUCCESS; }