X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fevdev.c;h=b09bb989f4187a0ebaa48e38093f1417cb1c9cc9;hb=bd8e3086939230a3501a6ee6ea3b2cdb1b2203a2;hp=22d969e80538c41b630febc07c145544e8ad0968;hpb=1dad790a7f84f019eb829bb7b91154a8e66b617c;p=platform%2Fupstream%2Flibinput.git diff --git a/src/evdev.c b/src/evdev.c index 22d969e..b09bb98 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -26,17 +26,96 @@ #include #include #include -#include +#include "linux/input.h" #include #include -#include +#include #include +#include +#include #include "libinput.h" #include "evdev.h" +#include "filter.h" #include "libinput-private.h" -#define DEFAULT_AXIS_STEP_DISTANCE li_fixed_from_int(10) +#define DEFAULT_AXIS_STEP_DISTANCE 10 + +enum evdev_key_type { + EVDEV_KEY_TYPE_NONE, + EVDEV_KEY_TYPE_KEY, + EVDEV_KEY_TYPE_BUTTON, +}; + +static void +set_key_down(struct evdev_device *device, int code, int pressed) +{ + long_set_bit_state(device->key_mask, code, pressed); +} + +static int +is_key_down(struct evdev_device *device, int code) +{ + return long_bit_is_set(device->key_mask, code); +} + +static int +get_key_down_count(struct evdev_device *device, int code) +{ + return device->key_count[code]; +} + +static int +update_key_down_count(struct evdev_device *device, int code, int pressed) +{ + int key_count; + assert(code >= 0 && code < KEY_CNT); + + if (pressed) { + key_count = ++device->key_count[code]; + } else { + assert(device->key_count[code] > 0); + key_count = --device->key_count[code]; + } + + if (key_count > 32) { + log_bug_libinput(device->base.seat->libinput, + "Key count for %s reached abnormal values\n", + libevdev_event_code_get_name(EV_KEY, code)); + } + + return key_count; +} + +void +evdev_keyboard_notify_key(struct evdev_device *device, + uint32_t time, + int key, + enum libinput_key_state state) +{ + int down_count; + + down_count = update_key_down_count(device, key, state); + + if ((state == LIBINPUT_KEY_STATE_PRESSED && down_count == 1) || + (state == LIBINPUT_KEY_STATE_RELEASED && down_count == 0)) + keyboard_notify_key(&device->base, time, key, state); +} + +void +evdev_pointer_notify_button(struct evdev_device *device, + uint32_t time, + int button, + enum libinput_button_state state) +{ + int down_count; + + down_count = update_key_down_count(device, button, state); + + if ((state == LIBINPUT_BUTTON_STATE_PRESSED && down_count == 1) || + (state == LIBINPUT_BUTTON_STATE_RELEASED && down_count == 0)) + pointer_notify_button(&device->base, time, button, state); +} void evdev_device_led_update(struct evdev_device *device, enum libinput_led leds) @@ -52,7 +131,7 @@ evdev_device_led_update(struct evdev_device *device, enum libinput_led leds) struct input_event ev[ARRAY_LENGTH(map) + 1]; unsigned int i; - if (!device->caps & EVDEV_KEYBOARD) + if (!(device->seat_caps & EVDEV_DEVICE_KEYBOARD)) return; memset(ev, 0, sizeof(ev)); @@ -86,12 +165,40 @@ transform_absolute(struct evdev_device *device, int32_t *x, int32_t *y) } } +static inline double +scale_axis(const struct input_absinfo *absinfo, double val, double to_range) +{ + return (val - absinfo->minimum) * to_range / + (absinfo->maximum - absinfo->minimum + 1); +} + +double +evdev_device_transform_x(struct evdev_device *device, + double x, + uint32_t width) +{ + return scale_axis(device->abs.absinfo_x, x, width); +} + +double +evdev_device_transform_y(struct evdev_device *device, + double y, + uint32_t height) +{ + return scale_axis(device->abs.absinfo_y, y, height); +} + static void -evdev_flush_pending_event(struct evdev_device *device, uint32_t time) +evdev_flush_pending_event(struct evdev_device *device, uint64_t time) { + struct libinput *libinput = device->base.seat->libinput; + struct motion_params motion; int32_t cx, cy; + double x, y; int slot; + int seat_slot; struct libinput_device *base = &device->base; + struct libinput_seat *seat = base->seat; slot = device->mt.slot; @@ -99,76 +206,150 @@ evdev_flush_pending_event(struct evdev_device *device, uint32_t time) case EVDEV_NONE: return; case EVDEV_RELATIVE_MOTION: - pointer_notify_motion(base, - time, - device->rel.dx, - device->rel.dy); + motion.dx = device->rel.dx; + motion.dy = device->rel.dy; device->rel.dx = 0; device->rel.dy = 0; - goto handled; + + /* Apply pointer acceleration. */ + filter_dispatch(device->pointer.filter, &motion, device, time); + + if (motion.dx == 0.0 && motion.dy == 0.0) + break; + + pointer_notify_motion(base, time, motion.dx, motion.dy); + break; case EVDEV_ABSOLUTE_MT_DOWN: - touch_notify_touch(base, - time, - slot, - li_fixed_from_int(device->mt.slots[slot].x), - li_fixed_from_int(device->mt.slots[slot].y), - LIBINPUT_TOUCH_TYPE_DOWN); - goto handled; + if (!(device->seat_caps & EVDEV_DEVICE_TOUCH)) + break; + + if (device->mt.slots[slot].seat_slot != -1) { + log_bug_kernel(libinput, + "%s: Driver sent multiple touch down for the " + "same slot", device->devnode); + break; + } + + seat_slot = ffs(~seat->slot_map) - 1; + device->mt.slots[slot].seat_slot = seat_slot; + + if (seat_slot == -1) + break; + + seat->slot_map |= 1 << seat_slot; + x = device->mt.slots[slot].x; + y = device->mt.slots[slot].y; + + touch_notify_touch_down(base, time, slot, seat_slot, x, y); + break; case EVDEV_ABSOLUTE_MT_MOTION: - touch_notify_touch(base, - time, - slot, - li_fixed_from_int(device->mt.slots[slot].x), - li_fixed_from_int(device->mt.slots[slot].y), - LIBINPUT_TOUCH_TYPE_MOTION); - goto handled; + if (!(device->seat_caps & EVDEV_DEVICE_TOUCH)) + break; + + seat_slot = device->mt.slots[slot].seat_slot; + x = device->mt.slots[slot].x; + y = device->mt.slots[slot].y; + + if (seat_slot == -1) + break; + + touch_notify_touch_motion(base, time, slot, seat_slot, x, y); + break; case EVDEV_ABSOLUTE_MT_UP: - touch_notify_touch(base, - time, - slot, - 0, 0, - LIBINPUT_TOUCH_TYPE_UP); - goto handled; + if (!(device->seat_caps & EVDEV_DEVICE_TOUCH)) + break; + + seat_slot = device->mt.slots[slot].seat_slot; + device->mt.slots[slot].seat_slot = -1; + + if (seat_slot == -1) + break; + + seat->slot_map &= ~(1 << seat_slot); + + touch_notify_touch_up(base, time, slot, seat_slot); + break; case EVDEV_ABSOLUTE_TOUCH_DOWN: + if (!(device->seat_caps & EVDEV_DEVICE_TOUCH)) + break; + + if (device->abs.seat_slot != -1) { + log_bug_kernel(libinput, + "%s: Driver sent multiple touch down for the " + "same slot", device->devnode); + break; + } + + seat_slot = ffs(~seat->slot_map) - 1; + device->abs.seat_slot = seat_slot; + + if (seat_slot == -1) + break; + + seat->slot_map |= 1 << seat_slot; + transform_absolute(device, &cx, &cy); - touch_notify_touch(base, - time, - slot, - li_fixed_from_int(cx), - li_fixed_from_int(cy), - LIBINPUT_TOUCH_TYPE_DOWN); - goto handled; + + touch_notify_touch_down(base, time, -1, seat_slot, cx, cy); + break; case EVDEV_ABSOLUTE_MOTION: transform_absolute(device, &cx, &cy); - if (device->caps & EVDEV_TOUCH) { - touch_notify_touch(base, - time, - slot, - li_fixed_from_int(cx), - li_fixed_from_int(cy), - LIBINPUT_TOUCH_TYPE_DOWN); - } else { - pointer_notify_motion_absolute(base, - time, - li_fixed_from_int(cx), - li_fixed_from_int(cy)); + x = cx; + y = cy; + + if (device->seat_caps & EVDEV_DEVICE_TOUCH) { + seat_slot = device->abs.seat_slot; + + if (seat_slot == -1) + break; + + touch_notify_touch_motion(base, time, -1, seat_slot, x, y); + } else if (device->seat_caps & EVDEV_DEVICE_POINTER) { + pointer_notify_motion_absolute(base, time, x, y); } - goto handled; + break; case EVDEV_ABSOLUTE_TOUCH_UP: - touch_notify_touch(base, - time, - 0, 0, 0, LIBINPUT_TOUCH_TYPE_UP); - goto handled; - } + if (!(device->seat_caps & EVDEV_DEVICE_TOUCH)) + break; + + seat_slot = device->abs.seat_slot; + device->abs.seat_slot = -1; + + if (seat_slot == -1) + break; - assert(0 && "Unknown pending event type"); + seat->slot_map &= ~(1 << seat_slot); + + touch_notify_touch_up(base, time, -1, seat_slot); + break; + default: + assert(0 && "Unknown pending event type"); + break; + } -handled: device->pending_event = EVDEV_NONE; } +static enum evdev_key_type +get_key_type(uint16_t code) +{ + if (code == BTN_TOUCH) + return EVDEV_KEY_TYPE_NONE; + + if (code >= KEY_ESC && code <= KEY_MICMUTE) + return EVDEV_KEY_TYPE_KEY; + if (code >= BTN_MISC && code <= BTN_GEAR_UP) + return EVDEV_KEY_TYPE_BUTTON; + if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE) + return EVDEV_KEY_TYPE_KEY; + if (code >= BTN_DPAD_UP && code <= BTN_TRIGGER_HAPPY40) + return EVDEV_KEY_TYPE_BUTTON; + return EVDEV_KEY_TYPE_NONE; +} + static void -evdev_process_touch_button(struct evdev_device *device, int time, int value) +evdev_process_touch_button(struct evdev_device *device, + uint64_t time, int value) { if (device->pending_event != EVDEV_NONE && device->pending_event != EVDEV_ABSOLUTE_MOTION) @@ -180,8 +361,11 @@ evdev_process_touch_button(struct evdev_device *device, int time, int value) } static inline void -evdev_process_key(struct evdev_device *device, struct input_event *e, int time) +evdev_process_key(struct evdev_device *device, + struct input_event *e, uint64_t time) { + enum evdev_key_type type; + /* ignore kernel key repeat */ if (e->value == 2) return; @@ -194,30 +378,41 @@ evdev_process_key(struct evdev_device *device, struct input_event *e, int time) evdev_flush_pending_event(device, time); - switch (e->code) { - case BTN_LEFT: - case BTN_RIGHT: - case BTN_MIDDLE: - case BTN_SIDE: - case BTN_EXTRA: - case BTN_FORWARD: - case BTN_BACK: - case BTN_TASK: - pointer_notify_button( - &device->base, + type = get_key_type(e->code); + + /* Ignore key release events from the kernel for keys that libinput + * never got a pressed event for. */ + if (e->value == 0) { + switch (type) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + case EVDEV_KEY_TYPE_BUTTON: + if (!is_key_down(device, e->code)) + return; + } + } + + set_key_down(device, e->code, e->value); + + switch (type) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + evdev_keyboard_notify_key( + device, time, e->code, - e->value ? LIBINPUT_POINTER_BUTTON_STATE_PRESSED : - LIBINPUT_POINTER_BUTTON_STATE_RELEASED); + e->value ? LIBINPUT_KEY_STATE_PRESSED : + LIBINPUT_KEY_STATE_RELEASED); break; - - default: - keyboard_notify_key( - &device->base, + case EVDEV_KEY_TYPE_BUTTON: + evdev_pointer_notify_button( + device, time, e->code, - e->value ? LIBINPUT_KEYBOARD_KEY_STATE_PRESSED : - LIBINPUT_KEYBOARD_KEY_STATE_RELEASED); + e->value ? LIBINPUT_BUTTON_STATE_PRESSED : + LIBINPUT_BUTTON_STATE_RELEASED); break; } } @@ -225,18 +420,8 @@ evdev_process_key(struct evdev_device *device, struct input_event *e, int time) static void evdev_process_touch(struct evdev_device *device, struct input_event *e, - uint32_t time) + uint64_t time) { - struct libinput *libinput = device->base.seat->libinput; - int screen_width; - int screen_height; - - libinput->interface->get_current_screen_dimensions( - &device->base, - &screen_width, - &screen_height, - libinput->user_data); - switch (e->code) { case ABS_MT_SLOT: evdev_flush_pending_event(device, time); @@ -252,16 +437,12 @@ evdev_process_touch(struct evdev_device *device, device->pending_event = EVDEV_ABSOLUTE_MT_UP; break; case ABS_MT_POSITION_X: - device->mt.slots[device->mt.slot].x = - (e->value - device->abs.min_x) * screen_width / - (device->abs.max_x - device->abs.min_x); + device->mt.slots[device->mt.slot].x = e->value; if (device->pending_event == EVDEV_NONE) device->pending_event = EVDEV_ABSOLUTE_MT_MOTION; break; case ABS_MT_POSITION_Y: - device->mt.slots[device->mt.slot].y = - (e->value - device->abs.min_y) * screen_height / - (device->abs.max_y - device->abs.min_y); + device->mt.slots[device->mt.slot].y = e->value; if (device->pending_event == EVDEV_NONE) device->pending_event = EVDEV_ABSOLUTE_MT_MOTION; break; @@ -272,28 +453,14 @@ static inline void evdev_process_absolute_motion(struct evdev_device *device, struct input_event *e) { - struct libinput *libinput = device->base.seat->libinput; - int screen_width; - int screen_height; - - libinput->interface->get_current_screen_dimensions( - &device->base, - &screen_width, - &screen_height, - libinput->user_data); - switch (e->code) { case ABS_X: - device->abs.x = - (e->value - device->abs.min_x) * screen_width / - (device->abs.max_x - device->abs.min_x); + device->abs.x = e->value; if (device->pending_event == EVDEV_NONE) device->pending_event = EVDEV_ABSOLUTE_MOTION; break; case ABS_Y: - device->abs.y = - (e->value - device->abs.min_y) * screen_height / - (device->abs.max_y - device->abs.min_y); + device->abs.y = e->value; if (device->pending_event == EVDEV_NONE) device->pending_event = EVDEV_ABSOLUTE_MOTION; break; @@ -302,7 +469,7 @@ evdev_process_absolute_motion(struct evdev_device *device, static inline void evdev_process_relative(struct evdev_device *device, - struct input_event *e, uint32_t time) + struct input_event *e, uint64_t time) { struct libinput_device *base = &device->base; @@ -310,31 +477,22 @@ evdev_process_relative(struct evdev_device *device, case REL_X: if (device->pending_event != EVDEV_RELATIVE_MOTION) evdev_flush_pending_event(device, time); - device->rel.dx += li_fixed_from_int(e->value); + device->rel.dx += e->value; device->pending_event = EVDEV_RELATIVE_MOTION; break; case REL_Y: if (device->pending_event != EVDEV_RELATIVE_MOTION) evdev_flush_pending_event(device, time); - device->rel.dy += li_fixed_from_int(e->value); + device->rel.dy += e->value; device->pending_event = EVDEV_RELATIVE_MOTION; break; case REL_WHEEL: evdev_flush_pending_event(device, time); - switch (e->value) { - case -1: - /* Scroll down */ - case 1: - /* Scroll up */ - pointer_notify_axis( - base, - time, - LIBINPUT_POINTER_AXIS_VERTICAL_SCROLL, - -1 * e->value * DEFAULT_AXIS_STEP_DISTANCE); - break; - default: - break; - } + pointer_notify_axis( + base, + time, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, + -1 * e->value * DEFAULT_AXIS_STEP_DISTANCE); break; case REL_HWHEEL: evdev_flush_pending_event(device, time); @@ -346,7 +504,7 @@ evdev_process_relative(struct evdev_device *device, pointer_notify_axis( base, time, - LIBINPUT_POINTER_AXIS_HORIZONTAL_SCROLL, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, e->value * DEFAULT_AXIS_STEP_DISTANCE); break; default: @@ -359,7 +517,7 @@ evdev_process_relative(struct evdev_device *device, static inline void evdev_process_absolute(struct evdev_device *device, struct input_event *e, - uint32_t time) + uint64_t time) { if (device->is_mt) { evdev_process_touch(device, e, time); @@ -368,12 +526,36 @@ evdev_process_absolute(struct evdev_device *device, } } +static inline int +evdev_need_touch_frame(struct evdev_device *device) +{ + if (!(device->seat_caps & EVDEV_DEVICE_TOUCH)) + return 0; + + switch (device->pending_event) { + case EVDEV_NONE: + case EVDEV_RELATIVE_MOTION: + break; + case EVDEV_ABSOLUTE_MT_DOWN: + case EVDEV_ABSOLUTE_MT_MOTION: + case EVDEV_ABSOLUTE_MT_UP: + case EVDEV_ABSOLUTE_TOUCH_DOWN: + case EVDEV_ABSOLUTE_TOUCH_UP: + case EVDEV_ABSOLUTE_MOTION: + return 1; + } + + return 0; +} + static void fallback_process(struct evdev_dispatch *dispatch, struct evdev_device *device, struct input_event *event, - uint32_t time) + uint64_t time) { + int need_frame = 0; + switch (event->type) { case EV_REL: evdev_process_relative(device, event, time); @@ -385,7 +567,10 @@ fallback_process(struct evdev_dispatch *dispatch, evdev_process_key(device, event, time); break; case EV_SYN: + need_frame = evdev_need_touch_frame(device); evdev_flush_pending_event(device, time); + if (need_frame) + touch_notify_frame(&device->base, time); break; } } @@ -413,246 +598,322 @@ fallback_dispatch_create(void) return dispatch; } -static void -evdev_process_events(struct evdev_device *device, - struct input_event *ev, int count) +static inline void +evdev_process_event(struct evdev_device *device, struct input_event *e) { struct evdev_dispatch *dispatch = device->dispatch; - struct input_event *e, *end; - uint32_t time = 0; + uint64_t time = e->time.tv_sec * 1000ULL + e->time.tv_usec / 1000; - e = ev; - end = e + count; - for (e = ev; e < end; e++) { - time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000; + dispatch->interface->process(dispatch, device, e, time); +} - dispatch->interface->process(dispatch, device, e, time); +static inline void +evdev_device_dispatch_one(struct evdev_device *device, + struct input_event *ev) +{ + if (!device->mtdev) { + evdev_process_event(device, ev); + } else { + mtdev_put_event(device->mtdev, ev); + if (libevdev_event_is_code(ev, EV_SYN, SYN_REPORT)) { + while (!mtdev_empty(device->mtdev)) { + struct input_event e; + mtdev_get_event(device->mtdev, &e); + evdev_process_event(device, &e); + } + } } } +static int +evdev_sync_device(struct evdev_device *device) +{ + struct input_event ev; + int rc; + + do { + rc = libevdev_next_event(device->evdev, + LIBEVDEV_READ_FLAG_SYNC, &ev); + if (rc < 0) + break; + evdev_device_dispatch_one(device, &ev); + } while (rc == LIBEVDEV_READ_STATUS_SYNC); + + return rc == -EAGAIN ? 0 : rc; +} + static void evdev_device_dispatch(void *data) { struct evdev_device *device = data; struct libinput *libinput = device->base.seat->libinput; - int fd = device->fd; - struct input_event ev[32]; - int len; + struct input_event ev; + int rc; /* If the compositor is repainting, this function is called only once * per frame and we have to process all the events available on the * fd, otherwise there will be input lag. */ do { - if (device->mtdev) - len = mtdev_get(device->mtdev, fd, ev, - ARRAY_LENGTH(ev)) * - sizeof (struct input_event); - else - len = read(fd, &ev, sizeof ev); - - if (len < 0 || len % sizeof ev[0] != 0) { - if (len < 0 && errno != EAGAIN && errno != EINTR) { - libinput_remove_source(libinput, - device->source); - device->source = NULL; - } - - return; + rc = libevdev_next_event(device->evdev, + LIBEVDEV_READ_FLAG_NORMAL, &ev); + if (rc == LIBEVDEV_READ_STATUS_SYNC) { + /* send one more sync event so we handle all + currently pending events before we sync up + to the current state */ + ev.code = SYN_REPORT; + evdev_device_dispatch_one(device, &ev); + + rc = evdev_sync_device(device); + if (rc == 0) + rc = LIBEVDEV_READ_STATUS_SUCCESS; + } else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) { + evdev_device_dispatch_one(device, &ev); } + } while (rc == LIBEVDEV_READ_STATUS_SUCCESS); + + if (rc != -EAGAIN && rc != -EINTR) { + libinput_remove_source(libinput, device->source); + device->source = NULL; + } +} - evdev_process_events(device, ev, len / sizeof ev[0]); +static int +configure_pointer_acceleration(struct evdev_device *device) +{ + device->pointer.filter = + create_pointer_accelator_filter( + pointer_accel_profile_smooth_simple); + if (!device->pointer.filter) + return -1; - } while (len > 0); + return 0; } static int -evdev_handle_device(struct evdev_device *device) -{ - struct input_absinfo absinfo; - unsigned long ev_bits[NBITS(EV_MAX)]; - unsigned long abs_bits[NBITS(ABS_MAX)]; - unsigned long rel_bits[NBITS(REL_MAX)]; - unsigned long key_bits[NBITS(KEY_MAX)]; - int has_key, has_abs; +evdev_configure_device(struct evdev_device *device) +{ + struct libinput *libinput = device->base.seat->libinput; + struct libevdev *evdev = device->evdev; + const struct input_absinfo *absinfo; + struct input_absinfo fixed; + int has_abs, has_rel, has_mt; + int has_button, has_keyboard, has_touch; + struct mt_slot *slots; + int num_slots; + int active_slot; + int slot; unsigned int i; - has_key = 0; + has_rel = 0; has_abs = 0; - device->caps = 0; - - ioctl(device->fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits); - if (TEST_BIT(ev_bits, EV_ABS)) { - has_abs = 1; - - ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), - abs_bits); - - if (TEST_BIT(abs_bits, ABS_WHEEL) || - TEST_BIT(abs_bits, ABS_GAS) || - TEST_BIT(abs_bits, ABS_BRAKE) || - TEST_BIT(abs_bits, ABS_HAT0X)) { - /* Device %s is a joystick, ignoring. */ - return 0; - } - - if (TEST_BIT(abs_bits, ABS_X)) { - ioctl(device->fd, EVIOCGABS(ABS_X), &absinfo); - device->abs.min_x = absinfo.minimum; - device->abs.max_x = absinfo.maximum; - device->caps |= EVDEV_MOTION_ABS; + has_mt = 0; + has_button = 0; + has_keyboard = 0; + has_touch = 0; + + if (libevdev_has_event_type(evdev, EV_ABS)) { + + if ((absinfo = libevdev_get_abs_info(evdev, ABS_X))) { + if (absinfo->resolution == 0) { + fixed = *absinfo; + fixed.resolution = 1; + libevdev_set_abs_info(evdev, ABS_X, &fixed); + device->abs.fake_resolution = 1; + } + device->abs.absinfo_x = absinfo; + has_abs = 1; } - if (TEST_BIT(abs_bits, ABS_Y)) { - ioctl(device->fd, EVIOCGABS(ABS_Y), &absinfo); - device->abs.min_y = absinfo.minimum; - device->abs.max_y = absinfo.maximum; - device->caps |= EVDEV_MOTION_ABS; + if ((absinfo = libevdev_get_abs_info(evdev, ABS_Y))) { + if (absinfo->resolution == 0) { + fixed = *absinfo; + fixed.resolution = 1; + libevdev_set_abs_info(evdev, ABS_Y, &fixed); + device->abs.fake_resolution = 1; + } + device->abs.absinfo_y = absinfo; + has_abs = 1; } /* We only handle the slotted Protocol B in weston. Devices with ABS_MT_POSITION_* but not ABS_MT_SLOT require mtdev for conversion. */ - if (TEST_BIT(abs_bits, ABS_MT_POSITION_X) && - TEST_BIT(abs_bits, ABS_MT_POSITION_Y)) { - ioctl(device->fd, EVIOCGABS(ABS_MT_POSITION_X), - &absinfo); - device->abs.min_x = absinfo.minimum; - device->abs.max_x = absinfo.maximum; - ioctl(device->fd, EVIOCGABS(ABS_MT_POSITION_Y), - &absinfo); - device->abs.min_y = absinfo.minimum; - device->abs.max_y = absinfo.maximum; + if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) && + libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y)) { + absinfo = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X); + if (absinfo->resolution == 0) { + fixed = *absinfo; + fixed.resolution = 1; + libevdev_set_abs_info(evdev, + ABS_MT_POSITION_X, + &fixed); + device->abs.fake_resolution = 1; + } + device->abs.absinfo_x = absinfo; + absinfo = libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y); + if (absinfo->resolution == 0) { + fixed = *absinfo; + fixed.resolution = 1; + libevdev_set_abs_info(evdev, + ABS_MT_POSITION_Y, + &fixed); + device->abs.fake_resolution = 1; + } + device->abs.absinfo_y = absinfo; device->is_mt = 1; - device->caps |= EVDEV_TOUCH; + has_touch = 1; + has_mt = 1; - if (!TEST_BIT(abs_bits, ABS_MT_SLOT)) { + if (!libevdev_has_event_code(evdev, + EV_ABS, ABS_MT_SLOT)) { device->mtdev = mtdev_new_open(device->fd); - if (!device->mtdev) { - /* mtdev required but failed to open. */ - return 0; - } - device->mt.slot = device->mtdev->caps.slot.value; + if (!device->mtdev) + return -1; + + num_slots = device->mtdev->caps.slot.maximum; + if (device->mtdev->caps.slot.minimum < 0 || + num_slots <= 0) + return -1; + active_slot = device->mtdev->caps.slot.value; } else { - ioctl(device->fd, EVIOCGABS(ABS_MT_SLOT), - &absinfo); - device->mt.slot = absinfo.value; + num_slots = libevdev_get_num_slots(device->evdev); + active_slot = libevdev_get_current_slot(evdev); } - } - } - if (TEST_BIT(ev_bits, EV_REL)) { - ioctl(device->fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), - rel_bits); - if (TEST_BIT(rel_bits, REL_X) || TEST_BIT(rel_bits, REL_Y)) - device->caps |= EVDEV_MOTION_REL; - } - if (TEST_BIT(ev_bits, EV_KEY)) { - has_key = 1; - ioctl(device->fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), - key_bits); - if (TEST_BIT(key_bits, BTN_TOOL_FINGER) && - !TEST_BIT(key_bits, BTN_TOOL_PEN) && - has_abs) { - device->dispatch = evdev_touchpad_create(device); - } - for (i = KEY_ESC; i < KEY_MAX; i++) { - if (i >= BTN_MISC && i < KEY_OK) - continue; - if (TEST_BIT(key_bits, i)) { - device->caps |= EVDEV_KEYBOARD; - break; + + slots = calloc(num_slots, sizeof(struct mt_slot)); + if (!slots) + return -1; + + for (slot = 0; slot < num_slots; ++slot) { + slots[slot].seat_slot = -1; + slots[slot].x = 0; + slots[slot].y = 0; } + device->mt.slots = slots; + device->mt.slots_len = num_slots; + device->mt.slot = active_slot; } - if (TEST_BIT(key_bits, BTN_TOUCH)) { - device->caps |= EVDEV_TOUCH; + } + if (libevdev_has_event_code(evdev, EV_REL, REL_X) || + libevdev_has_event_code(evdev, EV_REL, REL_Y)) + has_rel = 1; + + if (libevdev_has_event_type(evdev, EV_KEY)) { + if (!libevdev_has_property(evdev, INPUT_PROP_DIRECT) && + libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_FINGER) && + !libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN) && + (has_abs || has_mt)) { + device->dispatch = evdev_mt_touchpad_create(device); + log_info(libinput, + "input device '%s', %s is a touchpad\n", + device->devname, device->devnode); + return device->dispatch == NULL ? -1 : 0; } - for (i = BTN_MISC; i < BTN_JOYSTICK; i++) { - if (TEST_BIT(key_bits, i)) { - device->caps |= EVDEV_BUTTON; - device->caps &= ~EVDEV_TOUCH; - break; + + for (i = 0; i < KEY_MAX; i++) { + if (libevdev_has_event_code(evdev, EV_KEY, i)) { + switch (get_key_type(i)) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + has_keyboard = 1; + break; + case EVDEV_KEY_TYPE_BUTTON: + has_button = 1; + break; + } } } - } - if (TEST_BIT(ev_bits, EV_LED)) { - device->caps |= EVDEV_KEYBOARD; - } - /* This rule tries to catch accelerometer devices and opt out. We may - * want to adjust the protocol later adding a proper event for dealing - * with accelerometers and implement here accordingly */ - if (has_abs && !has_key && !device->is_mt) { - return 0; + if (libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH)) + has_touch = 1; } + if (libevdev_has_event_type(evdev, EV_LED)) + has_keyboard = 1; - return 1; -} + if ((has_abs || has_rel) && has_button) { + if (configure_pointer_acceleration(device) == -1) + return -1; -static void -evdev_configure_device(struct evdev_device *device) -{ - if ((device->caps & (EVDEV_MOTION_ABS | EVDEV_MOTION_REL)) && - (device->caps & EVDEV_BUTTON)) device->seat_caps |= EVDEV_DEVICE_POINTER; - if ((device->caps & EVDEV_KEYBOARD)) - device->seat_caps |= EVDEV_DEVICE_KEYBOARD; - if ((device->caps & EVDEV_TOUCH)) - device->seat_caps |= EVDEV_DEVICE_TOUCH; -} -static void -register_device_capabilities(struct evdev_device *device) -{ - if (device->seat_caps & EVDEV_DEVICE_POINTER) { - device_register_capability(&device->base, - LIBINPUT_DEVICE_CAP_POINTER); + log_info(libinput, + "input device '%s', %s is a pointer caps =%s%s%s\n", + device->devname, device->devnode, + has_abs ? " absolute-motion" : "", + has_rel ? " relative-motion": "", + has_button ? " button" : ""); } - if (device->seat_caps & EVDEV_DEVICE_KEYBOARD) { - device_register_capability(&device->base, - LIBINPUT_DEVICE_CAP_KEYBOARD); + if (has_keyboard) { + device->seat_caps |= EVDEV_DEVICE_KEYBOARD; + log_info(libinput, + "input device '%s', %s is a keyboard\n", + device->devname, device->devnode); } - if (device->seat_caps & EVDEV_DEVICE_TOUCH) { - device_register_capability(&device->base, - LIBINPUT_DEVICE_CAP_TOUCH); + if (has_touch && !has_button) { + device->seat_caps |= EVDEV_DEVICE_TOUCH; + log_info(libinput, + "input device '%s', %s is a touch device\n", + device->devname, device->devnode); } + + return 0; } struct evdev_device * evdev_device_create(struct libinput_seat *seat, const char *devnode, - const char *sysname, - int fd) + const char *sysname) { struct libinput *libinput = seat->libinput; struct evdev_device *device; - char devname[256] = "unknown"; + int rc; + int fd; + int unhandled_device = 0; + + /* Use non-blocking mode so that we can loop on read on + * evdev_device_data() until all events on the fd are + * read. mtdev_get() also expects this. */ + fd = open_restricted(libinput, devnode, O_RDWR | O_NONBLOCK); + if (fd < 0) { + log_info(libinput, + "opening input device '%s' failed (%s).\n", + devnode, strerror(-fd)); + return NULL; + } device = zalloc(sizeof *device); if (device == NULL) return NULL; libinput_device_init(&device->base, seat); + libinput_seat_ref(seat); + + rc = libevdev_new_from_fd(fd, &device->evdev); + if (rc != 0) + goto err; + + libevdev_set_clock_id(device->evdev, CLOCK_MONOTONIC); device->seat_caps = 0; device->is_mt = 0; device->mtdev = NULL; device->devnode = strdup(devnode); device->sysname = strdup(sysname); - device->mt.slot = -1; device->rel.dx = 0; device->rel.dy = 0; + device->abs.seat_slot = -1; device->dispatch = NULL; device->fd = fd; device->pending_event = EVDEV_NONE; + device->devname = libevdev_get_name(device->evdev); - ioctl(device->fd, EVIOCGNAME(sizeof(devname)), devname); - devname[sizeof(devname) - 1] = '\0'; - device->devname = strdup(devname); + if (evdev_configure_device(device) == -1) + goto err; - if (!evdev_handle_device(device)) { - evdev_device_destroy(device); - return NULL; + if (device->seat_caps == 0) { + unhandled_device = 1; + goto err; } - evdev_configure_device(device); - /* If the dispatch was not set up use the fallback. */ if (device->dispatch == NULL) device->dispatch = fallback_dispatch_create(); @@ -666,20 +927,22 @@ evdev_device_create(struct libinput_seat *seat, list_insert(seat->devices_list.prev, &device->base.link); notify_added_device(&device->base); - register_device_capabilities(device); return device; err: + if (fd >= 0) + close_restricted(libinput, fd); evdev_device_destroy(device); - return NULL; + + return unhandled_device ? EVDEV_UNHANDLED_DEVICE : NULL; } int evdev_device_get_keys(struct evdev_device *device, char *keys, size_t size) { memset(keys, 0, size); - return ioctl(device->fd, EVIOCGKEY(size), keys); + return 0; } const char * @@ -694,6 +957,24 @@ evdev_device_get_sysname(struct evdev_device *device) return device->sysname; } +const char * +evdev_device_get_name(struct evdev_device *device) +{ + return device->devname; +} + +unsigned int +evdev_device_get_id_product(struct evdev_device *device) +{ + return libevdev_get_id_product(device->evdev); +} + +unsigned int +evdev_device_get_id_vendor(struct evdev_device *device) +{ + return libevdev_get_id_vendor(device->evdev); +} + void evdev_device_calibrate(struct evdev_device *device, float calibration[6]) { @@ -717,29 +998,78 @@ evdev_device_has_capability(struct evdev_device *device, } } -void -evdev_device_remove(struct evdev_device *device) +int +evdev_device_get_size(struct evdev_device *device, + double *width, + double *height) { - if (device->seat_caps & EVDEV_DEVICE_POINTER) { - device_unregister_capability(&device->base, - LIBINPUT_DEVICE_CAP_POINTER); - } - if (device->seat_caps & EVDEV_DEVICE_KEYBOARD) { - device_unregister_capability(&device->base, - LIBINPUT_DEVICE_CAP_KEYBOARD); + const struct input_absinfo *x, *y; + + x = libevdev_get_abs_info(device->evdev, ABS_X); + y = libevdev_get_abs_info(device->evdev, ABS_Y); + + if (!x || !y || device->abs.fake_resolution || + !x->resolution || !y->resolution) + return -1; + + *width = evdev_convert_to_mm(x, x->maximum); + *height = evdev_convert_to_mm(y, y->maximum); + + return 0; +} + +static void +release_pressed_keys(struct evdev_device *device) +{ + struct libinput *libinput = device->base.seat->libinput; + struct timespec ts; + uint64_t time; + int code; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + log_bug_libinput(libinput, "clock_gettime: %s\n", strerror(errno)); + return; } - if (device->seat_caps & EVDEV_DEVICE_TOUCH) { - device_unregister_capability(&device->base, - LIBINPUT_DEVICE_CAP_TOUCH); + + time = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000; + + for (code = 0; code < KEY_CNT; code++) { + if (get_key_down_count(device, code) > 0) { + switch (get_key_type(code)) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + keyboard_notify_key( + &device->base, + time, + code, + LIBINPUT_KEY_STATE_RELEASED); + break; + case EVDEV_KEY_TYPE_BUTTON: + pointer_notify_button( + &device->base, + time, + code, + LIBINPUT_BUTTON_STATE_RELEASED); + break; + } + } } +} +void +evdev_device_remove(struct evdev_device *device) +{ if (device->source) libinput_remove_source(device->base.seat->libinput, device->source); + release_pressed_keys(device); + if (device->mtdev) mtdev_close_delete(device->mtdev); - close(device->fd); + close_restricted(device->base.seat->libinput, device->fd); + device->fd = -1; list_remove(&device->base.link); notify_removed_device(&device->base); @@ -755,7 +1085,10 @@ evdev_device_destroy(struct evdev_device *device) if (dispatch) dispatch->interface->destroy(dispatch); - free(device->devname); + filter_destroy(device->pointer.filter); + libinput_seat_unref(device->base.seat); + libevdev_free(device->evdev); + free(device->mt.slots); free(device->devnode); free(device->sysname); free(device);