/* * Copyright © 2010 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of the copyright holders not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. The copyright holders make * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "compositor.h" #include "evdev.h" #define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int(10) void evdev_led_update(struct evdev_device *device, enum weston_led leds) { static const struct { enum weston_led weston; int evdev; } map[] = { { LED_NUM_LOCK, LED_NUML }, { LED_CAPS_LOCK, LED_CAPSL }, { LED_SCROLL_LOCK, LED_SCROLLL }, }; struct input_event ev[ARRAY_LENGTH(map)]; unsigned int i; if (!device->caps & EVDEV_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); } i = write(device->fd, ev, sizeof ev); (void)i; /* no, we really don't care about the return value */ } static inline void evdev_process_key(struct evdev_device *device, struct input_event *e, int time) { if (e->value == 2) return; 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: notify_button(device->seat, time, e->code, e->value ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED); break; default: notify_key(device->seat, time, e->code, e->value ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED, STATE_UPDATE_AUTOMATIC); break; } } static void evdev_process_touch(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_MT_SLOT: device->mt.slot = e->value; break; case ABS_MT_TRACKING_ID: if (e->value >= 0) device->pending_events |= EVDEV_ABSOLUTE_MT_DOWN; else device->pending_events |= 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; 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; break; } } static inline void 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; 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; break; } } static inline void evdev_process_relative(struct evdev_device *device, struct input_event *e, uint32_t time) { switch (e->code) { case REL_X: device->rel.dx += wl_fixed_from_int(e->value); device->pending_events |= EVDEV_RELATIVE_MOTION; break; case REL_Y: device->rel.dy += wl_fixed_from_int(e->value); device->pending_events |= EVDEV_RELATIVE_MOTION; break; case REL_WHEEL: switch (e->value) { case -1: /* Scroll down */ case 1: /* Scroll up */ notify_axis(device->seat, time, WL_POINTER_AXIS_VERTICAL_SCROLL, -1 * e->value * DEFAULT_AXIS_STEP_DISTANCE); break; default: break; } break; case REL_HWHEEL: switch (e->value) { case -1: /* Scroll left */ case 1: /* Scroll right */ notify_axis(device->seat, time, WL_POINTER_AXIS_HORIZONTAL_SCROLL, e->value * DEFAULT_AXIS_STEP_DISTANCE); break; default: break; } } } static inline void evdev_process_absolute(struct evdev_device *device, struct input_event *e) { if (device->is_mt) { evdev_process_touch(device, e); } else { evdev_process_absolute_motion(device, e); } } static int is_motion_event(struct input_event *e) { switch (e->type) { case EV_REL: switch (e->code) { case REL_X: case REL_Y: return 1; } break; case EV_ABS: switch (e->code) { case ABS_X: case ABS_Y: case ABS_MT_POSITION_X: case ABS_MT_POSITION_Y: return 1; } } return 0; } static void evdev_flush_motion(struct evdev_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, 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, 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, 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, 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, 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_device *device, struct input_event *event, uint32_t time) { switch (event->type) { case EV_REL: evdev_process_relative(device, event, time); break; case EV_ABS: evdev_process_absolute(device, event); break; case EV_KEY: evdev_process_key(device, event, time); break; } } static void fallback_destroy(struct evdev_dispatch *dispatch) { free(dispatch); } struct evdev_dispatch_interface fallback_interface = { fallback_process, fallback_destroy }; static struct evdev_dispatch * fallback_dispatch_create(void) { struct evdev_dispatch *dispatch = malloc(sizeof *dispatch); if (dispatch == NULL) return NULL; dispatch->interface = &fallback_interface; return dispatch; } static void evdev_process_events(struct evdev_device *device, struct input_event *ev, int count) { struct evdev_dispatch *dispatch = device->dispatch; struct input_event *e, *end; uint32_t time = 0; device->pending_events = 0; 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); } evdev_flush_motion(device, time); } static int evdev_device_data(int fd, uint32_t mask, void *data) { struct weston_compositor *ec; struct evdev_device *device = data; struct input_event ev[32]; int len; ec = device->seat->compositor; if (!ec->focus) return 1; /* 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_device_destroy when errno is ENODEV. */ return 1; } evdev_process_events(device, ev, len / sizeof ev[0]); } while (len > 0); return 1; } static int 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; unsigned int i; has_key = 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; } 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 (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; device->is_mt = 1; device->mt.slot = 0; device->caps |= EVDEV_TOUCH; } } 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; } } for (i = BTN_MISC; i < KEY_OK; i++) { if (TEST_BIT(key_bits, i)) { device->caps |= EVDEV_BUTTON; 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) { weston_log("input device %s, %s " "ignored: unsupported device type\n", device->devname, device->devnode); 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); } 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 ((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); } return 0; } struct evdev_device * evdev_device_create(struct weston_seat *seat, const char *path, int device_fd) { struct evdev_device *device; struct weston_compositor *ec; char devname[256] = "unknown"; device = malloc(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); device->seat = seat; device->is_mt = 0; device->mtdev = NULL; device->devnode = strdup(path); device->mt.slot = -1; device->rel.dx = 0; device->rel.dy = 0; device->dispatch = NULL; device->fd = device_fd; ioctl(device->fd, EVIOCGNAME(sizeof(devname)), devname); device->devname = strdup(devname); if (evdev_configure_device(device) == -1) 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 err1; 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 = wl_event_loop_add_fd(ec->input_loop, device->fd, WL_EVENT_READABLE, evdev_device_data, device); if (device->source == NULL) goto err2; return device; err2: device->dispatch->interface->destroy(device->dispatch); err1: free(device->devname); free(device->devnode); free(device); return NULL; } void evdev_device_destroy(struct evdev_device *device) { struct evdev_dispatch *dispatch; dispatch = device->dispatch; if (dispatch) dispatch->interface->destroy(dispatch); 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); } void evdev_notify_keyboard_focus(struct weston_seat *seat, struct wl_list *evdev_devices) { struct evdev_device *device; struct wl_array keys; unsigned int i, set; char evdev_keys[(KEY_CNT + 7) / 8]; char all_keys[(KEY_CNT + 7) / 8]; uint32_t *k; int ret; if (!seat->seat.keyboard) return; 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; } } notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC); wl_array_release(&keys); }