blob: 3c76036d6d6870d333b4c9f79ed0660b2565f356 [file] [log] [blame]
Louis-Francis Ratté-Boulianne83630982017-11-28 20:42:47 -05001/*
2 * Copyright 2017-2018 Collabora, Ltd.
3 * Copyright 2017-2018 General Electric Company
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
27#include "config.h"
28
29#include <string.h>
30#include <wayland-server.h>
31
32#include "shared/helpers.h"
33#include "shared/string-helpers.h"
Pekka Paalanenecbdcfd2019-04-04 14:46:00 +030034#include <libweston/zalloc.h>
Louis-Francis Ratté-Boulianne83630982017-11-28 20:42:47 -050035#include "shared/timespec-util.h"
Pekka Paalanen3d5d9472019-03-28 16:28:47 +020036#include <libweston/libweston.h>
Marius Vlad56f3a682019-07-10 14:48:39 +030037#include "libweston-internal.h"
Guillaume Champagnef1e8fc92020-01-27 20:14:29 -050038#include "backend.h"
Louis-Francis Ratté-Boulianne83630982017-11-28 20:42:47 -050039
40#include "weston-touch-calibration-server-protocol.h"
41
42struct weston_touch_calibrator {
43 struct wl_resource *resource;
44
45 struct weston_compositor *compositor;
46
47 struct weston_surface *surface;
48 struct wl_listener surface_destroy_listener;
49 struct wl_listener surface_commit_listener;
50
51 struct weston_touch_device *device;
52 struct wl_listener device_destroy_listener;
53
54 struct weston_output *output;
55 struct wl_listener output_destroy_listener;
56
57 struct weston_view *view;
58
59 /** The calibration procedure has been cancelled. */
60 bool calibration_cancelled;
61
62 /** The current touch sequence has been cancelled. */
63 bool touch_cancelled;
64};
65
66static struct weston_touch_calibrator *
67calibrator_from_device(struct weston_touch_device *device)
68{
69 return device->aggregate->seat->compositor->touch_calibrator;
70}
71
72static uint32_t
73wire_uint_from_double(double c)
74{
75 assert(c >= 0.0);
76 assert(c <= 1.0);
77
78 return round(c * 0xffffffff);
79}
80
81static bool
82normalized_is_valid(const struct weston_point2d_device_normalized *p)
83{
84 return p->x >= 0.0 && p->x <= 1.0 &&
85 p->y >= 0.0 && p->y <= 1.0;
86}
87
88WL_EXPORT void
89notify_touch_calibrator(struct weston_touch_device *device,
90 const struct timespec *time, int32_t slot,
91 const struct weston_point2d_device_normalized *norm,
92 int touch_type)
93{
94 struct weston_touch_calibrator *calibrator;
95 struct wl_resource *res;
96 uint32_t msecs;
97 uint32_t x = 0;
98 uint32_t y = 0;
99
100 calibrator = calibrator_from_device(device);
101 if (!calibrator)
102 return;
103
104 res = calibrator->resource;
105
106 /* Ignore any touch events coming from another device */
107 if (device != calibrator->device) {
108 if (touch_type == WL_TOUCH_DOWN)
109 weston_touch_calibrator_send_invalid_touch(res);
110 return;
111 }
112
113 /* Ignore all events if we have sent 'cancel' event until all
114 * touches (on the seat) are up.
115 */
116 if (calibrator->touch_cancelled) {
117 if (calibrator->device->aggregate->num_tp == 0) {
118 assert(touch_type == WL_TOUCH_UP);
119 calibrator->touch_cancelled = false;
120 }
121 return;
122 }
123
124 msecs = timespec_to_msec(time);
125 if (touch_type != WL_TOUCH_UP) {
126 if (normalized_is_valid(norm)) {
127 x = wire_uint_from_double(norm->x);
128 y = wire_uint_from_double(norm->y);
129 } else {
130 /* Coordinates are out of bounds */
131 if (touch_type == WL_TOUCH_MOTION) {
132 weston_touch_calibrator_send_cancel(res);
133 calibrator->touch_cancelled = true;
134 }
135 weston_touch_calibrator_send_invalid_touch(res);
136 return;
137 }
138 }
139
140 switch (touch_type) {
141 case WL_TOUCH_UP:
142 weston_touch_calibrator_send_up(res, msecs, slot);
143 break;
144 case WL_TOUCH_DOWN:
145 weston_touch_calibrator_send_down(res, msecs, slot, x, y);
146 break;
147 case WL_TOUCH_MOTION:
148 weston_touch_calibrator_send_motion(res, msecs, slot, x, y);
149 break;
150 default:
151 return;
152 }
153}
154
155WL_EXPORT void
156notify_touch_calibrator_frame(struct weston_touch_device *device)
157{
158 struct weston_touch_calibrator *calibrator;
159
160 calibrator = calibrator_from_device(device);
161 if (!calibrator)
162 return;
163
164 weston_touch_calibrator_send_frame(calibrator->resource);
165}
166
167WL_EXPORT void
168notify_touch_calibrator_cancel(struct weston_touch_device *device)
169{
170 struct weston_touch_calibrator *calibrator;
171
172 calibrator = calibrator_from_device(device);
173 if (!calibrator)
174 return;
175
176 weston_touch_calibrator_send_cancel(calibrator->resource);
177}
178
179static void
180map_calibrator(struct weston_touch_calibrator *calibrator)
181{
182 struct weston_compositor *c = calibrator->compositor;
183 struct weston_touch_device *device = calibrator->device;
184 static const struct weston_touch_device_matrix identity = {
185 .m = { 1, 0, 0, 0, 1, 0}
186 };
187
188 assert(!calibrator->view);
189 assert(calibrator->output);
190 assert(calibrator->surface);
191 assert(calibrator->surface->resource);
192
193 calibrator->view = weston_view_create(calibrator->surface);
194 if (!calibrator->view) {
195 wl_resource_post_no_memory(calibrator->surface->resource);
196 return;
197 }
198
199 weston_layer_entry_insert(&c->calibrator_layer.view_list,
200 &calibrator->view->layer_link);
201
202 weston_view_set_position(calibrator->view,
203 calibrator->output->x,
204 calibrator->output->y);
205 calibrator->view->output = calibrator->surface->output;
206 calibrator->view->is_mapped = true;
207
208 calibrator->surface->output = calibrator->output;
209 calibrator->surface->is_mapped = true;
210
211 weston_output_schedule_repaint(calibrator->output);
212
213 device->ops->get_calibration(device, &device->saved_calibration);
214 device->ops->set_calibration(device, &identity);
215}
216
217static void
218unmap_calibrator(struct weston_touch_calibrator *calibrator)
219{
220 struct weston_touch_device *device = calibrator->device;
221
222 wl_list_remove(&calibrator->surface_commit_listener.link);
223 wl_list_init(&calibrator->surface_commit_listener.link);
224
225 if (!calibrator->view)
226 return;
227
228 weston_view_destroy(calibrator->view);
229 calibrator->view = NULL;
230 weston_surface_unmap(calibrator->surface);
231
232 /* Reload saved calibration */
233 if (device)
234 device->ops->set_calibration(device,
235 &device->saved_calibration);
236}
237
238void
239touch_calibrator_mode_changed(struct weston_compositor *compositor)
240{
241 struct weston_touch_calibrator *calibrator;
242
243 calibrator = compositor->touch_calibrator;
244 if (!calibrator)
245 return;
246
247 if (calibrator->calibration_cancelled)
248 return;
249
250 if (compositor->touch_mode == WESTON_TOUCH_MODE_CALIB)
251 map_calibrator(calibrator);
252}
253
254static void
255touch_calibrator_surface_committed(struct wl_listener *listener, void *data)
256{
257 struct weston_touch_calibrator *calibrator =
258 container_of(listener, struct weston_touch_calibrator,
259 surface_commit_listener);
260 struct weston_surface *surface = calibrator->surface;
261
262 wl_list_remove(&calibrator->surface_commit_listener.link);
263 wl_list_init(&calibrator->surface_commit_listener.link);
264
265 if (surface->width != calibrator->output->width ||
266 surface->height != calibrator->output->height) {
267 wl_resource_post_error(calibrator->resource,
268 WESTON_TOUCH_CALIBRATOR_ERROR_BAD_SIZE,
269 "calibrator surface size does not match");
270 return;
271 }
272
273 weston_compositor_set_touch_mode_calib(calibrator->compositor);
274 /* results in call to touch_calibrator_mode_changed() */
275}
276
277static void
278touch_calibrator_convert(struct wl_client *client,
279 struct wl_resource *resource,
280 int32_t x,
281 int32_t y,
282 uint32_t coordinate_id)
283{
284 struct weston_touch_calibrator *calibrator;
285 struct wl_resource *coordinate_resource;
286 struct weston_output *output;
287 struct weston_surface *surface;
288 uint32_t version;
289 struct weston_vector p = { { 0.0, 0.0, 0.0, 1.0 } };
290 struct weston_point2d_device_normalized norm;
291
292 version = wl_resource_get_version(resource);
293 calibrator = wl_resource_get_user_data(resource);
294 surface = calibrator->surface;
295 output = calibrator->output;
296
297 coordinate_resource =
298 wl_resource_create(client, &weston_touch_coordinate_interface,
299 version, coordinate_id);
300 if (!coordinate_resource) {
301 wl_client_post_no_memory(client);
302 return;
303 }
304
305 if (calibrator->calibration_cancelled) {
306 weston_touch_coordinate_send_result(coordinate_resource, 0, 0);
307 wl_resource_destroy(coordinate_resource);
308 return;
309 }
310
311 if (!surface || !surface->is_mapped) {
312 wl_resource_post_error(resource,
313 WESTON_TOUCH_CALIBRATOR_ERROR_NOT_MAPPED,
314 "calibrator surface is not mapped");
315 return;
316 }
317 assert(calibrator->view);
318 assert(output);
319
320 if (x < 0 || y < 0 || x >= surface->width || y >= surface->height) {
321 wl_resource_post_error(resource,
322 WESTON_TOUCH_CALIBRATOR_ERROR_BAD_COORDINATES,
323 "convert(%d, %d) input is out of bounds",
324 x, y);
325 return;
326 }
327
328 /* Convert from surface-local coordinates into global, from global
329 * into output-raw, do perspective division and normalize.
330 */
331 weston_view_to_global_float(calibrator->view, x, y, &p.f[0], &p.f[1]);
332 weston_matrix_transform(&output->matrix, &p);
333 norm.x = p.f[0] / (p.f[3] * output->current_mode->width);
334 norm.y = p.f[1] / (p.f[3] * output->current_mode->height);
335
336 if (!normalized_is_valid(&norm)) {
337 wl_resource_post_error(resource,
338 WESTON_TOUCH_CALIBRATOR_ERROR_BAD_COORDINATES,
339 "convert(%d, %d) output is out of bounds",
340 x, y);
341 return;
342 }
343
344 weston_touch_coordinate_send_result(coordinate_resource,
345 wire_uint_from_double(norm.x),
346 wire_uint_from_double(norm.y));
347 wl_resource_destroy(coordinate_resource);
348}
349
350static void
351destroy_touch_calibrator(struct wl_resource *resource)
352{
353 struct weston_touch_calibrator *calibrator;
354
355 calibrator = wl_resource_get_user_data(resource);
356
357 calibrator->compositor->touch_calibrator = NULL;
358
359 weston_compositor_set_touch_mode_normal(calibrator->compositor);
360
361 if (calibrator->surface) {
362 unmap_calibrator(calibrator);
363 weston_surface_set_role(calibrator->surface, NULL,
364 calibrator->surface->resource, 0);
365 wl_list_remove(&calibrator->surface_destroy_listener.link);
366 wl_list_remove(&calibrator->surface_commit_listener.link);
367 }
368
369 if (calibrator->device)
370 wl_list_remove(&calibrator->device_destroy_listener.link);
371
372 if (calibrator->output)
373 wl_list_remove(&calibrator->output_destroy_listener.link);
374
375 free(calibrator);
376}
377
378static void
379touch_calibrator_destroy(struct wl_client *client,
380 struct wl_resource *resource)
381{
382 wl_resource_destroy(resource);
383}
384
385static const struct weston_touch_calibrator_interface
386touch_calibrator_implementation = {
387 touch_calibrator_destroy,
388 touch_calibrator_convert
389};
390
391static void
392touch_calibrator_cancel_calibration(struct weston_touch_calibrator *calibrator)
393{
394 weston_touch_calibrator_send_cancel_calibration(calibrator->resource);
395 calibrator->calibration_cancelled = true;
396
397 if (calibrator->surface)
398 unmap_calibrator(calibrator);
399}
400
401static void
402touch_calibrator_output_destroyed(struct wl_listener *listener, void *data)
403{
404 struct weston_touch_calibrator *calibrator =
405 container_of(listener, struct weston_touch_calibrator,
406 output_destroy_listener);
407
408 assert(calibrator->output == data);
409 calibrator->output = NULL;
410
411 touch_calibrator_cancel_calibration(calibrator);
412}
413
414static void
415touch_calibrator_device_destroyed(struct wl_listener *listener, void *data)
416{
417 struct weston_touch_calibrator *calibrator =
418 container_of(listener, struct weston_touch_calibrator,
419 device_destroy_listener);
420
421 assert(calibrator->device == data);
422 calibrator->device = NULL;
423
424 touch_calibrator_cancel_calibration(calibrator);
425}
426
427static void
428touch_calibrator_surface_destroyed(struct wl_listener *listener, void *data)
429{
430 struct weston_touch_calibrator *calibrator =
431 container_of(listener, struct weston_touch_calibrator,
432 surface_destroy_listener);
433
434 assert(calibrator->surface->resource == data);
435
436 unmap_calibrator(calibrator);
437 calibrator->surface = NULL;
438}
439
440static void
441touch_calibration_destroy(struct wl_client *client,
442 struct wl_resource *resource)
443{
444 wl_resource_destroy(resource);
445}
446
447static struct weston_touch_device *
448weston_compositor_find_touch_device_by_syspath(
449 struct weston_compositor *compositor,
450 const char *syspath)
451{
452 struct weston_seat *seat;
453 struct weston_touch *touch;
454 struct weston_touch_device *device;
455
456 if (!syspath)
457 return NULL;
458
459 wl_list_for_each(seat, &compositor->seat_list, link) {
460 touch = weston_seat_get_touch(seat);
461 if (!touch)
462 continue;
463
464 wl_list_for_each(device, &touch->device_list, link) {
465 if (strcmp(device->syspath, syspath) == 0)
466 return device;
467 }
468 }
469
470 return NULL;
471}
472
473static void
474touch_calibration_create_calibrator(
475 struct wl_client *client,
476 struct wl_resource *touch_calibration_resource,
477 struct wl_resource *surface_resource,
478 const char *syspath,
479 uint32_t calibrator_id)
480{
481 struct weston_compositor *compositor;
482 struct weston_touch_calibrator *calibrator;
483 struct weston_touch_device *device;
484 struct weston_output *output = NULL;
485 struct weston_surface *surface;
486 uint32_t version;
487 int ret;
488
489 version = wl_resource_get_version(touch_calibration_resource);
490 compositor = wl_resource_get_user_data(touch_calibration_resource);
491
492 if (compositor->touch_calibrator != NULL) {
493 wl_resource_post_error(touch_calibration_resource,
494 WESTON_TOUCH_CALIBRATION_ERROR_ALREADY_EXISTS,
495 "a calibrator has already been created");
496 return;
497 }
498
499 calibrator = zalloc(sizeof *calibrator);
500 if (!calibrator) {
501 wl_client_post_no_memory(client);
502 return;
503 }
504
505 calibrator->compositor = compositor;
506 calibrator->resource = wl_resource_create(client,
507 &weston_touch_calibrator_interface,
508 version, calibrator_id);
509 if (!calibrator->resource) {
510 wl_client_post_no_memory(client);
511 goto err_dealloc;
512 }
513
514 surface = wl_resource_get_user_data(surface_resource);
515 assert(surface);
516 ret = weston_surface_set_role(surface, "weston_touch_calibrator",
517 touch_calibration_resource,
518 WESTON_TOUCH_CALIBRATION_ERROR_INVALID_SURFACE);
519 if (ret < 0)
520 goto err_destroy_resource;
521
522 calibrator->surface_destroy_listener.notify =
523 touch_calibrator_surface_destroyed;
524 wl_resource_add_destroy_listener(surface->resource,
525 &calibrator->surface_destroy_listener);
526 calibrator->surface = surface;
527
528 calibrator->surface_commit_listener.notify =
529 touch_calibrator_surface_committed;
530 wl_signal_add(&surface->commit_signal,
531 &calibrator->surface_commit_listener);
532
533 device = weston_compositor_find_touch_device_by_syspath(compositor,
534 syspath);
535 if (device) {
536 output = device->ops->get_output(device);
537 if (weston_touch_device_can_calibrate(device) && output)
538 calibrator->device = device;
539 }
540
541 if (!calibrator->device) {
542 wl_resource_post_error(touch_calibration_resource,
543 WESTON_TOUCH_CALIBRATION_ERROR_INVALID_DEVICE,
544 "the given touch device '%s' is not valid",
545 syspath ?: "");
546 goto err_unlink_surface;
547 }
548
549 calibrator->device_destroy_listener.notify =
550 touch_calibrator_device_destroyed;
551 wl_signal_add(&calibrator->device->destroy_signal,
552 &calibrator->device_destroy_listener);
553
554 wl_resource_set_implementation(calibrator->resource,
555 &touch_calibrator_implementation,
556 calibrator, destroy_touch_calibrator);
557
558 assert(output);
559 calibrator->output_destroy_listener.notify =
560 touch_calibrator_output_destroyed;
561 wl_signal_add(&output->destroy_signal,
562 &calibrator->output_destroy_listener);
563 calibrator->output = output;
564
565 weston_touch_calibrator_send_configure(calibrator->resource,
566 output->width,
567 output->height);
568
569 compositor->touch_calibrator = calibrator;
570
571 return;
572
573err_unlink_surface:
574 wl_list_remove(&calibrator->surface_commit_listener.link);
575 wl_list_remove(&calibrator->surface_destroy_listener.link);
576
577err_destroy_resource:
578 wl_resource_destroy(calibrator->resource);
579
580err_dealloc:
581 free(calibrator);
582}
583
584static void
585touch_calibration_save(struct wl_client *client,
586 struct wl_resource *touch_calibration_resource,
587 const char *device_name,
588 struct wl_array *matrix_data)
589{
590 struct weston_touch_device *device;
591 struct weston_compositor *compositor;
592 struct weston_touch_device_matrix calibration;
593 struct weston_touch_calibrator *calibrator;
594 int i = 0;
595 float *c;
596
597 compositor = wl_resource_get_user_data(touch_calibration_resource);
598
599 device = weston_compositor_find_touch_device_by_syspath(compositor,
600 device_name);
601 if (!device || !weston_touch_device_can_calibrate(device)) {
602 wl_resource_post_error(touch_calibration_resource,
603 WESTON_TOUCH_CALIBRATION_ERROR_INVALID_DEVICE,
604 "the given device is not valid");
605 return;
606 }
607
608 wl_array_for_each(c, matrix_data) {
609 calibration.m[i++] = *c;
610 }
611
612 /* If calibration can't be saved, don't set it as current */
613 if (compositor->touch_calibration_save &&
614 compositor->touch_calibration_save(compositor, device,
615 &calibration) < 0)
616 return;
617
618 /* If calibrator is still mapped, the compositor will use
619 * saved_calibration when going back to normal touch handling.
620 * Continuing calibrating after save request is undefined. */
621 calibrator = compositor->touch_calibrator;
622 if (calibrator &&
623 calibrator->surface &&
624 weston_surface_is_mapped(calibrator->surface))
625 device->saved_calibration = calibration;
626 else
627 device->ops->set_calibration(device, &calibration);
628}
629
630static const struct weston_touch_calibration_interface
631touch_calibration_implementation = {
632 touch_calibration_destroy,
633 touch_calibration_create_calibrator,
634 touch_calibration_save
635};
636
637static void
638bind_touch_calibration(struct wl_client *client,
639 void *data, uint32_t version, uint32_t id)
640{
641 struct weston_compositor *compositor = data;
642 struct wl_resource *resource;
643 struct weston_touch_device *device;
644 struct weston_seat *seat;
645 struct weston_touch *touch;
646 const char *name;
647
648 resource = wl_resource_create(client,
649 &weston_touch_calibration_interface,
650 version, id);
651 if (resource == NULL) {
652 wl_client_post_no_memory(client);
653 return;
654 }
655
656 wl_resource_set_implementation(resource,
657 &touch_calibration_implementation,
658 compositor, NULL);
659
660 wl_list_for_each(seat, &compositor->seat_list, link) {
661 touch = weston_seat_get_touch(seat);
662 if (!touch)
663 continue;
664
665 wl_list_for_each(device, &touch->device_list, link) {
666 if (!weston_touch_device_can_calibrate(device))
667 continue;
668
669 name = device->ops->get_calibration_head_name(device);
670 if (!name)
671 continue;
672
673 weston_touch_calibration_send_touch_device(resource,
674 device->syspath, name);
675 }
676 }
677}
678
679/** Advertise touch_calibration support
680 *
681 * \param compositor The compositor to init for.
682 * \param save The callback function for saving a new calibration, or NULL.
683 * \return Zero on success, -1 on failure or if already enabled.
684 *
685 * Calling this initializes the weston_touch_calibration protocol support,
686 * so that the interface will be advertised to clients. It is recommended
687 * to use some mechanism, e.g. wl_display_set_global_filter(), to restrict
688 * access to the interface.
689 *
690 * There is no way to disable this once enabled.
691 *
692 * If the save callback is NULL, a new calibration provided by a client will
693 * always be accepted. If the save callback is not NULL, it must return
694 * success for the new calibration to be accepted.
695 */
696WL_EXPORT int
697weston_compositor_enable_touch_calibrator(struct weston_compositor *compositor,
698 weston_touch_calibration_save_func save)
699{
700 if (compositor->touch_calibration)
701 return -1;
702
703 compositor->touch_calibration = wl_global_create(compositor->wl_display,
704 &weston_touch_calibration_interface, 1,
705 compositor, bind_touch_calibration);
706 if (!compositor->touch_calibration)
707 return -1;
708
709 compositor->touch_calibration_save = save;
710 weston_layer_init(&compositor->calibrator_layer, compositor);
711
712 /* needs to be stacked above everything except lock screen and cursor,
713 * otherwise the position value is arbitrary */
714 weston_layer_set_position(&compositor->calibrator_layer,
715 WESTON_LAYER_POSITION_TOP_UI + 120);
716
717 return 0;
718}