diff --git a/libweston/compositor-wayland.c b/libweston/compositor-wayland.c index 56ed474a..5ed90c0f 100644 --- a/libweston/compositor-wayland.c +++ b/libweston/compositor-wayland.c @@ -198,6 +198,8 @@ struct wayland_input { } cursor; } parent; + struct weston_touch_device *touch_device; + enum weston_key_state_update keyboard_state_update; uint32_t key_serial; uint32_t enter_serial; @@ -2248,6 +2250,22 @@ static const struct wl_touch_listener touch_listener = { }; +static struct weston_touch_device * +create_touch_device(struct wayland_input *input) +{ + struct weston_touch_device *touch_device; + char str[128]; + + /* manufacture a unique'ish name */ + snprintf(str, sizeof str, "wayland-touch[%u]", + wl_proxy_get_id((struct wl_proxy *)input->parent.seat)); + + touch_device = weston_touch_create_touch_device(input->base.touch_state, + str, NULL, NULL); + + return touch_device; +} + static void input_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps) @@ -2289,7 +2307,10 @@ input_handle_capabilities(void *data, struct wl_seat *seat, wl_touch_add_listener(input->parent.touch, &touch_listener, input); weston_seat_init_touch(&input->base); + input->touch_device = create_touch_device(input); } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->parent.touch) { + weston_touch_device_destroy(input->touch_device); + input->touch_device = NULL; if (input->seat_version >= WL_TOUCH_RELEASE_SINCE_VERSION) wl_touch_release(input->parent.touch); else diff --git a/libweston/compositor.h b/libweston/compositor.h index 47337d8a..7f3fdb95 100644 --- a/libweston/compositor.h +++ b/libweston/compositor.h @@ -454,10 +454,54 @@ struct weston_pointer { struct wl_list timestamps_list; }; +/** libinput style calibration matrix + * + * See https://wayland.freedesktop.org/libinput/doc/latest/absolute_axes.html + * and libinput_device_config_calibration_set_matrix(). + */ +struct weston_touch_device_matrix { + float m[6]; +}; + +struct weston_touch_device; + +/** Operations for a calibratable touchscreen */ +struct weston_touch_device_ops { + /** Get the associated output if existing. */ + struct weston_output *(*get_output)(struct weston_touch_device *device); + + /** Get the name of the associated head if existing. */ + const char * + (*get_calibration_head_name)(struct weston_touch_device *device); + + /** Retrieve the current calibration matrix. */ + void (*get_calibration)(struct weston_touch_device *device, + struct weston_touch_device_matrix *cal); + + /** Set a new calibration matrix. */ + void (*set_calibration)(struct weston_touch_device *device, + const struct weston_touch_device_matrix *cal); +}; + +/** Represents a physical touchscreen input device */ +struct weston_touch_device { + char *syspath; /**< unique name */ + struct weston_touch *aggregate; /**< weston_touch this is part of */ + struct wl_list link; /**< in weston_touch::device_list */ + struct wl_signal destroy_signal; /**< destroy notifier */ + + void *backend_data; /**< backend-specific private */ + + const struct weston_touch_device_ops *ops; +}; + +/** Represents a set of touchscreen devices aggregated under a seat */ struct weston_touch { struct weston_seat *seat; + struct wl_list device_list; /* struct weston_touch_device::link */ + struct wl_list resource_list; struct wl_list focus_resource_list; struct weston_view *focus; @@ -579,6 +623,18 @@ weston_touch_send_motion(struct weston_touch *touch, void weston_touch_send_frame(struct weston_touch *touch); +struct weston_touch_device * +weston_touch_create_touch_device(struct weston_touch *touch, + const char *syspath, + void *backend_data, + const struct weston_touch_device_ops *ops); + +void +weston_touch_device_destroy(struct weston_touch_device *device); + +bool +weston_touch_device_can_calibrate(struct weston_touch_device *device); + void wl_data_device_set_keyboard_focus(struct weston_seat *seat); diff --git a/libweston/input.c b/libweston/input.c index 833ce22e..acb564e0 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -1,5 +1,7 @@ /* * Copyright © 2013 Intel Corporation + * 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 @@ -123,6 +125,68 @@ remove_input_resource_from_timestamps(struct wl_resource *input_resource, } } +/** Register a touchscreen input device + * + * \param touch The parent weston_touch that identifies the seat. + * \param syspath Unique device name. + * \param backend_data Backend private data if necessary. + * \param ops Calibration operations, or NULL for not able to run calibration. + * \return New touch device, or NULL on failure. + */ +WL_EXPORT struct weston_touch_device * +weston_touch_create_touch_device(struct weston_touch *touch, + const char *syspath, + void *backend_data, + const struct weston_touch_device_ops *ops) +{ + struct weston_touch_device *device; + + assert(syspath); + if (ops) { + assert(ops->get_output); + assert(ops->get_calibration_head_name); + assert(ops->get_calibration); + assert(ops->set_calibration); + } + + device = zalloc(sizeof *device); + if (!device) + return NULL; + + wl_signal_init(&device->destroy_signal); + + device->syspath = strdup(syspath); + if (!device->syspath) { + free(device); + return NULL; + } + + device->backend_data = backend_data; + device->ops = ops; + + device->aggregate = touch; + wl_list_insert(touch->device_list.prev, &device->link); + + return device; +} + +/** Destroy the touch device. */ +WL_EXPORT void +weston_touch_device_destroy(struct weston_touch_device *device) +{ + wl_list_remove(&device->link); + wl_signal_emit(&device->destroy_signal, device); + free(device->syspath); + free(device); +} + +/** Is it possible to run calibration on this touch device? */ +WL_EXPORT bool +weston_touch_device_can_calibrate(struct weston_touch_device *device) +{ + return !!device->ops; +} + static struct weston_pointer_client * weston_pointer_client_create(struct wl_client *client) { @@ -1277,6 +1341,7 @@ weston_touch_create(void) if (touch == NULL) return NULL; + wl_list_init(&touch->device_list); wl_list_init(&touch->resource_list); wl_list_init(&touch->focus_resource_list); wl_list_init(&touch->focus_view_listener.link); @@ -1297,6 +1362,8 @@ weston_touch_destroy(struct weston_touch *touch) { struct wl_resource *resource; + assert(wl_list_empty(&touch->device_list)); + wl_resource_for_each(resource, &touch->resource_list) { wl_resource_set_user_data(resource, NULL); } diff --git a/libweston/libinput-device.c b/libweston/libinput-device.c index e1738613..63f50319 100644 --- a/libweston/libinput-device.c +++ b/libweston/libinput-device.c @@ -1,6 +1,8 @@ /* * 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 @@ -296,6 +298,111 @@ handle_pointer_axis(struct libinput_device *libinput_device, 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; + + status = libinput_device_config_calibration_set_matrix(evdev_device->device, + cal->m); + if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) + weston_log("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, @@ -466,6 +573,12 @@ evdev_device_set_calibration(struct evdev_device *device) calibration) != 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", @@ -598,6 +711,7 @@ evdev_device_create(struct libinput_device *libinput_device, LIBINPUT_DEVICE_CAP_TOUCH)) { weston_seat_init_touch(seat); device->seat_caps |= EVDEV_SEAT_TOUCH; + device->touch_device = create_touch_device(device); } libinput_device_set_user_data(libinput_device, device); @@ -613,8 +727,10 @@ evdev_device_destroy(struct evdev_device *device) 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) + 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); diff --git a/libweston/libinput-device.h b/libweston/libinput-device.h index 8e9f5608..6147a513 100644 --- a/libweston/libinput-device.h +++ b/libweston/libinput-device.h @@ -31,6 +31,7 @@ #include #include #include +#include #include "compositor.h" @@ -44,11 +45,13 @@ struct evdev_device { struct weston_seat *seat; enum evdev_device_seat_capability seat_caps; struct libinput_device *device; + struct weston_touch_device *touch_device; struct wl_list link; struct weston_output *output; struct wl_listener output_destroy_listener; char *output_name; int fd; + bool override_wl_calibration; }; void diff --git a/tests/weston-test.c b/tests/weston-test.c index ae08b02f..9662b67c 100644 --- a/tests/weston-test.c +++ b/tests/weston-test.c @@ -45,11 +45,15 @@ #include "shared/helpers.h" #include "shared/timespec-util.h" +#define MAX_TOUCH_DEVICES 32 + struct weston_test { struct weston_compositor *compositor; struct weston_layer layer; struct weston_process process; struct weston_seat seat; + struct weston_touch_device *touch_device[MAX_TOUCH_DEVICES]; + int nr_touch_devices; bool is_seat_initialized; }; @@ -77,6 +81,34 @@ test_client_sigchld(struct weston_process *process, int status) wl_display_terminate(test->compositor->wl_display); } +static void +touch_device_add(struct weston_test *test) +{ + char buf[128]; + int i = test->nr_touch_devices; + + assert(i < MAX_TOUCH_DEVICES); + assert(!test->touch_device[i]); + + snprintf(buf, sizeof buf, "test-touch-device-%d", i); + + test->touch_device[i] = weston_touch_create_touch_device( + test->seat.touch_state, buf, NULL, NULL); + test->nr_touch_devices++; +} + +static void +touch_device_remove(struct weston_test *test) +{ + int i = test->nr_touch_devices - 1; + + assert(i >= 0); + assert(test->touch_device[i]); + weston_touch_device_destroy(test->touch_device[i]); + test->touch_device[i] = NULL; + --test->nr_touch_devices; +} + static int test_seat_init(struct weston_test *test) { @@ -92,6 +124,7 @@ test_seat_init(struct weston_test *test) if (weston_seat_init_keyboard(&test->seat, NULL) < 0) return -1; weston_seat_init_touch(&test->seat); + touch_device_add(test); return 0; } @@ -99,6 +132,9 @@ test_seat_init(struct weston_test *test) static void test_seat_release(struct weston_test *test) { + while (test->nr_touch_devices > 0) + touch_device_remove(test); + assert(test->is_seat_initialized && "Trying to release already released test seat"); test->is_seat_initialized = false; @@ -281,6 +317,7 @@ device_release(struct wl_client *client, } else if (strcmp(device, "keyboard") == 0) { weston_seat_release_keyboard(seat); } else if (strcmp(device, "touch") == 0) { + touch_device_remove(test); weston_seat_release_touch(seat); } else if (strcmp(device, "seat") == 0) { test_seat_release(test); @@ -302,6 +339,7 @@ device_add(struct wl_client *client, weston_seat_init_keyboard(seat, NULL); } else if (strcmp(device, "touch") == 0) { weston_seat_init_touch(seat); + touch_device_add(test); } else if (strcmp(device, "seat") == 0) { test_seat_init(test); } else {