1 /*
2 * Copyright © 2012 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26 #include "config.h"
27
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <signal.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <pthread.h>
37
38 #include <libweston/libweston.h>
39 #include <libweston/weston-log.h>
40 #include "backend.h"
41 #include "libweston-internal.h"
42 #include "compositor/weston.h"
43 #include "weston-test-server-protocol.h"
44 #include "weston.h"
45 #include "weston-testsuite-data.h"
46
47 #include "shared/helpers.h"
48 #include "shared/timespec-util.h"
49
50 #define MAX_TOUCH_DEVICES 32
51
52 struct weston_test {
53 struct weston_compositor *compositor;
54 struct wl_listener destroy_listener;
55
56 struct weston_log_scope *log;
57
58 struct weston_layer layer;
59 struct weston_seat seat;
60 struct weston_touch_device *touch_device[MAX_TOUCH_DEVICES];
61 int nr_touch_devices;
62 bool is_seat_initialized;
63
64 pthread_t client_thread;
65 struct wl_event_source *client_source;
66 };
67
68 struct weston_test_surface {
69 struct weston_surface *surface;
70 struct weston_view *view;
71 int32_t x, y;
72 struct weston_test *test;
73 };
74
75 static void
touch_device_add(struct weston_test * test)76 touch_device_add(struct weston_test *test)
77 {
78 char buf[128];
79 int i = test->nr_touch_devices;
80
81 assert(i < MAX_TOUCH_DEVICES);
82 assert(!test->touch_device[i]);
83
84 snprintf(buf, sizeof buf, "test-touch-device-%d", i);
85
86 test->touch_device[i] = weston_touch_create_touch_device(
87 test->seat.touch_state, buf, NULL, NULL);
88 test->nr_touch_devices++;
89 }
90
91 static void
touch_device_remove(struct weston_test * test)92 touch_device_remove(struct weston_test *test)
93 {
94 int i = test->nr_touch_devices - 1;
95
96 assert(i >= 0);
97 assert(test->touch_device[i]);
98 weston_touch_device_destroy(test->touch_device[i]);
99 test->touch_device[i] = NULL;
100 --test->nr_touch_devices;
101 }
102
103 static int
test_seat_init(struct weston_test * test)104 test_seat_init(struct weston_test *test)
105 {
106 assert(!test->is_seat_initialized &&
107 "Trying to add already added test seat");
108
109 /* create our own seat */
110 weston_seat_init(&test->seat, test->compositor, "test-seat");
111 test->is_seat_initialized = true;
112
113 /* add devices */
114 weston_seat_init_pointer(&test->seat);
115 if (weston_seat_init_keyboard(&test->seat, NULL) < 0)
116 return -1;
117 weston_seat_init_touch(&test->seat);
118 touch_device_add(test);
119
120 return 0;
121 }
122
123 static void
test_seat_release(struct weston_test * test)124 test_seat_release(struct weston_test *test)
125 {
126 while (test->nr_touch_devices > 0)
127 touch_device_remove(test);
128
129 assert(test->is_seat_initialized &&
130 "Trying to release already released test seat");
131 test->is_seat_initialized = false;
132 weston_seat_release(&test->seat);
133 memset(&test->seat, 0, sizeof test->seat);
134 }
135
136 static struct weston_seat *
get_seat(struct weston_test * test)137 get_seat(struct weston_test *test)
138 {
139 return &test->seat;
140 }
141
142 static void
notify_pointer_position(struct weston_test * test,struct wl_resource * resource)143 notify_pointer_position(struct weston_test *test, struct wl_resource *resource)
144 {
145 struct weston_seat *seat = get_seat(test);
146 struct weston_pointer *pointer = weston_seat_get_pointer(seat);
147
148 weston_test_send_pointer_position(resource, pointer->x, pointer->y);
149 }
150
151 static void
test_surface_committed(struct weston_surface * surface,int32_t sx,int32_t sy)152 test_surface_committed(struct weston_surface *surface, int32_t sx, int32_t sy)
153 {
154 struct weston_test_surface *test_surface = surface->committed_private;
155 struct weston_test *test = test_surface->test;
156
157 if (wl_list_empty(&test_surface->view->layer_link.link))
158 weston_layer_entry_insert(&test->layer.view_list,
159 &test_surface->view->layer_link);
160
161 weston_view_set_position(test_surface->view,
162 test_surface->x, test_surface->y);
163
164 weston_view_update_transform(test_surface->view);
165
166 test_surface->surface->is_mapped = true;
167 test_surface->view->is_mapped = true;
168 }
169
170 static void
move_surface(struct wl_client * client,struct wl_resource * resource,struct wl_resource * surface_resource,int32_t x,int32_t y)171 move_surface(struct wl_client *client, struct wl_resource *resource,
172 struct wl_resource *surface_resource,
173 int32_t x, int32_t y)
174 {
175 struct weston_surface *surface =
176 wl_resource_get_user_data(surface_resource);
177 struct weston_test_surface *test_surface;
178
179 test_surface = surface->committed_private;
180 if (!test_surface) {
181 test_surface = malloc(sizeof *test_surface);
182 if (!test_surface) {
183 wl_resource_post_no_memory(resource);
184 return;
185 }
186
187 test_surface->view = weston_view_create(surface);
188 if (!test_surface->view) {
189 wl_resource_post_no_memory(resource);
190 free(test_surface);
191 return;
192 }
193
194 surface->committed_private = test_surface;
195 surface->committed = test_surface_committed;
196 }
197
198 test_surface->surface = surface;
199 test_surface->test = wl_resource_get_user_data(resource);
200 test_surface->x = x;
201 test_surface->y = y;
202 }
203
204 static void
move_pointer(struct wl_client * client,struct wl_resource * resource,uint32_t tv_sec_hi,uint32_t tv_sec_lo,uint32_t tv_nsec,int32_t x,int32_t y)205 move_pointer(struct wl_client *client, struct wl_resource *resource,
206 uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec,
207 int32_t x, int32_t y)
208 {
209 struct weston_test *test = wl_resource_get_user_data(resource);
210 struct weston_seat *seat = get_seat(test);
211 struct weston_pointer *pointer = weston_seat_get_pointer(seat);
212 struct weston_pointer_motion_event event = { 0 };
213 struct timespec time;
214
215 event = (struct weston_pointer_motion_event) {
216 .mask = WESTON_POINTER_MOTION_REL,
217 .dx = wl_fixed_to_double(wl_fixed_from_int(x) - pointer->x),
218 .dy = wl_fixed_to_double(wl_fixed_from_int(y) - pointer->y),
219 };
220
221 timespec_from_proto(&time, tv_sec_hi, tv_sec_lo, tv_nsec);
222
223 notify_motion(seat, &time, &event);
224
225 notify_pointer_position(test, resource);
226 }
227
228 static void
send_button(struct wl_client * client,struct wl_resource * resource,uint32_t tv_sec_hi,uint32_t tv_sec_lo,uint32_t tv_nsec,int32_t button,uint32_t state)229 send_button(struct wl_client *client, struct wl_resource *resource,
230 uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec,
231 int32_t button, uint32_t state)
232 {
233 struct timespec time;
234
235 struct weston_test *test = wl_resource_get_user_data(resource);
236 struct weston_seat *seat = get_seat(test);
237
238 timespec_from_proto(&time, tv_sec_hi, tv_sec_lo, tv_nsec);
239
240 notify_button(seat, &time, button, state);
241 }
242
243 static void
send_axis(struct wl_client * client,struct wl_resource * resource,uint32_t tv_sec_hi,uint32_t tv_sec_lo,uint32_t tv_nsec,uint32_t axis,wl_fixed_t value)244 send_axis(struct wl_client *client, struct wl_resource *resource,
245 uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec,
246 uint32_t axis, wl_fixed_t value)
247 {
248 struct weston_test *test = wl_resource_get_user_data(resource);
249 struct weston_seat *seat = get_seat(test);
250 struct timespec time;
251 struct weston_pointer_axis_event axis_event;
252
253 timespec_from_proto(&time, tv_sec_hi, tv_sec_lo, tv_nsec);
254 axis_event.axis = axis;
255 axis_event.value = wl_fixed_to_double(value);
256 axis_event.has_discrete = false;
257 axis_event.discrete = 0;
258
259 notify_axis(seat, &time, &axis_event);
260 }
261
262 static void
activate_surface(struct wl_client * client,struct wl_resource * resource,struct wl_resource * surface_resource)263 activate_surface(struct wl_client *client, struct wl_resource *resource,
264 struct wl_resource *surface_resource)
265 {
266 struct weston_surface *surface = surface_resource ?
267 wl_resource_get_user_data(surface_resource) : NULL;
268 struct weston_test *test = wl_resource_get_user_data(resource);
269 struct weston_seat *seat;
270 struct weston_keyboard *keyboard;
271
272 seat = get_seat(test);
273 keyboard = weston_seat_get_keyboard(seat);
274 if (surface) {
275 weston_seat_set_keyboard_focus(seat, surface);
276 notify_keyboard_focus_in(seat, &keyboard->keys,
277 STATE_UPDATE_AUTOMATIC);
278 }
279 else {
280 notify_keyboard_focus_out(seat);
281 weston_seat_set_keyboard_focus(seat, surface);
282 }
283 }
284
285 static void
send_key(struct wl_client * client,struct wl_resource * resource,uint32_t tv_sec_hi,uint32_t tv_sec_lo,uint32_t tv_nsec,uint32_t key,enum wl_keyboard_key_state state)286 send_key(struct wl_client *client, struct wl_resource *resource,
287 uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec,
288 uint32_t key, enum wl_keyboard_key_state state)
289 {
290 struct weston_test *test = wl_resource_get_user_data(resource);
291 struct weston_seat *seat = get_seat(test);
292 struct timespec time;
293
294 timespec_from_proto(&time, tv_sec_hi, tv_sec_lo, tv_nsec);
295
296 notify_key(seat, &time, key, state, STATE_UPDATE_AUTOMATIC);
297 }
298
299 static void
device_release(struct wl_client * client,struct wl_resource * resource,const char * device)300 device_release(struct wl_client *client,
301 struct wl_resource *resource, const char *device)
302 {
303 struct weston_test *test = wl_resource_get_user_data(resource);
304 struct weston_seat *seat = get_seat(test);
305
306 if (strcmp(device, "pointer") == 0) {
307 weston_seat_release_pointer(seat);
308 } else if (strcmp(device, "keyboard") == 0) {
309 weston_seat_release_keyboard(seat);
310 } else if (strcmp(device, "touch") == 0) {
311 touch_device_remove(test);
312 weston_seat_release_touch(seat);
313 } else if (strcmp(device, "seat") == 0) {
314 test_seat_release(test);
315 } else {
316 assert(0 && "Unsupported device");
317 }
318 }
319
320 static void
device_add(struct wl_client * client,struct wl_resource * resource,const char * device)321 device_add(struct wl_client *client,
322 struct wl_resource *resource, const char *device)
323 {
324 struct weston_test *test = wl_resource_get_user_data(resource);
325 struct weston_seat *seat = get_seat(test);
326
327 if (strcmp(device, "pointer") == 0) {
328 weston_seat_init_pointer(seat);
329 } else if (strcmp(device, "keyboard") == 0) {
330 weston_seat_init_keyboard(seat, NULL);
331 } else if (strcmp(device, "touch") == 0) {
332 weston_seat_init_touch(seat);
333 touch_device_add(test);
334 } else if (strcmp(device, "seat") == 0) {
335 test_seat_init(test);
336 } else {
337 assert(0 && "Unsupported device");
338 }
339 }
340
341 enum weston_test_screenshot_outcome {
342 WESTON_TEST_SCREENSHOT_SUCCESS,
343 WESTON_TEST_SCREENSHOT_NO_MEMORY,
344 WESTON_TEST_SCREENSHOT_BAD_BUFFER
345 };
346
347 typedef void (*weston_test_screenshot_done_func_t)(void *data,
348 enum weston_test_screenshot_outcome outcome);
349
350 struct test_screenshot {
351 struct weston_compositor *compositor;
352 struct wl_global *global;
353 struct wl_client *client;
354 struct weston_process process;
355 struct wl_listener destroy_listener;
356 };
357
358 struct test_screenshot_frame_listener {
359 struct wl_listener listener;
360 struct weston_buffer *buffer;
361 struct weston_output *output;
362 weston_test_screenshot_done_func_t done;
363 void *data;
364 };
365
366 static void
copy_bgra_yflip(uint8_t * dst,uint8_t * src,int height,int stride)367 copy_bgra_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
368 {
369 uint8_t *end;
370
371 end = dst + height * stride;
372 while (dst < end) {
373 memcpy(dst, src, stride);
374 dst += stride;
375 src -= stride;
376 }
377 }
378
379
380 static void
copy_bgra(uint8_t * dst,uint8_t * src,int height,int stride)381 copy_bgra(uint8_t *dst, uint8_t *src, int height, int stride)
382 {
383 /* TODO: optimize this out */
384 memcpy(dst, src, height * stride);
385 }
386
387 static void
copy_row_swap_RB(void * vdst,void * vsrc,int bytes)388 copy_row_swap_RB(void *vdst, void *vsrc, int bytes)
389 {
390 uint32_t *dst = vdst;
391 uint32_t *src = vsrc;
392 uint32_t *end = dst + bytes / 4;
393
394 while (dst < end) {
395 uint32_t v = *src++;
396 /* A R G B */
397 uint32_t tmp = v & 0xff00ff00;
398 tmp |= (v >> 16) & 0x000000ff;
399 tmp |= (v << 16) & 0x00ff0000;
400 *dst++ = tmp;
401 }
402 }
403
404 static void
copy_rgba_yflip(uint8_t * dst,uint8_t * src,int height,int stride)405 copy_rgba_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
406 {
407 uint8_t *end;
408
409 end = dst + height * stride;
410 while (dst < end) {
411 copy_row_swap_RB(dst, src, stride);
412 dst += stride;
413 src -= stride;
414 }
415 }
416
417 static void
copy_rgba(uint8_t * dst,uint8_t * src,int height,int stride)418 copy_rgba(uint8_t *dst, uint8_t *src, int height, int stride)
419 {
420 uint8_t *end;
421
422 end = dst + height * stride;
423 while (dst < end) {
424 copy_row_swap_RB(dst, src, stride);
425 dst += stride;
426 src += stride;
427 }
428 }
429
430 static void
test_screenshot_frame_notify(struct wl_listener * listener,void * data)431 test_screenshot_frame_notify(struct wl_listener *listener, void *data)
432 {
433 struct test_screenshot_frame_listener *l =
434 container_of(listener,
435 struct test_screenshot_frame_listener, listener);
436 struct weston_output *output = l->output;
437 struct weston_compositor *compositor = output->compositor;
438 int32_t stride;
439 uint8_t *pixels, *d, *s;
440
441 weston_output_disable_planes_decr(output);
442 wl_list_remove(&listener->link);
443 stride = l->buffer->width * (PIXMAN_FORMAT_BPP(compositor->read_format) / 8);
444 pixels = malloc(stride * l->buffer->height);
445
446 if (pixels == NULL) {
447 l->done(l->data, WESTON_TEST_SCREENSHOT_NO_MEMORY);
448 free(l);
449 return;
450 }
451
452 /* FIXME: Needs to handle output transformations */
453
454 compositor->renderer->read_pixels(output,
455 compositor->read_format,
456 pixels,
457 0, 0,
458 output->current_mode->width,
459 output->current_mode->height);
460
461 stride = wl_shm_buffer_get_stride(l->buffer->shm_buffer);
462
463 d = wl_shm_buffer_get_data(l->buffer->shm_buffer);
464 s = pixels + stride * (l->buffer->height - 1);
465
466 wl_shm_buffer_begin_access(l->buffer->shm_buffer);
467
468 /* XXX: It would be nice if we used Pixman to do all this rather
469 * than our own implementation
470 */
471 switch (compositor->read_format) {
472 case PIXMAN_a8r8g8b8:
473 case PIXMAN_x8r8g8b8:
474 if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
475 copy_bgra_yflip(d, s, output->current_mode->height, stride);
476 else
477 copy_bgra(d, pixels, output->current_mode->height, stride);
478 break;
479 case PIXMAN_x8b8g8r8:
480 case PIXMAN_a8b8g8r8:
481 if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
482 copy_rgba_yflip(d, s, output->current_mode->height, stride);
483 else
484 copy_rgba(d, pixels, output->current_mode->height, stride);
485 break;
486 default:
487 break;
488 }
489
490 wl_shm_buffer_end_access(l->buffer->shm_buffer);
491
492 l->done(l->data, WESTON_TEST_SCREENSHOT_SUCCESS);
493 free(pixels);
494 free(l);
495 }
496
497 static bool
weston_test_screenshot_shoot(struct weston_output * output,struct weston_buffer * buffer,weston_test_screenshot_done_func_t done,void * data)498 weston_test_screenshot_shoot(struct weston_output *output,
499 struct weston_buffer *buffer,
500 weston_test_screenshot_done_func_t done,
501 void *data)
502 {
503 struct test_screenshot_frame_listener *l;
504
505 /* Get the shm buffer resource the client created */
506 if (!wl_shm_buffer_get(buffer->resource)) {
507 done(data, WESTON_TEST_SCREENSHOT_BAD_BUFFER);
508 return false;
509 }
510
511 buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
512 buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
513 buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
514
515 /* Verify buffer is big enough */
516 if (buffer->width < output->current_mode->width ||
517 buffer->height < output->current_mode->height) {
518 done(data, WESTON_TEST_SCREENSHOT_BAD_BUFFER);
519 return false;
520 }
521
522 /* allocate the frame listener */
523 l = malloc(sizeof *l);
524 if (l == NULL) {
525 done(data, WESTON_TEST_SCREENSHOT_NO_MEMORY);
526 return false;
527 }
528
529 /* Set up the listener */
530 l->buffer = buffer;
531 l->output = output;
532 l->done = done;
533 l->data = data;
534 l->listener.notify = test_screenshot_frame_notify;
535 wl_signal_add(&output->frame_signal, &l->listener);
536
537 /* Fire off a repaint */
538 weston_output_disable_planes_incr(output);
539 weston_output_schedule_repaint(output);
540
541 return true;
542 }
543
544 static void
capture_screenshot_done(void * data,enum weston_test_screenshot_outcome outcome)545 capture_screenshot_done(void *data, enum weston_test_screenshot_outcome outcome)
546 {
547 struct wl_resource *resource = data;
548
549 switch (outcome) {
550 case WESTON_TEST_SCREENSHOT_SUCCESS:
551 weston_test_send_capture_screenshot_done(resource);
552 break;
553 case WESTON_TEST_SCREENSHOT_NO_MEMORY:
554 wl_resource_post_no_memory(resource);
555 break;
556 default:
557 break;
558 }
559 }
560
561
562 /**
563 * Grabs a snapshot of the screen.
564 */
565 static void
capture_screenshot(struct wl_client * client,struct wl_resource * resource,struct wl_resource * output_resource,struct wl_resource * buffer_resource)566 capture_screenshot(struct wl_client *client,
567 struct wl_resource *resource,
568 struct wl_resource *output_resource,
569 struct wl_resource *buffer_resource)
570 {
571 struct weston_output *output =
572 weston_head_from_resource(output_resource)->output;
573 struct weston_buffer *buffer =
574 weston_buffer_from_resource(buffer_resource);
575
576 if (buffer == NULL) {
577 wl_resource_post_no_memory(resource);
578 return;
579 }
580
581 weston_test_screenshot_shoot(output, buffer,
582 capture_screenshot_done, resource);
583 }
584
585 static void
send_touch(struct wl_client * client,struct wl_resource * resource,uint32_t tv_sec_hi,uint32_t tv_sec_lo,uint32_t tv_nsec,int32_t touch_id,wl_fixed_t x,wl_fixed_t y,uint32_t touch_type)586 send_touch(struct wl_client *client, struct wl_resource *resource,
587 uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec,
588 int32_t touch_id, wl_fixed_t x, wl_fixed_t y, uint32_t touch_type)
589 {
590 struct weston_test *test = wl_resource_get_user_data(resource);
591 struct weston_touch_device *device = test->touch_device[0];
592 struct timespec time;
593
594 assert(device);
595
596 timespec_from_proto(&time, tv_sec_hi, tv_sec_lo, tv_nsec);
597
598 notify_touch(device, &time, touch_id, wl_fixed_to_double(x),
599 wl_fixed_to_double(y), touch_type);
600 }
601
602 static const struct weston_test_interface test_implementation = {
603 move_surface,
604 move_pointer,
605 send_button,
606 send_axis,
607 activate_surface,
608 send_key,
609 device_release,
610 device_add,
611 capture_screenshot,
612 send_touch,
613 };
614
615 static void
bind_test(struct wl_client * client,void * data,uint32_t version,uint32_t id)616 bind_test(struct wl_client *client, void *data, uint32_t version, uint32_t id)
617 {
618 struct weston_test *test = data;
619 struct wl_resource *resource;
620
621 resource = wl_resource_create(client, &weston_test_interface, 1, id);
622 if (!resource) {
623 wl_client_post_no_memory(client);
624 return;
625 }
626
627 wl_resource_set_implementation(resource,
628 &test_implementation, test, NULL);
629
630 notify_pointer_position(test, resource);
631 }
632
633 static void
client_thread_cleanup(void * data_)634 client_thread_cleanup(void *data_)
635 {
636 struct wet_testsuite_data *data = data_;
637
638 close(data->thread_event_pipe);
639 data->thread_event_pipe = -1;
640 }
641
642 static void *
client_thread_routine(void * data_)643 client_thread_routine(void *data_)
644 {
645 struct wet_testsuite_data *data = data_;
646
647 pthread_setname_np(pthread_self(), "client");
648 pthread_cleanup_push(client_thread_cleanup, data);
649 data->run(data);
650 pthread_cleanup_pop(true);
651
652 return NULL;
653 }
654
655 static void
client_thread_join(struct weston_test * test)656 client_thread_join(struct weston_test *test)
657 {
658 assert(test->client_source);
659
660 pthread_join(test->client_thread, NULL);
661 wl_event_source_remove(test->client_source);
662 test->client_source = NULL;
663
664 weston_log_scope_printf(test->log, "Test thread reaped.\n");
665 }
666
667 static int
handle_client_thread_event(int fd,uint32_t mask,void * data_)668 handle_client_thread_event(int fd, uint32_t mask, void *data_)
669 {
670 struct weston_test *test = data_;
671
672 weston_log_scope_printf(test->log,
673 "Received thread event mask 0x%x\n", mask);
674
675 if (mask != WL_EVENT_HANGUP)
676 weston_log("%s: unexpected event %u\n", __func__, mask);
677
678 client_thread_join(test);
679 weston_compositor_exit(test->compositor);
680
681 return 0;
682 }
683
684 static int
create_client_thread(struct weston_test * test,struct wet_testsuite_data * data)685 create_client_thread(struct weston_test *test, struct wet_testsuite_data *data)
686 {
687 struct wl_event_loop *loop;
688 int pipefd[2] = { -1, -1 };
689 sigset_t saved;
690 sigset_t blocked;
691 int ret;
692
693 weston_log_scope_printf(test->log, "Creating a thread for running tests...\n");
694
695 if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) < 0) {
696 weston_log("Creating pipe for a client thread failed: %s\n",
697 strerror(errno));
698 return -1;
699 }
700
701 loop = wl_display_get_event_loop(test->compositor->wl_display);
702 test->client_source = wl_event_loop_add_fd(loop, pipefd[0],
703 WL_EVENT_READABLE,
704 handle_client_thread_event,
705 test);
706 close(pipefd[0]);
707
708 if (!test->client_source) {
709 weston_log("Adding client thread fd to event loop failed.\n");
710 goto out_pipe;
711 }
712
713 data->thread_event_pipe = pipefd[1];
714
715 /* Ensure we don't accidentally get signals to the thread. */
716 sigfillset(&blocked);
717 sigdelset(&blocked, SIGSEGV);
718 sigdelset(&blocked, SIGFPE);
719 sigdelset(&blocked, SIGILL);
720 sigdelset(&blocked, SIGCONT);
721 sigdelset(&blocked, SIGSYS);
722 if (pthread_sigmask(SIG_BLOCK, &blocked, &saved) != 0)
723 goto out_source;
724
725 ret = pthread_create(&test->client_thread, NULL,
726 client_thread_routine, data);
727
728 pthread_sigmask(SIG_SETMASK, &saved, NULL);
729
730 if (ret != 0) {
731 weston_log("Creating client thread failed: %s (%d)\n",
732 strerror(ret), ret);
733 goto out_source;
734 }
735
736 return 0;
737
738 out_source:
739 data->thread_event_pipe = -1;
740 wl_event_source_remove(test->client_source);
741 test->client_source = NULL;
742
743 out_pipe:
744 close(pipefd[1]);
745
746 return -1;
747 }
748
749 static void
idle_launch_testsuite(void * test_)750 idle_launch_testsuite(void *test_)
751 {
752 struct weston_test *test = test_;
753 struct wet_testsuite_data *data = wet_testsuite_data_get();
754
755 if (!data)
756 return;
757
758 switch (data->type) {
759 case TEST_TYPE_CLIENT:
760 if (create_client_thread(test, data) < 0) {
761 weston_log("Error: creating client thread for test suite failed.\n");
762 weston_compositor_exit_with_code(test->compositor,
763 RESULT_HARD_ERROR);
764 }
765 break;
766
767 case TEST_TYPE_PLUGIN:
768 data->compositor = test->compositor;
769 weston_log_scope_printf(test->log,
770 "Running tests from idle handler...\n");
771 data->run(data);
772 weston_compositor_exit(test->compositor);
773 break;
774
775 case TEST_TYPE_STANDALONE:
776 weston_log("Error: unknown test internal type %d.\n",
777 data->type);
778 weston_compositor_exit_with_code(test->compositor,
779 RESULT_HARD_ERROR);
780 }
781 }
782
783 static void
handle_compositor_destroy(struct wl_listener * listener,void * weston_compositor)784 handle_compositor_destroy(struct wl_listener *listener,
785 void *weston_compositor)
786 {
787 struct weston_test *test;
788
789 test = wl_container_of(listener, test, destroy_listener);
790
791 if (test->client_source) {
792 weston_log_scope_printf(test->log, "Cancelling client thread...\n");
793 pthread_cancel(test->client_thread);
794 client_thread_join(test);
795 }
796
797 if (test->is_seat_initialized)
798 test_seat_release(test);
799
800 wl_list_remove(&test->layer.view_list.link);
801 wl_list_remove(&test->layer.link);
802
803 weston_log_scope_destroy(test->log);
804 free(test);
805 }
806
807 WL_EXPORT int
wet_module_init(struct weston_compositor * ec,int * argc,char * argv[])808 wet_module_init(struct weston_compositor *ec,
809 int *argc, char *argv[])
810 {
811 struct weston_test *test;
812 struct wl_event_loop *loop;
813
814 test = zalloc(sizeof *test);
815 if (test == NULL)
816 return -1;
817
818 if (!weston_compositor_add_destroy_listener_once(ec,
819 &test->destroy_listener,
820 handle_compositor_destroy)) {
821 free(test);
822 return 0;
823 }
824
825 test->compositor = ec;
826 weston_layer_init(&test->layer, ec);
827 weston_layer_set_position(&test->layer, WESTON_LAYER_POSITION_CURSOR - 1);
828
829 test->log = weston_compositor_add_log_scope(ec, "test-harness-plugin",
830 "weston-test plugin's own actions",
831 NULL, NULL, NULL);
832
833 if (wl_global_create(ec->wl_display, &weston_test_interface, 1,
834 test, bind_test) == NULL)
835 goto out_free;
836
837 if (test_seat_init(test) == -1)
838 goto out_free;
839
840 loop = wl_display_get_event_loop(ec->wl_display);
841 wl_event_loop_add_idle(loop, idle_launch_testsuite, test);
842
843 return 0;
844
845 out_free:
846 wl_list_remove(&test->destroy_listener.link);
847 free(test);
848 return -1;
849 }
850