• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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