/* * Copyright © 2010 Intel Corporation * Copyright © 2013 Jonas Ådahl * Copyright 2017-2018 Collabora, Ltd. * Copyright 2017-2018 General Electric Company * * 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 #include "backend.h" #include "libweston-internal.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 ensure_pointer_capability(struct libinput_device *libinput_device) { struct evdev_device *device = libinput_device_get_user_data(libinput_device); struct weston_seat *seat = device->seat; if (!libinput_device_has_capability(libinput_device, LIBINPUT_DEVICE_CAP_POINTER)) return; if (!(device->seat_caps & EVDEV_SEAT_POINTER)) { weston_seat_init_pointer(seat); device->seat_caps |= EVDEV_SEAT_POINTER; } } 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; ensure_pointer_capability(libinput_device); 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, }; event.rel = weston_coord(libinput_event_pointer_get_dx(pointer_event), libinput_event_pointer_get_dy(pointer_event)); event.rel_unaccel = weston_coord(dx_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 weston_coord_global pos; struct timespec time; double x, y; uint32_t width, height; ensure_pointer_capability(libinput_device); 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); pos = weston_coord_global_from_output_point(x, y, output); notify_motion_absolute(device->seat, &time, pos); 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; ensure_pointer_capability(libinput_device); /* 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) { 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; ensure_pointer_capability(libinput_device); 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: weston_log_paced(&device->unknown_scroll_pacer, 5, 0, "Unknown scroll source %d.\n", source); 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 struct weston_output * touch_get_output(struct weston_touch_device *device) { struct evdev_device *evdev_device = device->backend_data; return evdev_device->output; } static const char * touch_get_calibration_head_name(struct weston_touch_device *device) { struct evdev_device *evdev_device = device->backend_data; struct weston_output *output = evdev_device->output; struct weston_head *head; if (!output) return NULL; assert(output->enabled); if (evdev_device->output_name) return evdev_device->output_name; /* No specific head was configured, so the association was made by * the default rule. Just grab whatever head's name. */ wl_list_for_each(head, &output->head_list, output_link) return head->name; assert(0); return NULL; } static void touch_get_calibration(struct weston_touch_device *device, struct weston_touch_device_matrix *cal) { struct evdev_device *evdev_device = device->backend_data; libinput_device_config_calibration_get_matrix(evdev_device->device, cal->m); } static void do_set_calibration(struct evdev_device *evdev_device, const struct weston_touch_device_matrix *cal) { enum libinput_config_status status; weston_log("input device %s: applying calibration:\n", libinput_device_get_sysname(evdev_device->device)); weston_log_continue(STAMP_SPACE " %f %f %f\n", cal->m[0], cal->m[1], cal->m[2]); weston_log_continue(STAMP_SPACE " %f %f %f\n", cal->m[3], cal->m[4], cal->m[5]); status = libinput_device_config_calibration_set_matrix(evdev_device->device, cal->m); if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) weston_log("Error: Failed to apply calibration.\n"); } static void touch_set_calibration(struct weston_touch_device *device, const struct weston_touch_device_matrix *cal) { struct evdev_device *evdev_device = device->backend_data; /* Stop output hotplug from reloading the WL_CALIBRATION values. * libinput will maintain the latest calibration for us. */ evdev_device->override_wl_calibration = true; do_set_calibration(evdev_device, cal); } static const struct weston_touch_device_ops touch_calibration_ops = { .get_output = touch_get_output, .get_calibration_head_name = touch_get_calibration_head_name, .get_calibration = touch_get_calibration, .set_calibration = touch_set_calibration }; static struct weston_touch_device * create_touch_device(struct evdev_device *device) { const struct weston_touch_device_ops *ops = NULL; struct weston_touch_device *touch_device; struct udev_device *udev_device; if (libinput_device_config_calibration_has_matrix(device->device)) ops = &touch_calibration_ops; udev_device = libinput_device_get_udev_device(device->device); if (!udev_device) return NULL; touch_device = weston_touch_create_touch_device(device->seat->touch_state, udev_device_get_syspath(udev_device), device, ops); udev_device_unref(udev_device); if (!touch_device) return NULL; weston_log("Touchscreen - %s - %s\n", libinput_device_get_name(device->device), touch_device->syspath); return touch_device; } 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; struct weston_point2d_device_normalized norm; uint32_t width, height; struct timespec time; int32_t slot; struct weston_coord_global pos; 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); pos = weston_coord_global_from_output_point(x, y, device->output); if (weston_touch_device_can_calibrate(device->touch_device)) { norm.x = libinput_event_touch_get_x_transformed(touch_event, 1); norm.y = libinput_event_touch_get_y_transformed(touch_event, 1); notify_touch_normalized(device->touch_device, &time, slot, &pos, &norm, touch_type); } else { notify_touch(device->touch_device, &time, slot, &pos, 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->touch_device, &time, slot, NULL, 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); notify_touch_frame(device->touch_device); } 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; if (!device) return 0; 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); evdev_device_set_output(device, 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; struct weston_touch_device_matrix calibration; if (!libinput_device_config_calibration_has_matrix(device->device)) return; /* If LIBINPUT_CALIBRATION_MATRIX was set to non-identity, we will not * override it with WL_CALIBRATION. It also means we don't need an * output to load a calibration. */ if (libinput_device_config_calibration_get_default_matrix( device->device, calibration.m) != 0) return; /* touch_set_calibration() has updated the values, do not load old * values from WL_CALIBRATION. */ if (device->override_wl_calibration) return; if (!device->output) { weston_log("input device %s has no enabled output associated " "(%s named), skipping calibration for now.\n", sysname, device->output_name ?: "none"); return; } width = device->output->width; height = device->output->height; if (width == 0 || height == 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) { weston_log("Warning: input device %s has WL_CALIBRATION property set. " "Support for it will be removed in the future. " "Please use LIBINPUT_CALIBRATION_MATRIX instead.\n", sysname); } if (!calibration_values || sscanf(calibration_values, "%f %f %f %f %f %f", &calibration.m[0], &calibration.m[1], &calibration.m[2], &calibration.m[3], &calibration.m[4], &calibration.m[5]) != 6) goto out; /* 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.m[2] /= width; calibration.m[5] /= height; do_set_calibration(device, &calibration); weston_log_continue(STAMP_SPACE " raw translation %f %f for output %s\n", calibration.m[2] * width, calibration.m[5] * height, device->output->name); 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 == output) return; if (device->output_destroy_listener.notify) { wl_list_remove(&device->output_destroy_listener.link); device->output_destroy_listener.notify = NULL; } if (!output) { weston_log("output for input device %s removed\n", libinput_device_get_sysname(device->device)); device->output = NULL; return; } weston_log("associating input device %s with output %s " "(%s by udev)\n", libinput_device_get_sysname(device->device), output->name, device->output_name ?: "none"); 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)) { if (weston_seat_init_keyboard(seat, NULL) < 0) { free(device); return NULL; } device->seat_caps |= EVDEV_SEAT_KEYBOARD; } if (libinput_device_has_capability(libinput_device, LIBINPUT_DEVICE_CAP_TOUCH)) { if (weston_seat_init_touch(seat) < 0) { /* maybe we're a keyboard + touch device thus we need * to release the keyboard in case we couldn't make use * of the touch */ if (device->seat_caps & EVDEV_SEAT_KEYBOARD) weston_seat_release_keyboard(seat); free(device); return NULL; } device->seat_caps |= EVDEV_SEAT_TOUCH; device->touch_device = create_touch_device(device); } 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_touch_device_destroy(device->touch_device); 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->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); }