X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fevdev.c;h=122a2d9bead76088fef19de539afe3c2f7d5382d;hb=967911791f9988be63b8a2c1ffb608eb2871889b;hp=7efbc7da96c766453c62264c8878a64efd01d9a7;hpb=b2d021b95ec157b16d65a709d79d817106640778;p=platform%2Fupstream%2Flibinput.git diff --git a/src/evdev.c b/src/evdev.c index 7efbc7d..63eaa4d 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1,5 +1,6 @@ /* * Copyright © 2010 Intel Corporation + * Copyright © 2013 Jonas Ådahl * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -20,51 +21,268 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + +#include #include #include -#include +#include "linux/input.h" #include #include -#include +#include +#include +#include +#include -#include "compositor.h" +#include "libinput.h" #include "evdev.h" +#include "filter.h" +#include "libinput-private.h" + +#define DEFAULT_AXIS_STEP_DISTANCE 10 void -evdev_led_update(struct wl_list *evdev_devices, enum weston_led leds) +evdev_device_led_update(struct evdev_device *device, enum libinput_led leds) { static const struct { - enum weston_led weston; + enum libinput_led weston; int evdev; } map[] = { - { LED_NUM_LOCK, LED_NUML }, - { LED_CAPS_LOCK, LED_CAPSL }, - { LED_SCROLL_LOCK, LED_SCROLLL }, + { LIBINPUT_LED_NUM_LOCK, LED_NUML }, + { LIBINPUT_LED_CAPS_LOCK, LED_CAPSL }, + { LIBINPUT_LED_SCROLL_LOCK, LED_SCROLLL }, }; - struct evdev_input_device *device; - struct input_event ev[ARRAY_LENGTH(map)]; + struct input_event ev[ARRAY_LENGTH(map) + 1]; unsigned int i; + if (!(device->seat_caps & EVDEV_DEVICE_KEYBOARD)) + return; + memset(ev, 0, sizeof(ev)); for (i = 0; i < ARRAY_LENGTH(map); i++) { ev[i].type = EV_LED; ev[i].code = map[i].evdev; ev[i].value = !!(leds & map[i].weston); } + ev[i].type = EV_SYN; + ev[i].code = SYN_REPORT; + + i = write(device->fd, ev, sizeof ev); + (void)i; /* no, we really don't care about the return value */ +} + +static void +transform_absolute(struct evdev_device *device, int32_t *x, int32_t *y) +{ + if (!device->abs.apply_calibration) { + *x = device->abs.x; + *y = device->abs.y; + return; + } else { + *x = device->abs.x * device->abs.calibration[0] + + device->abs.y * device->abs.calibration[1] + + device->abs.calibration[2]; - wl_list_for_each(device, evdev_devices, link) { - if (device->caps & EVDEV_KEYBOARD) - write(device->fd, ev, sizeof ev); + *y = device->abs.x * device->abs.calibration[3] + + device->abs.y * device->abs.calibration[4] + + device->abs.calibration[5]; } } +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, uint64_t time) +{ + 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; + + switch (device->pending_event) { + case EVDEV_NONE: + return; + case EVDEV_RELATIVE_MOTION: + motion.dx = device->rel.dx; + motion.dy = device->rel.dy; + device->rel.dx = 0; + device->rel.dy = 0; + + /* 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: + if (!(device->seat_caps & EVDEV_DEVICE_TOUCH)) + break; + + if (device->mt.slots[slot].seat_slot != -1) { + log_bug_kernel("%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: + 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: + 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("%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_down(base, time, -1, seat_slot, cx, cy); + break; + case EVDEV_ABSOLUTE_MOTION: + transform_absolute(device, &cx, &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); + } + break; + case EVDEV_ABSOLUTE_TOUCH_UP: + if (!(device->seat_caps & EVDEV_DEVICE_TOUCH)) + break; + + seat_slot = device->abs.seat_slot; + device->abs.seat_slot = -1; + + if (seat_slot == -1) + break; + + seat->slot_map &= ~(1 << seat_slot); + + touch_notify_touch_up(base, time, -1, seat_slot); + break; + default: + assert(0 && "Unknown pending event type"); + break; + } + + device->pending_event = EVDEV_NONE; +} + +static void +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) + evdev_flush_pending_event(device, time); + + device->pending_event = (value ? + EVDEV_ABSOLUTE_TOUCH_DOWN : + EVDEV_ABSOLUTE_TOUCH_UP); +} + static inline void -evdev_process_key(struct evdev_input_device *device, - struct input_event *e, int time) +evdev_process_key(struct evdev_device *device, + struct input_event *e, uint64_t time) { + /* ignore kernel key repeat */ if (e->value == 2) return; + if (e->code > KEY_MAX) + return; + + if (e->code == BTN_TOUCH) { + if (!device->is_mt) + evdev_process_touch_button(device, time, e->value); + return; + } + + evdev_flush_pending_event(device, time); + switch (e->code) { case BTN_LEFT: case BTN_RIGHT: @@ -74,206 +292,185 @@ evdev_process_key(struct evdev_input_device *device, case BTN_FORWARD: case BTN_BACK: case BTN_TASK: - notify_button(&device->seat->seat, - time, e->code, - e->value ? WL_POINTER_BUTTON_STATE_PRESSED : - WL_POINTER_BUTTON_STATE_RELEASED); + pointer_notify_button( + &device->base, + time, + e->code, + e->value ? LIBINPUT_BUTTON_STATE_PRESSED : + LIBINPUT_BUTTON_STATE_RELEASED); break; default: - notify_key(&device->seat->seat, - time, e->code, - e->value ? WL_KEYBOARD_KEY_STATE_PRESSED : - WL_KEYBOARD_KEY_STATE_RELEASED, - STATE_UPDATE_AUTOMATIC); + /* Only let KEY_* codes pass through. */ + if (!(e->code <= KEY_MICMUTE || + (e->code >= KEY_OK && e->code <= KEY_LIGHTS_TOGGLE))) + break; + + keyboard_notify_key( + &device->base, + time, + e->code, + e->value ? LIBINPUT_KEY_STATE_PRESSED : + LIBINPUT_KEY_STATE_RELEASED); break; } } static void -evdev_process_touch(struct evdev_input_device *device, - struct input_event *e) +evdev_process_touch(struct evdev_device *device, + struct input_event *e, + uint64_t time) { - const int screen_width = device->output->current->width; - const int screen_height = device->output->current->height; - switch (e->code) { case ABS_MT_SLOT: + evdev_flush_pending_event(device, time); device->mt.slot = e->value; break; case ABS_MT_TRACKING_ID: + if (device->pending_event != EVDEV_NONE && + device->pending_event != EVDEV_ABSOLUTE_MT_MOTION) + evdev_flush_pending_event(device, time); if (e->value >= 0) - device->pending_events |= EVDEV_ABSOLUTE_MT_DOWN; + device->pending_event = EVDEV_ABSOLUTE_MT_DOWN; else - device->pending_events |= EVDEV_ABSOLUTE_MT_UP; + device->pending_event = EVDEV_ABSOLUTE_MT_UP; break; case ABS_MT_POSITION_X: - device->mt.x[device->mt.slot] = - (e->value - device->abs.min_x) * screen_width / - (device->abs.max_x - device->abs.min_x) + - device->output->x; - device->pending_events |= EVDEV_ABSOLUTE_MT_MOTION; + 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.y[device->mt.slot] = - (e->value - device->abs.min_y) * screen_height / - (device->abs.max_y - device->abs.min_y) + - device->output->y; - device->pending_events |= EVDEV_ABSOLUTE_MT_MOTION; + device->mt.slots[device->mt.slot].y = e->value; + if (device->pending_event == EVDEV_NONE) + device->pending_event = EVDEV_ABSOLUTE_MT_MOTION; break; } } static inline void -evdev_process_absolute_motion(struct evdev_input_device *device, +evdev_process_absolute_motion(struct evdev_device *device, struct input_event *e) { - const int screen_width = device->output->current->width; - const int screen_height = device->output->current->height; - 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->output->x; - device->pending_events |= EVDEV_ABSOLUTE_MOTION; + 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->output->y; - device->pending_events |= EVDEV_ABSOLUTE_MOTION; + device->abs.y = e->value; + if (device->pending_event == EVDEV_NONE) + device->pending_event = EVDEV_ABSOLUTE_MOTION; break; } } static inline void -evdev_process_relative(struct evdev_input_device *device, - struct input_event *e, uint32_t time) +evdev_process_relative(struct evdev_device *device, + struct input_event *e, uint64_t time) { + struct libinput_device *base = &device->base; + switch (e->code) { case REL_X: - device->rel.dx += wl_fixed_from_int(e->value); - device->pending_events |= EVDEV_RELATIVE_MOTION; + if (device->pending_event != EVDEV_RELATIVE_MOTION) + evdev_flush_pending_event(device, time); + device->rel.dx += e->value; + device->pending_event = EVDEV_RELATIVE_MOTION; break; case REL_Y: - device->rel.dy += wl_fixed_from_int(e->value); - device->pending_events |= EVDEV_RELATIVE_MOTION; + if (device->pending_event != EVDEV_RELATIVE_MOTION) + evdev_flush_pending_event(device, time); + device->rel.dy += e->value; + device->pending_event = EVDEV_RELATIVE_MOTION; break; case REL_WHEEL: - notify_axis(&device->seat->seat, - time, - WL_POINTER_AXIS_VERTICAL_SCROLL, - wl_fixed_from_int(e->value)); + evdev_flush_pending_event(device, time); + pointer_notify_axis( + base, + time, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, + -1 * e->value * DEFAULT_AXIS_STEP_DISTANCE); break; case REL_HWHEEL: - notify_axis(&device->seat->seat, - time, - WL_POINTER_AXIS_HORIZONTAL_SCROLL, - wl_fixed_from_int(e->value)); - break; + evdev_flush_pending_event(device, time); + switch (e->value) { + case -1: + /* Scroll left */ + case 1: + /* Scroll right */ + pointer_notify_axis( + base, + time, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, + e->value * DEFAULT_AXIS_STEP_DISTANCE); + break; + default: + break; + + } } } static inline void -evdev_process_absolute(struct evdev_input_device *device, - struct input_event *e) +evdev_process_absolute(struct evdev_device *device, + struct input_event *e, + uint64_t time) { if (device->is_mt) { - evdev_process_touch(device, e); + evdev_process_touch(device, e, time); } else { evdev_process_absolute_motion(device, e); } } -static int -is_motion_event(struct input_event *e) +static inline int +evdev_need_touch_frame(struct evdev_device *device) { - switch (e->type) { - case EV_REL: - switch (e->code) { - case REL_X: - case REL_Y: - return 1; - } - case EV_ABS: - switch (e->code) { - case ABS_X: - case ABS_Y: - case ABS_MT_POSITION_X: - case ABS_MT_POSITION_Y: - return 1; - } + 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 -evdev_flush_motion(struct evdev_input_device *device, uint32_t time) -{ - struct weston_seat *master = device->seat; - - if (!device->pending_events) - return; - - if (device->pending_events & EVDEV_RELATIVE_MOTION) { - notify_motion(&master->seat, time, - master->seat.pointer->x + device->rel.dx, - master->seat.pointer->y + device->rel.dy); - device->pending_events &= ~EVDEV_RELATIVE_MOTION; - device->rel.dx = 0; - device->rel.dy = 0; - } - if (device->pending_events & EVDEV_ABSOLUTE_MT_DOWN) { - notify_touch(&master->seat, time, - device->mt.slot, - wl_fixed_from_int(device->mt.x[device->mt.slot]), - wl_fixed_from_int(device->mt.y[device->mt.slot]), - WL_TOUCH_DOWN); - device->pending_events &= ~EVDEV_ABSOLUTE_MT_DOWN; - device->pending_events &= ~EVDEV_ABSOLUTE_MT_MOTION; - } - if (device->pending_events & EVDEV_ABSOLUTE_MT_MOTION) { - notify_touch(&master->seat, time, - device->mt.slot, - wl_fixed_from_int(device->mt.x[device->mt.slot]), - wl_fixed_from_int(device->mt.y[device->mt.slot]), - WL_TOUCH_MOTION); - device->pending_events &= ~EVDEV_ABSOLUTE_MT_DOWN; - device->pending_events &= ~EVDEV_ABSOLUTE_MT_MOTION; - } - if (device->pending_events & EVDEV_ABSOLUTE_MT_UP) { - notify_touch(&master->seat, time, device->mt.slot, 0, 0, - WL_TOUCH_UP); - device->pending_events &= ~EVDEV_ABSOLUTE_MT_UP; - } - if (device->pending_events & EVDEV_ABSOLUTE_MOTION) { - notify_motion(&master->seat, time, - wl_fixed_from_int(device->abs.x), - wl_fixed_from_int(device->abs.y)); - device->pending_events &= ~EVDEV_ABSOLUTE_MOTION; - } -} - -static void fallback_process(struct evdev_dispatch *dispatch, - struct evdev_input_device *device, + 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); break; case EV_ABS: - evdev_process_absolute(device, event); + evdev_process_absolute(device, event, time); break; case EV_KEY: 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; } } @@ -300,295 +497,430 @@ fallback_dispatch_create(void) return dispatch; } -static void -evdev_process_events(struct evdev_input_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; - - device->pending_events = 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; - - /* we try to minimize the amount of notifications to be - * forwarded to the compositor, so we accumulate motion - * events and send as a bunch */ - if (!is_motion_event(e)) - evdev_flush_motion(device, time); + 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); + } + } } - - evdev_flush_motion(device, time); } static int -evdev_input_device_data(int fd, uint32_t mask, void *data) +evdev_sync_device(struct evdev_device *device) { - struct weston_compositor *ec; - struct evdev_input_device *device = data; - struct input_event ev[32]; - int len; + struct input_event ev; + int rc; - ec = device->seat->compositor; - if (!ec->focus) - return 1; + 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; + 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) { - /* FIXME: call evdev_input_device_destroy when errno is ENODEV. */ - return 1; + 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); - evdev_process_events(device, ev, len / sizeof ev[0]); + if (rc != -EAGAIN && rc != -EINTR) { + libinput_remove_source(libinput, device->source); + device->source = NULL; + } +} - } while (len > 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; - return 1; + return 0; } static int -evdev_configure_device(struct evdev_input_device *device) +evdev_configure_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; + 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_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.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.absinfo_y = absinfo; + has_abs = 1; } - if (TEST_BIT(abs_bits, ABS_MT_SLOT)) { - 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; + /* We only handle the slotted Protocol B in weston. + Devices with ABS_MT_POSITION_* but not ABS_MT_SLOT + require mtdev for conversion. */ + 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.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.absinfo_y = absinfo; device->is_mt = 1; - device->mt.slot = 0; - device->caps |= EVDEV_TOUCH; + has_touch = 1; + has_mt = 1; + + if (!libevdev_has_event_code(evdev, + EV_ABS, ABS_MT_SLOT)) { + device->mtdev = mtdev_new_open(device->fd); + 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 { + num_slots = libevdev_get_num_slots(device->evdev); + active_slot = libevdev_get_current_slot(evdev); + } + + 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(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); + 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("input device '%s', %s is a touchpad\n", + device->devname, device->devnode); + } 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; + if (libevdev_has_event_code(evdev, EV_KEY, i)) { + has_keyboard = 1; break; } } - for (i = BTN_MISC; i < KEY_OK; i++) { - if (TEST_BIT(key_bits, i)) { - device->caps |= EVDEV_BUTTON; + if (libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH)) + has_touch = 1; + for (i = BTN_MISC; i < BTN_JOYSTICK; i++) { + if (libevdev_has_event_code(evdev, EV_KEY, i)) { + has_button = 1; break; } } } - if (TEST_BIT(ev_bits, EV_LED)) { - device->caps |= EVDEV_KEYBOARD; - } + if (libevdev_has_event_type(evdev, EV_LED)) + has_keyboard = 1; - /* 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) { - weston_log("input device %s, %s " - "ignored: unsupported device type\n", - device->devname, device->devnode); - return -1; - } + if ((has_abs || has_rel) && has_button) { + if (configure_pointer_acceleration(device) == -1) + return -1; - if ((device->caps & - (EVDEV_MOTION_ABS | EVDEV_MOTION_REL | EVDEV_BUTTON))) { - weston_seat_init_pointer(device->seat); - weston_log("input device %s, %s is a pointer\n", - device->devname, device->devnode); + device->seat_caps |= EVDEV_DEVICE_POINTER; + + log_info("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->caps & EVDEV_KEYBOARD)) { - weston_seat_init_keyboard(device->seat, NULL); - weston_log("input device %s, %s is a keyboard\n", - device->devname, device->devnode); + if (has_keyboard) { + device->seat_caps |= EVDEV_DEVICE_KEYBOARD; + log_info("input device '%s', %s is a keyboard\n", + device->devname, device->devnode); } - if ((device->caps & EVDEV_TOUCH)) { - weston_seat_init_touch(device->seat); - weston_log("input device %s, %s is a touch device\n", - device->devname, device->devnode); + if (has_touch && !has_button) { + device->seat_caps |= EVDEV_DEVICE_TOUCH; + log_info("input device '%s', %s is a touch device\n", + device->devname, device->devnode); } return 0; } -struct evdev_input_device * -evdev_input_device_create(struct weston_seat *seat, - const char *path, int device_fd) +struct evdev_device * +evdev_device_create(struct libinput_seat *seat, + const char *devnode, + const char *sysname) { - struct evdev_input_device *device; - struct weston_compositor *ec; - char devname[256] = "unknown"; + struct libinput *libinput = seat->libinput; + struct evdev_device *device; + 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("opening input device '%s' failed (%s).\n", + devnode, strerror(-fd)); + return NULL; + } - device = malloc(sizeof *device); + device = zalloc(sizeof *device); if (device == NULL) return NULL; - memset(device, 0, sizeof *device); - ec = seat->compositor; - device->output = - container_of(ec->output_list.next, struct weston_output, link); + libinput_device_init(&device->base, seat); - device->seat = seat; + rc = libevdev_new_from_fd(fd, &device->evdev); + if (rc != 0) + return NULL; + + libevdev_set_clock_id(device->evdev, CLOCK_MONOTONIC); + + device->seat_caps = 0; device->is_mt = 0; device->mtdev = NULL; - device->devnode = strdup(path); - device->mt.slot = -1; + device->devnode = strdup(devnode); + device->sysname = strdup(sysname); device->rel.dx = 0; device->rel.dy = 0; + device->abs.seat_slot = -1; device->dispatch = NULL; - device->fd = device_fd; + device->fd = fd; + device->pending_event = EVDEV_NONE; + device->devname = libevdev_get_name(device->evdev); - ioctl(device->fd, EVIOCGNAME(sizeof(devname)), devname); - device->devname = strdup(devname); + libinput_seat_ref(seat); if (evdev_configure_device(device) == -1) - goto err1; + goto err; + + if (device->seat_caps == 0) { + unhandled_device = 1; + goto err; + } /* If the dispatch was not set up use the fallback. */ if (device->dispatch == NULL) device->dispatch = fallback_dispatch_create(); if (device->dispatch == NULL) - goto err1; - + goto err; - if (device->is_mt) { - device->mtdev = mtdev_new_open(device->fd); - if (!device->mtdev) - weston_log("mtdev failed to open for %s\n", path); - } + device->source = + libinput_add_fd(libinput, fd, evdev_device_dispatch, device); + if (!device->source) + goto err; - device->source = wl_event_loop_add_fd(ec->input_loop, device->fd, - WL_EVENT_READABLE, - evdev_input_device_data, device); - if (device->source == NULL) - goto err2; + list_insert(seat->devices_list.prev, &device->base.link); + notify_added_device(&device->base); return device; -err2: - device->dispatch->interface->destroy(device->dispatch); -err1: - free(device->devname); - free(device->devnode); - free(device); - return NULL; +err: + if (fd >= 0) + close_restricted(libinput, fd); + evdev_device_destroy(device); + + return unhandled_device ? EVDEV_UNHANDLED_DEVICE : NULL; +} + +int +evdev_device_get_keys(struct evdev_device *device, char *keys, size_t size) +{ + int len; + + memset(keys, 0, size); + len = ioctl(device->fd, EVIOCGKEY(size), keys); + + return (len == -1) ? -errno : len; +} + +const char * +evdev_device_get_output(struct evdev_device *device) +{ + return device->output_name; +} + +const char * +evdev_device_get_sysname(struct evdev_device *device) +{ + return device->sysname; } void -evdev_input_device_destroy(struct evdev_input_device *device) +evdev_device_calibrate(struct evdev_device *device, float calibration[6]) { - struct evdev_dispatch *dispatch; + device->abs.apply_calibration = 1; + memcpy(device->abs.calibration, calibration, sizeof device->abs.calibration); +} - dispatch = device->dispatch; - if (dispatch) - dispatch->interface->destroy(dispatch); +int +evdev_device_has_capability(struct evdev_device *device, + enum libinput_device_capability capability) +{ + switch (capability) { + case LIBINPUT_DEVICE_CAP_POINTER: + return !!(device->seat_caps & EVDEV_DEVICE_POINTER); + case LIBINPUT_DEVICE_CAP_KEYBOARD: + return !!(device->seat_caps & EVDEV_DEVICE_KEYBOARD); + case LIBINPUT_DEVICE_CAP_TOUCH: + return !!(device->seat_caps & EVDEV_DEVICE_TOUCH); + default: + return 0; + } +} + +int +evdev_device_get_size(struct evdev_device *device, + double *width, + double *height) +{ + 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 || !x->resolution || !y->resolution) + return -1; + + *width = evdev_convert_to_mm(x, x->maximum); + *height = evdev_convert_to_mm(y, y->maximum); + + return 0; +} + +void +evdev_device_remove(struct evdev_device *device) +{ + if (device->source) + libinput_remove_source(device->base.seat->libinput, + device->source); - wl_event_source_remove(device->source); - wl_list_remove(&device->link); if (device->mtdev) mtdev_close_delete(device->mtdev); - close(device->fd); - free(device->devname); - free(device->devnode); - free(device); + close_restricted(device->base.seat->libinput, device->fd); + device->fd = -1; + list_remove(&device->base.link); + + notify_removed_device(&device->base); + libinput_device_unref(&device->base); } void -evdev_notify_keyboard_focus(struct weston_seat *seat, - struct wl_list *evdev_devices) +evdev_device_destroy(struct evdev_device *device) { - struct evdev_input_device *device; - struct wl_array keys; - unsigned int i, set; - char evdev_keys[(KEY_CNT + 7) / 8], all_keys[(KEY_CNT + 7) / 8]; - uint32_t *k; - int ret; - - memset(all_keys, 0, sizeof all_keys); - wl_list_for_each(device, evdev_devices, link) { - memset(evdev_keys, 0, sizeof evdev_keys); - ret = ioctl(device->fd, - EVIOCGKEY(sizeof evdev_keys), evdev_keys); - if (ret < 0) { - weston_log("failed to get keys for device %s\n", - device->devnode); - continue; - } - for (i = 0; i < ARRAY_LENGTH(evdev_keys); i++) - all_keys[i] |= evdev_keys[i]; - } - - wl_array_init(&keys); - for (i = 0; i < KEY_CNT; i++) { - set = all_keys[i >> 3] & (1 << (i & 7)); - if (set) { - k = wl_array_add(&keys, sizeof *k); - *k = i; - } - } + struct evdev_dispatch *dispatch; - notify_keyboard_focus_in(&seat->seat, &keys, STATE_UPDATE_AUTOMATIC); + dispatch = device->dispatch; + if (dispatch) + dispatch->interface->destroy(dispatch); - wl_array_release(&keys); + motion_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); }