/* * Copyright © 2010 Intel Corporation * Copyright © 2013 Jonas Ådahl * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "compositor.h" #include "libinput-device.h" #include "shared/helpers.h" #include "shared/timespec-util.h" void evdev_led_update(struct evdev_device *device, enum weston_led weston_leds) { enum libinput_led leds = 0; if (weston_leds & LED_NUM_LOCK) leds |= LIBINPUT_LED_NUM_LOCK; if (weston_leds & LED_CAPS_LOCK) leds |= LIBINPUT_LED_CAPS_LOCK; if (weston_leds & LED_SCROLL_LOCK) leds |= LIBINPUT_LED_SCROLL_LOCK; libinput_device_led_update(device->device, leds); } static void handle_keyboard_key(struct libinput_device *libinput_device, struct libinput_event_keyboard *keyboard_event) { struct evdev_device *device = libinput_device_get_user_data(libinput_device); int key_state = libinput_event_keyboard_get_key_state(keyboard_event); int seat_key_count = libinput_event_keyboard_get_seat_key_count(keyboard_event); struct timespec time; /* Ignore key events that are not seat wide state changes. */ if ((key_state == LIBINPUT_KEY_STATE_PRESSED && seat_key_count != 1) || (key_state == LIBINPUT_KEY_STATE_RELEASED && seat_key_count != 0)) return; timespec_from_usec(&time, libinput_event_keyboard_get_time_usec(keyboard_event)); notify_key(device->seat, &time, libinput_event_keyboard_get_key(keyboard_event), key_state, STATE_UPDATE_AUTOMATIC); } static bool handle_pointer_motion(struct libinput_device *libinput_device, struct libinput_event_pointer *pointer_event) { struct evdev_device *device = libinput_device_get_user_data(libinput_device); struct weston_pointer_motion_event event = { 0 }; struct timespec time; double dx_unaccel, dy_unaccel; timespec_from_usec(&time, libinput_event_pointer_get_time_usec(pointer_event)); dx_unaccel = libinput_event_pointer_get_dx_unaccelerated(pointer_event); dy_unaccel = libinput_event_pointer_get_dy_unaccelerated(pointer_event); event = (struct weston_pointer_motion_event) { .mask = WESTON_POINTER_MOTION_REL | WESTON_POINTER_MOTION_REL_UNACCEL, .time = time, .dx = libinput_event_pointer_get_dx(pointer_event), .dy = libinput_event_pointer_get_dy(pointer_event), .dx_unaccel = dx_unaccel, .dy_unaccel = dy_unaccel, }; notify_motion(device->seat, &time, &event); return true; } static bool handle_pointer_motion_absolute( struct libinput_device *libinput_device, struct libinput_event_pointer *pointer_event) { struct evdev_device *device = libinput_device_get_user_data(libinput_device); struct weston_output *output = device->output; struct timespec time; double x, y; uint32_t width, height; if (!output) return false; timespec_from_usec(&time, libinput_event_pointer_get_time_usec(pointer_event)); width = device->output->current_mode->width; height = device->output->current_mode->height; x = libinput_event_pointer_get_absolute_x_transformed(pointer_event, width); y = libinput_event_pointer_get_absolute_y_transformed(pointer_event, height); weston_output_transform_coordinate(device->output, x, y, &x, &y); notify_motion_absolute(device->seat, &time, x, y); return true; } static bool handle_pointer_button(struct libinput_device *libinput_device, struct libinput_event_pointer *pointer_event) { struct evdev_device *device = libinput_device_get_user_data(libinput_device); int button_state = libinput_event_pointer_get_button_state(pointer_event); int seat_button_count = libinput_event_pointer_get_seat_button_count(pointer_event); struct timespec time; /* Ignore button events that are not seat wide state changes. */ if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED && seat_button_count != 1) || (button_state == LIBINPUT_BUTTON_STATE_RELEASED && seat_button_count != 0)) return false; timespec_from_usec(&time, libinput_event_pointer_get_time_usec(pointer_event)); notify_button(device->seat, &time, libinput_event_pointer_get_button(pointer_event), button_state); return true; } static double normalize_scroll(struct libinput_event_pointer *pointer_event, enum libinput_pointer_axis axis) { enum libinput_pointer_axis_source source; double value = 0.0; source = libinput_event_pointer_get_axis_source(pointer_event); /* libinput < 0.8 sent wheel click events with value 10. Since 0.8 the value is the angle of the click in degrees. To keep backwards-compat with existing clients, we just send multiples of the click count. */ switch (source) { case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: value = 10 * libinput_event_pointer_get_axis_value_discrete( pointer_event, axis); break; case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: value = libinput_event_pointer_get_axis_value(pointer_event, axis); break; default: assert(!"unhandled event source in normalize_scroll"); } return value; } static int32_t get_axis_discrete(struct libinput_event_pointer *pointer_event, enum libinput_pointer_axis axis) { enum libinput_pointer_axis_source source; source = libinput_event_pointer_get_axis_source(pointer_event); if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL) return 0; return libinput_event_pointer_get_axis_value_discrete(pointer_event, axis); } static bool handle_pointer_axis(struct libinput_device *libinput_device, struct libinput_event_pointer *pointer_event) { static int warned; struct evdev_device *device = libinput_device_get_user_data(libinput_device); double vert, horiz; int32_t vert_discrete, horiz_discrete; enum libinput_pointer_axis axis; struct weston_pointer_axis_event weston_event; enum libinput_pointer_axis_source source; uint32_t wl_axis_source; bool has_vert, has_horiz; struct timespec time; has_vert = libinput_event_pointer_has_axis(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); has_horiz = libinput_event_pointer_has_axis(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); if (!has_vert && !has_horiz) return false; source = libinput_event_pointer_get_axis_source(pointer_event); switch (source) { case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL; break; case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER; break; case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS; break; default: if (warned < 5) { weston_log("Unknown scroll source %d.\n", source); warned++; } return false; } notify_axis_source(device->seat, wl_axis_source); timespec_from_usec(&time, libinput_event_pointer_get_time_usec(pointer_event)); if (has_vert) { axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL; vert_discrete = get_axis_discrete(pointer_event, axis); vert = normalize_scroll(pointer_event, axis); weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL; weston_event.value = vert; weston_event.discrete = vert_discrete; weston_event.has_discrete = (vert_discrete != 0); notify_axis(device->seat, &time, &weston_event); } if (has_horiz) { axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL; horiz_discrete = get_axis_discrete(pointer_event, axis); horiz = normalize_scroll(pointer_event, axis); weston_event.axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL; weston_event.value = horiz; weston_event.discrete = horiz_discrete; weston_event.has_discrete = (horiz_discrete != 0); notify_axis(device->seat, &time, &weston_event); } return true; } static void handle_touch_with_coords(struct libinput_device *libinput_device, struct libinput_event_touch *touch_event, int touch_type) { struct evdev_device *device = libinput_device_get_user_data(libinput_device); double x; double y; uint32_t width, height; struct timespec time; int32_t slot; if (!device->output) return; timespec_from_usec(&time, libinput_event_touch_get_time_usec(touch_event)); slot = libinput_event_touch_get_seat_slot(touch_event); width = device->output->current_mode->width; height = device->output->current_mode->height; x = libinput_event_touch_get_x_transformed(touch_event, width); y = libinput_event_touch_get_y_transformed(touch_event, height); weston_output_transform_coordinate(device->output, x, y, &x, &y); notify_touch(device->seat, &time, slot, x, y, touch_type); } static void handle_touch_down(struct libinput_device *device, struct libinput_event_touch *touch_event) { handle_touch_with_coords(device, touch_event, WL_TOUCH_DOWN); } static void handle_touch_motion(struct libinput_device *device, struct libinput_event_touch *touch_event) { handle_touch_with_coords(device, touch_event, WL_TOUCH_MOTION); } static void handle_touch_up(struct libinput_device *libinput_device, struct libinput_event_touch *touch_event) { struct evdev_device *device = libinput_device_get_user_data(libinput_device); struct timespec time; int32_t slot = libinput_event_touch_get_seat_slot(touch_event); timespec_from_usec(&time, libinput_event_touch_get_time_usec(touch_event)); notify_touch(device->seat, &time, slot, 0, 0, WL_TOUCH_UP); } static void handle_touch_frame(struct libinput_device *libinput_device, struct libinput_event_touch *touch_event) { struct evdev_device *device = libinput_device_get_user_data(libinput_device); struct weston_seat *seat = device->seat; notify_touch_frame(seat); } int evdev_device_process_event(struct libinput_event *event) { struct libinput_device *libinput_device = libinput_event_get_device(event); struct evdev_device *device = libinput_device_get_user_data(libinput_device); int handled = 1; bool need_frame = false; switch (libinput_event_get_type(event)) { case LIBINPUT_EVENT_KEYBOARD_KEY: handle_keyboard_key(libinput_device, libinput_event_get_keyboard_event(event)); break; case LIBINPUT_EVENT_POINTER_MOTION: need_frame = handle_pointer_motion(libinput_device, libinput_event_get_pointer_event(event)); break; case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: need_frame = handle_pointer_motion_absolute( libinput_device, libinput_event_get_pointer_event(event)); break; case LIBINPUT_EVENT_POINTER_BUTTON: need_frame = handle_pointer_button(libinput_device, libinput_event_get_pointer_event(event)); break; case LIBINPUT_EVENT_POINTER_AXIS: need_frame = handle_pointer_axis( libinput_device, libinput_event_get_pointer_event(event)); break; case LIBINPUT_EVENT_TOUCH_DOWN: handle_touch_down(libinput_device, libinput_event_get_touch_event(event)); break; case LIBINPUT_EVENT_TOUCH_MOTION: handle_touch_motion(libinput_device, libinput_event_get_touch_event(event)); break; case LIBINPUT_EVENT_TOUCH_UP: handle_touch_up(libinput_device, libinput_event_get_touch_event(event)); break; case LIBINPUT_EVENT_TOUCH_FRAME: handle_touch_frame(libinput_device, libinput_event_get_touch_event(event)); break; default: handled = 0; weston_log("unknown libinput event %d\n", libinput_event_get_type(event)); } if (need_frame) notify_pointer_frame(device->seat); return handled; } static void notify_output_destroy(struct wl_listener *listener, void *data) { struct evdev_device *device = container_of(listener, struct evdev_device, output_destroy_listener); struct weston_compositor *c = device->seat->compositor; struct weston_output *output; if (!device->output_name && !wl_list_empty(&c->output_list)) { output = container_of(c->output_list.next, struct weston_output, link); evdev_device_set_output(device, output); } else { device->output = NULL; } } /** * The WL_CALIBRATION property requires a pixel-specific matrix to be * applied after scaling device coordinates to screen coordinates. libinput * can't do that, so we need to convert the calibration to the normalized * format libinput expects. */ void evdev_device_set_calibration(struct evdev_device *device) { struct udev *udev; struct udev_device *udev_device = NULL; const char *sysname = libinput_device_get_sysname(device->device); const char *calibration_values; uint32_t width, height; float calibration[6]; enum libinput_config_status status; if (!device->output) return; width = device->output->width; height = device->output->height; if (width == 0 || height == 0) return; /* If libinput has a pre-set calibration matrix, don't override it */ if (!libinput_device_config_calibration_has_matrix(device->device) || libinput_device_config_calibration_get_default_matrix( device->device, calibration) != 0) return; udev = udev_new(); if (!udev) return; udev_device = udev_device_new_from_subsystem_sysname(udev, "input", sysname); if (!udev_device) goto out; calibration_values = udev_device_get_property_value(udev_device, "WL_CALIBRATION"); if (!calibration_values || sscanf(calibration_values, "%f %f %f %f %f %f", &calibration[0], &calibration[1], &calibration[2], &calibration[3], &calibration[4], &calibration[5]) != 6) goto out; weston_log("Applying calibration: %f %f %f %f %f %f " "(normalized %f %f)\n", calibration[0], calibration[1], calibration[2], calibration[3], calibration[4], calibration[5], calibration[2] / width, calibration[5] / height); /* normalize to a format libinput can use. There is a chance of this being wrong if the width/height don't match the device width/height but I'm not sure how to fix that */ calibration[2] /= width; calibration[5] /= height; status = libinput_device_config_calibration_set_matrix(device->device, calibration); if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) weston_log("Failed to apply calibration.\n"); out: if (udev_device) udev_device_unref(udev_device); udev_unref(udev); } void evdev_device_set_output(struct evdev_device *device, struct weston_output *output) { if (device->output_destroy_listener.notify) { wl_list_remove(&device->output_destroy_listener.link); device->output_destroy_listener.notify = NULL; } device->output = output; device->output_destroy_listener.notify = notify_output_destroy; wl_signal_add(&output->destroy_signal, &device->output_destroy_listener); evdev_device_set_calibration(device); } struct evdev_device * evdev_device_create(struct libinput_device *libinput_device, struct weston_seat *seat) { struct evdev_device *device; device = zalloc(sizeof *device); if (device == NULL) return NULL; device->seat = seat; wl_list_init(&device->link); device->device = libinput_device; if (libinput_device_has_capability(libinput_device, LIBINPUT_DEVICE_CAP_KEYBOARD)) { weston_seat_init_keyboard(seat, NULL); device->seat_caps |= EVDEV_SEAT_KEYBOARD; } if (libinput_device_has_capability(libinput_device, LIBINPUT_DEVICE_CAP_POINTER)) { weston_seat_init_pointer(seat); device->seat_caps |= EVDEV_SEAT_POINTER; } if (libinput_device_has_capability(libinput_device, LIBINPUT_DEVICE_CAP_TOUCH)) { weston_seat_init_touch(seat); device->seat_caps |= EVDEV_SEAT_TOUCH; } libinput_device_set_user_data(libinput_device, device); libinput_device_ref(libinput_device); return device; } void evdev_device_destroy(struct evdev_device *device) { if (device->seat_caps & EVDEV_SEAT_POINTER) weston_seat_release_pointer(device->seat); if (device->seat_caps & EVDEV_SEAT_KEYBOARD) weston_seat_release_keyboard(device->seat); if (device->seat_caps & EVDEV_SEAT_TOUCH) weston_seat_release_touch(device->seat); if (device->output) wl_list_remove(&device->output_destroy_listener.link); wl_list_remove(&device->link); libinput_device_unref(device->device); free(device->devnode); free(device->output_name); free(device); } void evdev_notify_keyboard_focus(struct weston_seat *seat, struct wl_list *evdev_devices) { struct wl_array keys; if (seat->keyboard_device_count == 0) return; wl_array_init(&keys); notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC); wl_array_release(&keys); }