X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fevdev.c;h=122a2d9bead76088fef19de539afe3c2f7d5382d;hb=967911791f9988be63b8a2c1ffb608eb2871889b;hp=32d73679dadfbc9d00b6cc5d78493804b9a4e97e;hpb=bcac853c5321dc9279f2737386f621279ac7ff01;p=platform%2Fupstream%2Flibinput.git diff --git a/src/evdev.c b/src/evdev.c index 32d7367..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,94 +21,269 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include "config.h" + +#include #include #include -#include +#include "linux/input.h" #include #include +#include +#include +#include +#include -#include "compositor.h" +#include "libinput.h" #include "evdev.h" +#include "filter.h" +#include "libinput-private.h" -struct evdev_input { - struct weston_input_device base; - struct wl_list devices_list; - struct udev_monitor *udev_monitor; - char *seat_id; -}; +#define DEFAULT_AXIS_STEP_DISTANCE 10 -#define MAX_SLOTS 16 +void +evdev_device_led_update(struct evdev_device *device, enum libinput_led leds) +{ + static const struct { + enum libinput_led weston; + int evdev; + } map[] = { + { LIBINPUT_LED_NUM_LOCK, LED_NUML }, + { LIBINPUT_LED_CAPS_LOCK, LED_CAPSL }, + { LIBINPUT_LED_SCROLL_LOCK, LED_SCROLLL }, + }; + struct input_event ev[ARRAY_LENGTH(map) + 1]; + unsigned int i; + + if (!(device->seat_caps & EVDEV_DEVICE_KEYBOARD)) + return; -struct evdev_input_device { - struct evdev_input *master; - struct wl_list link; - struct wl_event_source *source; - struct weston_output *output; - char *devnode; - int fd; - struct { - int min_x, max_x, min_y, max_y; - int old_x, old_y, reset_x, reset_y; - int32_t x, y; - } abs; + 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; - struct { - int slot; - int32_t x[MAX_SLOTS]; - int32_t y[MAX_SLOTS]; - } mt; + i = write(device->fd, ev, sizeof ev); + (void)i; /* no, we really don't care about the return value */ +} - struct { - int dx, dy; - } rel; +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]; - int type; /* event type flags */ + *y = device->abs.x * device->abs.calibration[3] + + device->abs.y * device->abs.calibration[4] + + device->abs.calibration[5]; + } +} - int is_touchpad, is_mt; -}; +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); +} -/* event type flags */ -#define EVDEV_ABSOLUTE_MOTION (1 << 0) -#define EVDEV_ABSOLUTE_MT_DOWN (1 << 1) -#define EVDEV_ABSOLUTE_MT_MOTION (1 << 2) -#define EVDEV_ABSOLUTE_MT_UP (1 << 3) -#define EVDEV_RELATIVE_MOTION (1 << 4) +double +evdev_device_transform_x(struct evdev_device *device, + double x, + uint32_t width) +{ + return scale_axis(device->abs.absinfo_x, x, width); +} -static inline void -evdev_process_key(struct evdev_input_device *device, - struct input_event *e, int time) +double +evdev_device_transform_y(struct evdev_device *device, + double y, + uint32_t height) { - if (e->value == 2) + 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; - switch (e->code) { - case BTN_TOOL_PEN: - case BTN_TOOL_RUBBER: - case BTN_TOOL_BRUSH: - case BTN_TOOL_PENCIL: - case BTN_TOOL_AIRBRUSH: - case BTN_TOOL_FINGER: - case BTN_TOOL_MOUSE: - case BTN_TOOL_LENS: - if (device->is_touchpad) - { - device->abs.reset_x = 1; - device->abs.reset_y = 1; + /* 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 BTN_TOUCH: - /* Multitouch touchscreen devices might not send individually - * button events each time a new finger is down. So we don't - * send notification for such devices and we solve the button - * case emulating on compositor side. */ - if (device->is_mt) + 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; - /* Treat BTN_TOUCH from devices that only have BTN_TOUCH as - * BTN_LEFT */ - e->code = BTN_LEFT; - /* Intentional fallthrough! */ + 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_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: case BTN_MIDDLE: @@ -116,547 +292,635 @@ evdev_process_key(struct evdev_input_device *device, case BTN_FORWARD: case BTN_BACK: case BTN_TASK: - notify_button(&device->master->base.input_device, - time, e->code, e->value); + pointer_notify_button( + &device->base, + time, + e->code, + e->value ? LIBINPUT_BUTTON_STATE_PRESSED : + LIBINPUT_BUTTON_STATE_RELEASED); break; default: - notify_key(&device->master->base.input_device, - time, e->code, e->value); + /* 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->type |= EVDEV_ABSOLUTE_MT_DOWN; + device->pending_event = EVDEV_ABSOLUTE_MT_DOWN; else - device->type |= 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->type |= 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->type |= 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->type |= 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->type |= 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_absolute_motion_touchpad(struct evdev_input_device *device, - struct input_event *e) +evdev_process_relative(struct evdev_device *device, + struct input_event *e, uint64_t time) { - /* FIXME: Make this configurable somehow. */ - const int touchpad_speed = 700; + struct libinput_device *base = &device->base; switch (e->code) { - case ABS_X: - e->value -= device->abs.min_x; - if (device->abs.reset_x) - device->abs.reset_x = 0; - else { - device->rel.dx = - (e->value - device->abs.old_x) * - touchpad_speed / - (device->abs.max_x - device->abs.min_x); - } - device->abs.old_x = e->value; - device->type |= EVDEV_RELATIVE_MOTION; - break; - case ABS_Y: - e->value -= device->abs.min_y; - if (device->abs.reset_y) - device->abs.reset_y = 0; - else { - device->rel.dy = - (e->value - device->abs.old_y) * - touchpad_speed / - /* maybe use x size here to have the same scale? */ - (device->abs.max_y - device->abs.min_y); - } - device->abs.old_y = e->value; - device->type |= EVDEV_RELATIVE_MOTION; - break; - } -} - -static inline void -evdev_process_relative_motion(struct evdev_input_device *device, - struct input_event *e) -{ - switch (e->code) { case REL_X: + if (device->pending_event != EVDEV_RELATIVE_MOTION) + evdev_flush_pending_event(device, time); device->rel.dx += e->value; - device->type |= EVDEV_RELATIVE_MOTION; + 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 += e->value; - device->type |= EVDEV_RELATIVE_MOTION; + device->pending_event = EVDEV_RELATIVE_MOTION; + break; + case REL_WHEEL: + 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: + 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_touchpad) { - evdev_process_absolute_motion_touchpad(device, e); - } else if (device->is_mt) { - evdev_process_touch(device, e); + if (device->is_mt) { + 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) +fallback_process(struct evdev_dispatch *dispatch, + struct evdev_device *device, + struct input_event *event, + uint64_t time) { - struct wl_input_device *master = &device->master->base.input_device; - - if (!device->type) - return; + int need_frame = 0; - if (device->type & EVDEV_RELATIVE_MOTION) { - notify_motion(master, time, - master->x + device->rel.dx, - master->y + device->rel.dy); - device->type &= ~EVDEV_RELATIVE_MOTION; - device->rel.dx = 0; - device->rel.dy = 0; - } - if (device->type & EVDEV_ABSOLUTE_MT_DOWN) { - notify_touch(master, time, - device->mt.slot, - device->mt.x[device->mt.slot], - device->mt.y[device->mt.slot], - WL_INPUT_DEVICE_TOUCH_DOWN); - device->type &= ~EVDEV_ABSOLUTE_MT_DOWN; - device->type &= ~EVDEV_ABSOLUTE_MT_MOTION; - } - if (device->type & EVDEV_ABSOLUTE_MT_MOTION) { - notify_touch(master, time, - device->mt.slot, - device->mt.x[device->mt.slot], - device->mt.y[device->mt.slot], - WL_INPUT_DEVICE_TOUCH_MOTION); - device->type &= ~EVDEV_ABSOLUTE_MT_DOWN; - device->type &= ~EVDEV_ABSOLUTE_MT_MOTION; - } - if (device->type & EVDEV_ABSOLUTE_MT_UP) { - notify_touch(master, time, device->mt.slot, 0, 0, - WL_INPUT_DEVICE_TOUCH_UP); - device->type &= ~EVDEV_ABSOLUTE_MT_UP; - } - if (device->type & EVDEV_ABSOLUTE_MOTION) { - notify_motion(master, time, device->abs.x, device->abs.y); - device->type &= ~EVDEV_ABSOLUTE_MOTION; + switch (event->type) { + case EV_REL: + evdev_process_relative(device, event, time); + break; + case EV_ABS: + 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; } } -static int -evdev_input_device_data(int fd, uint32_t mask, void *data) +static void +fallback_destroy(struct evdev_dispatch *dispatch) { - struct weston_compositor *ec; - struct evdev_input_device *device = data; - struct input_event ev[8], *e, *end; - int len; - uint32_t time = 0; + free(dispatch); +} - ec = device->master->base.compositor; - if (!ec->focus) - return 1; +struct evdev_dispatch_interface fallback_interface = { + fallback_process, + fallback_destroy +}; - len = read(fd, &ev, sizeof ev); - if (len < 0 || len % sizeof e[0] != 0) { - /* FIXME: call device_removed when errno is ENODEV. */; - return 1; - } +static struct evdev_dispatch * +fallback_dispatch_create(void) +{ + struct evdev_dispatch *dispatch = malloc(sizeof *dispatch); + if (dispatch == NULL) + return NULL; - e = ev; - end = (void *) ev + len; - 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); - switch (e->type) { - case EV_REL: - evdev_process_relative_motion(device, e); - break; - case EV_ABS: - evdev_process_absolute(device, e); - break; - case EV_KEY: - evdev_process_key(device, e, time); - break; + dispatch->interface = &fallback_interface; + + return dispatch; +} + +static inline void +evdev_process_event(struct evdev_device *device, struct input_event *e) +{ + struct evdev_dispatch *dispatch = device->dispatch; + uint64_t time = e->time.tv_sec * 1000ULL + e->time.tv_usec / 1000; + + 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; - evdev_flush_motion(device, time); + 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 1; + 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 { + 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); -/* copied from udev/extras/input_id/input_id.c */ -/* we must use this kernel-compatible implementation */ -#define BITS_PER_LONG (sizeof(unsigned long) * 8) -#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) -#define OFF(x) ((x)%BITS_PER_LONG) -#define BIT(x) (1UL<> OFF(bit)) & 1) -/* end copied */ + if (rc != -EAGAIN && rc != -EINTR) { + libinput_remove_source(libinput, device->source); + device->source = NULL; + } +} static int -evdev_configure_device(struct evdev_input_device *device) +configure_pointer_acceleration(struct evdev_device *device) { - struct input_absinfo absinfo; - unsigned long ev_bits[NBITS(EV_MAX)]; - unsigned long abs_bits[NBITS(ABS_MAX)]; - unsigned long key_bits[NBITS(KEY_MAX)]; - int has_key, has_abs; - - has_key = 0; - has_abs = 0; + device->pointer.filter = + create_pointer_accelator_filter( + pointer_accel_profile_smooth_simple); + if (!device->pointer.filter) + return -1; - ioctl(device->fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits); - if (TEST_BIT(ev_bits, EV_ABS)) { - has_abs = 1; + return 0; +} - 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; +static int +evdev_configure_device(struct evdev_device *device) +{ + 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_rel = 0; + has_abs = 0; + 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; + 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)) { + /* 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; + 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_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)) - device->is_touchpad = 1; + 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 (libevdev_has_event_code(evdev, EV_KEY, i)) { + has_keyboard = 1; + break; + } + } + 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 (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) - return -1; + if ((has_abs || has_rel) && has_button) { + if (configure_pointer_acceleration(device) == -1) + return -1; + + 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 (has_keyboard) { + device->seat_caps |= EVDEV_DEVICE_KEYBOARD; + log_info("input device '%s', %s is a keyboard\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; } -static struct evdev_input_device * -evdev_input_device_create(struct evdev_input *master, - struct wl_display *display, const char *path) +struct evdev_device * +evdev_device_create(struct libinput_seat *seat, + const char *devnode, + const char *sysname) { - struct evdev_input_device *device; - struct wl_event_loop *loop; - struct weston_compositor *ec; + 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; - ec = master->base.compositor; - device->output = - container_of(ec->output_list.next, struct weston_output, link); + libinput_device_init(&device->base, seat); + + rc = libevdev_new_from_fd(fd, &device->evdev); + if (rc != 0) + return NULL; + + libevdev_set_clock_id(device->evdev, CLOCK_MONOTONIC); - device->master = master; - device->is_touchpad = 0; + device->seat_caps = 0; device->is_mt = 0; - device->devnode = strdup(path); - device->mt.slot = -1; + device->mtdev = NULL; + 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 = fd; + device->pending_event = EVDEV_NONE; + device->devname = libevdev_get_name(device->evdev); - device->fd = open(path, O_RDONLY); - if (device->fd < 0) - goto err0; + libinput_seat_ref(seat); if (evdev_configure_device(device) == -1) - goto err1; + goto err; + + if (device->seat_caps == 0) { + unhandled_device = 1; + goto err; + } - loop = wl_display_get_event_loop(display); - device->source = wl_event_loop_add_fd(loop, device->fd, - WL_EVENT_READABLE, - evdev_input_device_data, device); - if (device->source == NULL) - goto err1; + /* If the dispatch was not set up use the fallback. */ + if (device->dispatch == NULL) + device->dispatch = fallback_dispatch_create(); + if (device->dispatch == NULL) + goto err; - wl_list_insert(master->devices_list.prev, &device->link); + device->source = + libinput_add_fd(libinput, fd, evdev_device_dispatch, device); + if (!device->source) + goto err; + + list_insert(seat->devices_list.prev, &device->base.link); + notify_added_device(&device->base); return device; -err1: - close(device->fd); -err0: - free(device->devnode); - free(device); - return NULL; -} +err: + if (fd >= 0) + close_restricted(libinput, fd); + evdev_device_destroy(device); -static const char default_seat[] = "seat0"; + return unhandled_device ? EVDEV_UNHANDLED_DEVICE : NULL; +} -static void -device_added(struct udev_device *udev_device, struct evdev_input *master) +int +evdev_device_get_keys(struct evdev_device *device, char *keys, size_t size) { - struct weston_compositor *c; - const char *devnode; - const char *device_seat; - - device_seat = udev_device_get_property_value(udev_device, "ID_SEAT"); - if (!device_seat) - device_seat = default_seat; + int len; - if (strcmp(device_seat, master->seat_id)) - return; + memset(keys, 0, size); + len = ioctl(device->fd, EVIOCGKEY(size), keys); - c = master->base.compositor; - devnode = udev_device_get_devnode(udev_device); - evdev_input_device_create(master, c->wl_display, devnode); + return (len == -1) ? -errno : len; } -static void -device_removed(struct udev_device *udev_device, struct evdev_input *master) +const char * +evdev_device_get_output(struct evdev_device *device) { - const char *devnode = udev_device_get_devnode(udev_device); - struct evdev_input_device *device, *next; - - wl_list_for_each_safe(device, next, &master->devices_list, link) { - if (!strcmp(device->devnode, devnode)) { - wl_event_source_remove(device->source); - wl_list_remove(&device->link); - close(device->fd); - free(device->devnode); - free(device); - break; - } - } + return device->output_name; } -void -evdev_add_devices(struct udev *udev, struct weston_input_device *input_base) +const char * +evdev_device_get_sysname(struct evdev_device *device) { - struct evdev_input *input = (struct evdev_input *) input_base; - struct udev_enumerate *e; - struct udev_list_entry *entry; - struct udev_device *device; - const char *path; - - e = udev_enumerate_new(udev); - udev_enumerate_add_match_subsystem(e, "input"); - udev_enumerate_scan_devices(e); - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { - path = udev_list_entry_get_name(entry); - device = udev_device_new_from_syspath(udev, path); - - if (strncmp("event", udev_device_get_sysname(device), 5) != 0) - continue; - - device_added(device, input); - - udev_device_unref(device); - } - udev_enumerate_unref(e); - - if (wl_list_empty(&input->devices_list)) { - fprintf(stderr, - "warning: no input devices on entering Weston. " - "Possible causes:\n" - "\t- no permissions to read /dev/input/event*\n" - "\t- seats misconfigured " - "(Weston backend option 'seat', " - "udev device property ID_SEAT)\n"); - } + return device->sysname; } -static int -evdev_udev_handler(int fd, uint32_t mask, void *data) +void +evdev_device_calibrate(struct evdev_device *device, float calibration[6]) { - struct evdev_input *master = data; - struct udev_device *udev_device; - const char *action; - - udev_device = udev_monitor_receive_device(master->udev_monitor); - if (!udev_device) - return 1; - - action = udev_device_get_action(udev_device); - if (action) { - if (strncmp("event", udev_device_get_sysname(udev_device), 5) != 0) - return 0; - - if (!strcmp(action, "add")) { - device_added(udev_device, master); - } - else if (!strcmp(action, "remove")) - device_removed(udev_device, master); - } - udev_device_unref(udev_device); - - return 0; + device->abs.apply_calibration = 1; + memcpy(device->abs.calibration, calibration, sizeof device->abs.calibration); } -static int -evdev_config_udev_monitor(struct udev *udev, struct evdev_input *master) +int +evdev_device_has_capability(struct evdev_device *device, + enum libinput_device_capability capability) { - struct wl_event_loop *loop; - struct weston_compositor *c = master->base.compositor; - - master->udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (!master->udev_monitor) { - fprintf(stderr, "udev: failed to create the udev monitor\n"); - return 0; - } - - udev_monitor_filter_add_match_subsystem_devtype(master->udev_monitor, - "input", NULL); - - if (udev_monitor_enable_receiving(master->udev_monitor)) { - fprintf(stderr, "udev: failed to bind the udev monitor\n"); + 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; } - - loop = wl_display_get_event_loop(c->wl_display); - wl_event_loop_add_fd(loop, udev_monitor_get_fd(master->udev_monitor), - WL_EVENT_READABLE, evdev_udev_handler, master); - - return 1; } -void -evdev_input_create(struct weston_compositor *c, struct udev *udev, - const char *seat) +int +evdev_device_get_size(struct evdev_device *device, + double *width, + double *height) { - struct evdev_input *input; - - input = malloc(sizeof *input); - if (input == NULL) - return; + const struct input_absinfo *x, *y; - memset(input, 0, sizeof *input); - weston_input_device_init(&input->base, c); + x = libevdev_get_abs_info(device->evdev, ABS_X); + y = libevdev_get_abs_info(device->evdev, ABS_Y); - wl_list_init(&input->devices_list); - input->seat_id = strdup(seat); - if (!evdev_config_udev_monitor(udev, input)) { - free(input->seat_id); - free(input); - return; - } + if (!x || !y || !x->resolution || !y->resolution) + return -1; - evdev_add_devices(udev, &input->base); + *width = evdev_convert_to_mm(x, x->maximum); + *height = evdev_convert_to_mm(y, y->maximum); - c->input_device = &input->base.input_device; + return 0; } void -evdev_remove_devices(struct weston_input_device *input_base) +evdev_device_remove(struct evdev_device *device) { - struct evdev_input *input = (struct evdev_input *) input_base; - struct evdev_input_device *device, *next; - - wl_list_for_each_safe(device, next, &input->devices_list, link) { - wl_event_source_remove(device->source); - wl_list_remove(&device->link); - close(device->fd); - free(device->devnode); - free(device); - } + if (device->source) + libinput_remove_source(device->base.seat->libinput, + device->source); + + if (device->mtdev) + mtdev_close_delete(device->mtdev); + 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_input_destroy(struct weston_input_device *input_base) +evdev_device_destroy(struct evdev_device *device) { - struct evdev_input *input = (struct evdev_input *) input_base; + struct evdev_dispatch *dispatch; - evdev_remove_devices(input_base); - wl_list_remove(&input->base.link); - free(input->seat_id); - free(input); + dispatch = device->dispatch; + if (dispatch) + dispatch->interface->destroy(dispatch); + + 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); }