You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							558 lines
						
					
					
						
							13 KiB
						
					
					
				
			
		
		
	
	
							558 lines
						
					
					
						
							13 KiB
						
					
					
				| /*
 | |
|  * Copyright © 2012 Collabora, Ltd.
 | |
|  *
 | |
|  * Permission to use, copy, modify, distribute, and sell this software and
 | |
|  * its documentation for any purpose is hereby granted without fee, provided
 | |
|  * that the above copyright notice appear in all copies and that both that
 | |
|  * copyright notice and this permission notice appear in supporting
 | |
|  * documentation, and that the name of the copyright holders not be used in
 | |
|  * advertising or publicity pertaining to distribution of the software
 | |
|  * without specific, written prior permission.  The copyright holders make
 | |
|  * no representations about the suitability of this software for any
 | |
|  * purpose.  It is provided "as is" without express or implied warranty.
 | |
|  *
 | |
|  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 | |
|  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 | |
|  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
 | |
|  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 | |
|  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 | |
|  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 | |
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include <string.h>
 | |
| 
 | |
| #include "weston-test-client-helper.h"
 | |
| #include <stdio.h>
 | |
| 
 | |
| #define NUM_SUBSURFACES 3
 | |
| 
 | |
| struct compound_surface {
 | |
| 	struct wl_subcompositor *subco;
 | |
| 	struct wl_surface *parent;
 | |
| 	struct wl_surface *child[NUM_SUBSURFACES];
 | |
| 	struct wl_subsurface *sub[NUM_SUBSURFACES];
 | |
| };
 | |
| 
 | |
| static struct wl_subcompositor *
 | |
| get_subcompositor(struct client *client)
 | |
| {
 | |
| 	struct global *g;
 | |
| 	struct global *global_sub = NULL;
 | |
| 	struct wl_subcompositor *sub;
 | |
| 
 | |
| 	wl_list_for_each(g, &client->global_list, link) {
 | |
| 		if (strcmp(g->interface, "wl_subcompositor"))
 | |
| 			continue;
 | |
| 
 | |
| 		if (global_sub)
 | |
| 			assert(0 && "multiple wl_subcompositor objects");
 | |
| 
 | |
| 		global_sub = g;
 | |
| 	}
 | |
| 
 | |
| 	assert(global_sub && "no wl_subcompositor found");
 | |
| 
 | |
| 	assert(global_sub->version == 1);
 | |
| 
 | |
| 	sub = wl_registry_bind(client->wl_registry, global_sub->name,
 | |
| 			       &wl_subcompositor_interface, 1);
 | |
| 	assert(sub);
 | |
| 
 | |
| 	return sub;
 | |
| }
 | |
| 
 | |
| static void
 | |
| populate_compound_surface(struct compound_surface *com, struct client *client)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	com->subco = get_subcompositor(client);
 | |
| 
 | |
| 	com->parent = wl_compositor_create_surface(client->wl_compositor);
 | |
| 
 | |
| 	for (i = 0; i < NUM_SUBSURFACES; i++) {
 | |
| 		com->child[i] =
 | |
| 			wl_compositor_create_surface(client->wl_compositor);
 | |
| 		com->sub[i] =
 | |
| 			wl_subcompositor_get_subsurface(com->subco,
 | |
| 							com->child[i],
 | |
| 							com->parent);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_basic_protocol)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com1;
 | |
| 	struct compound_surface com2;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	populate_compound_surface(&com1, client);
 | |
| 	populate_compound_surface(&com2, client);
 | |
| 
 | |
| 	client_roundtrip(client);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_position_protocol)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com;
 | |
| 	int i;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	populate_compound_surface(&com, client);
 | |
| 	for (i = 0; i < NUM_SUBSURFACES; i++)
 | |
| 		wl_subsurface_set_position(com.sub[i],
 | |
| 					   (i + 2) * 20, (i + 2) * 10);
 | |
| 
 | |
| 	client_roundtrip(client);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_placement_protocol)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	populate_compound_surface(&com, client);
 | |
| 
 | |
| 	wl_subsurface_place_above(com.sub[0], com.child[1]);
 | |
| 	wl_subsurface_place_above(com.sub[1], com.parent);
 | |
| 	wl_subsurface_place_below(com.sub[2], com.child[0]);
 | |
| 	wl_subsurface_place_below(com.sub[1], com.parent);
 | |
| 
 | |
| 	client_roundtrip(client);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_paradox)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct wl_surface *parent;
 | |
| 	struct wl_subcompositor *subco;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	subco = get_subcompositor(client);
 | |
| 	parent = wl_compositor_create_surface(client->wl_compositor);
 | |
| 
 | |
| 	/* surface is its own parent */
 | |
| 	wl_subcompositor_get_subsurface(subco, parent, parent);
 | |
| 
 | |
| 	expect_protocol_error(client, &wl_subcompositor_interface,
 | |
| 			      WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_identical_link)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	populate_compound_surface(&com, client);
 | |
| 
 | |
| 	/* surface is already a subsurface */
 | |
| 	wl_subcompositor_get_subsurface(com.subco, com.child[0], com.parent);
 | |
| 
 | |
| 	expect_protocol_error(client, &wl_subcompositor_interface,
 | |
| 			      WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_change_link)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com;
 | |
| 	struct wl_surface *stranger;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	stranger = wl_compositor_create_surface(client->wl_compositor);
 | |
| 	populate_compound_surface(&com, client);
 | |
| 
 | |
| 	/* surface is already a subsurface */
 | |
| 	wl_subcompositor_get_subsurface(com.subco, com.child[0], stranger);
 | |
| 
 | |
| 	expect_protocol_error(client, &wl_subcompositor_interface,
 | |
| 			      WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_nesting)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com;
 | |
| 	struct wl_surface *stranger;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	stranger = wl_compositor_create_surface(client->wl_compositor);
 | |
| 	populate_compound_surface(&com, client);
 | |
| 
 | |
| 	/* parent is a sub-surface */
 | |
| 	wl_subcompositor_get_subsurface(com.subco, stranger, com.child[0]);
 | |
| 
 | |
| 	client_roundtrip(client);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_nesting_parent)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com;
 | |
| 	struct wl_surface *stranger;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	stranger = wl_compositor_create_surface(client->wl_compositor);
 | |
| 	populate_compound_surface(&com, client);
 | |
| 
 | |
| 	/* surface is already a parent */
 | |
| 	wl_subcompositor_get_subsurface(com.subco, com.parent, stranger);
 | |
| 
 | |
| 	client_roundtrip(client);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_loop_paradox)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct wl_surface *surface[3];
 | |
| 	struct wl_subcompositor *subco;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	subco = get_subcompositor(client);
 | |
| 	surface[0] = wl_compositor_create_surface(client->wl_compositor);
 | |
| 	surface[1] = wl_compositor_create_surface(client->wl_compositor);
 | |
| 	surface[2] = wl_compositor_create_surface(client->wl_compositor);
 | |
| 
 | |
| 	/* create a nesting loop */
 | |
| 	wl_subcompositor_get_subsurface(subco, surface[1], surface[0]);
 | |
| 	wl_subcompositor_get_subsurface(subco, surface[2], surface[1]);
 | |
| 	wl_subcompositor_get_subsurface(subco, surface[0], surface[2]);
 | |
| 
 | |
| 	expect_protocol_error(client, &wl_subcompositor_interface,
 | |
| 			      WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_place_above_stranger)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com;
 | |
| 	struct wl_surface *stranger;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	stranger = wl_compositor_create_surface(client->wl_compositor);
 | |
| 	populate_compound_surface(&com, client);
 | |
| 
 | |
| 	/* bad sibling */
 | |
| 	wl_subsurface_place_above(com.sub[0], stranger);
 | |
| 
 | |
| 	expect_protocol_error(client, &wl_subsurface_interface,
 | |
| 			      WL_SUBSURFACE_ERROR_BAD_SURFACE);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_place_below_stranger)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com;
 | |
| 	struct wl_surface *stranger;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	stranger = wl_compositor_create_surface(client->wl_compositor);
 | |
| 	populate_compound_surface(&com, client);
 | |
| 
 | |
| 	/* bad sibling */
 | |
| 	wl_subsurface_place_below(com.sub[0], stranger);
 | |
| 
 | |
| 	expect_protocol_error(client, &wl_subsurface_interface,
 | |
| 			      WL_SUBSURFACE_ERROR_BAD_SURFACE);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_place_above_foreign)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com1;
 | |
| 	struct compound_surface com2;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	populate_compound_surface(&com1, client);
 | |
| 	populate_compound_surface(&com2, client);
 | |
| 
 | |
| 	/* bad sibling */
 | |
| 	wl_subsurface_place_above(com1.sub[0], com2.child[0]);
 | |
| 
 | |
| 	expect_protocol_error(client, &wl_subsurface_interface,
 | |
| 			      WL_SUBSURFACE_ERROR_BAD_SURFACE);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_place_below_foreign)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com1;
 | |
| 	struct compound_surface com2;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	populate_compound_surface(&com1, client);
 | |
| 	populate_compound_surface(&com2, client);
 | |
| 
 | |
| 	/* bad sibling */
 | |
| 	wl_subsurface_place_below(com1.sub[0], com2.child[0]);
 | |
| 
 | |
| 	expect_protocol_error(client, &wl_subsurface_interface,
 | |
| 			      WL_SUBSURFACE_ERROR_BAD_SURFACE);
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_destroy_protocol)
 | |
| {
 | |
| 	struct client *client;
 | |
| 	struct compound_surface com;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	populate_compound_surface(&com, client);
 | |
| 
 | |
| 	/* not needed anymore */
 | |
| 	wl_subcompositor_destroy(com.subco);
 | |
| 
 | |
| 	/* detach child from parent */
 | |
| 	wl_subsurface_destroy(com.sub[0]);
 | |
| 
 | |
| 	/* destroy: child, parent */
 | |
| 	wl_surface_destroy(com.child[1]);
 | |
| 	wl_surface_destroy(com.parent);
 | |
| 
 | |
| 	/* destroy: parent, child */
 | |
| 	wl_surface_destroy(com.child[2]);
 | |
| 
 | |
| 	/* destroy: sub, child */
 | |
| 	wl_surface_destroy(com.child[0]);
 | |
| 
 | |
| 	/* 2x destroy: child, sub */
 | |
| 	wl_subsurface_destroy(com.sub[2]);
 | |
| 	wl_subsurface_destroy(com.sub[1]);
 | |
| 
 | |
| 	client_roundtrip(client);
 | |
| }
 | |
| 
 | |
| static void
 | |
| create_subsurface_tree(struct client *client, struct wl_surface **surfs,
 | |
| 		       struct wl_subsurface **subs, int n)
 | |
| {
 | |
| 	struct wl_subcompositor *subco;
 | |
| 	int i;
 | |
| 
 | |
| 	subco = get_subcompositor(client);
 | |
| 
 | |
| 	for (i = 0; i < n; i++)
 | |
| 		surfs[i] = wl_compositor_create_surface(client->wl_compositor);
 | |
| 
 | |
| 	/*
 | |
| 	 * The tree of sub-surfaces:
 | |
| 	 *                            0
 | |
| 	 *                           / \
 | |
| 	 *                          1   2 - 10
 | |
| 	 *                         / \  |\
 | |
| 	 *                        3   5 9 6
 | |
| 	 *                       /       / \
 | |
| 	 *                      4       7   8
 | |
| 	 * Surface 0 has no wl_subsurface, others do.
 | |
| 	 */
 | |
| 
 | |
| 	switch (n) {
 | |
| 	default:
 | |
| 		assert(0);
 | |
| 		break;
 | |
| 
 | |
| #define SUB_LINK(s,p) \
 | |
| 	subs[s] = wl_subcompositor_get_subsurface(subco, surfs[s], surfs[p])
 | |
| 
 | |
| 	case 11:
 | |
| 		SUB_LINK(10, 2);
 | |
| 	case 10:
 | |
| 		SUB_LINK(9, 2);
 | |
| 	case 9:
 | |
| 		SUB_LINK(8, 6);
 | |
| 	case 8:
 | |
| 		SUB_LINK(7, 6);
 | |
| 	case 7:
 | |
| 		SUB_LINK(6, 2);
 | |
| 	case 6:
 | |
| 		SUB_LINK(5, 1);
 | |
| 	case 5:
 | |
| 		SUB_LINK(4, 3);
 | |
| 	case 4:
 | |
| 		SUB_LINK(3, 1);
 | |
| 	case 3:
 | |
| 		SUB_LINK(2, 0);
 | |
| 	case 2:
 | |
| 		SUB_LINK(1, 0);
 | |
| 
 | |
| #undef SUB_LINK
 | |
| 	};
 | |
| }
 | |
| 
 | |
| static void
 | |
| destroy_subsurface_tree(struct wl_surface **surfs,
 | |
| 			struct wl_subsurface **subs, int n)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = n; i-- > 0; ) {
 | |
| 		if (surfs[i])
 | |
| 			wl_surface_destroy(surfs[i]);
 | |
| 
 | |
| 		if (subs[i])
 | |
| 			wl_subsurface_destroy(subs[i]);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int
 | |
| has_dupe(int *cnt, int n)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < n; i++)
 | |
| 		if (cnt[i] == cnt[n])
 | |
| 			return 1;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Note: number of permutations to test is: set_size! / (set_size - NSTEPS)!
 | |
|  */
 | |
| #define NSTEPS 3
 | |
| 
 | |
| struct permu_state {
 | |
| 	int set_size;
 | |
| 	int cnt[NSTEPS];
 | |
| };
 | |
| 
 | |
| static void
 | |
| permu_init(struct permu_state *s, int set_size)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	s->set_size = set_size;
 | |
| 	for (i = 0; i < NSTEPS; i++)
 | |
| 		s->cnt[i] = 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| permu_next(struct permu_state *s)
 | |
| {
 | |
| 	int k;
 | |
| 
 | |
| 	s->cnt[NSTEPS - 1]++;
 | |
| 
 | |
| 	while (1) {
 | |
| 		if (s->cnt[0] >= s->set_size) {
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		for (k = 1; k < NSTEPS; k++) {
 | |
| 			if (s->cnt[k] >= s->set_size) {
 | |
| 				s->cnt[k - 1]++;
 | |
| 				s->cnt[k] = 0;
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			if (has_dupe(s->cnt, k)) {
 | |
| 				s->cnt[k]++;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (k == NSTEPS)
 | |
| 			return 0;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void
 | |
| destroy_permu_object(struct wl_surface **surfs,
 | |
| 		     struct wl_subsurface **subs, int i)
 | |
| {
 | |
| 	int h = (i + 1) / 2;
 | |
| 
 | |
| 	if (i & 1) {
 | |
| 		fprintf(stderr, " [sub  %2d]", h);
 | |
| 		wl_subsurface_destroy(subs[h]);
 | |
| 		subs[h] = NULL;
 | |
| 	} else {
 | |
| 		fprintf(stderr, " [surf %2d]", h);
 | |
| 		wl_surface_destroy(surfs[h]);
 | |
| 		surfs[h] = NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| TEST(test_subsurface_destroy_permutations)
 | |
| {
 | |
| 	/*
 | |
| 	 * Test wl_surface and wl_subsurface destruction orders in a
 | |
| 	 * complex tree of sub-surfaces.
 | |
| 	 *
 | |
| 	 * In the tree of sub-surfaces, go through every possible
 | |
| 	 * permutation of destroying all wl_surface and wl_subsurface
 | |
| 	 * objects. Execpt, to limit running time to a reasonable level,
 | |
| 	 * execute only the first NSTEPS destructions from each
 | |
| 	 * permutation, and ignore identical cases.
 | |
| 	 */
 | |
| 
 | |
| 	const int test_size = 11;
 | |
| 	struct client *client;
 | |
| 	struct wl_surface *surfs[test_size];
 | |
| 	struct wl_subsurface *subs[test_size];
 | |
| 	struct permu_state per;
 | |
| 	int counter = 0;
 | |
| 	int i;
 | |
| 
 | |
| 	client = client_create(100, 50, 123, 77);
 | |
| 	assert(client);
 | |
| 
 | |
| 	permu_init(&per, test_size * 2 - 1);
 | |
| 	while (permu_next(&per) != -1) {
 | |
| 		/* for each permutation of NSTEPS out of test_size */
 | |
| 		memset(surfs, 0, sizeof surfs);
 | |
| 		memset(subs, 0, sizeof subs);
 | |
| 
 | |
| 		create_subsurface_tree(client, surfs, subs, test_size);
 | |
| 
 | |
| 		fprintf(stderr, "permu");
 | |
| 
 | |
| 		for (i = 0; i < NSTEPS; i++)
 | |
| 			fprintf(stderr, " %2d", per.cnt[i]);
 | |
| 
 | |
| 		for (i = 0; i < NSTEPS; i++)
 | |
| 			destroy_permu_object(surfs, subs, per.cnt[i]);
 | |
| 
 | |
| 		fprintf(stderr, "\n");
 | |
| 		client_roundtrip(client);
 | |
| 
 | |
| 		destroy_subsurface_tree(surfs, subs, test_size);
 | |
| 		counter++;
 | |
| 	}
 | |
| 
 | |
| 	client_roundtrip(client);
 | |
| 	fprintf(stderr, "tried %d destroy permutations\n", counter);
 | |
| }
 | |
| 
 |