1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5#include <sys/ipc.h>
6#include <sys/shm.h>
7
8#include "test.h"
9
10static Window get_root(struct test_display *t)
11{
12	XSetWindowAttributes attr;
13	Window w;
14
15	/* Be nasty and install a fullscreen window on top so that we
16	 * can guarantee we do not get clipped by children.
17	 */
18	attr.override_redirect = 1;
19	w = XCreateWindow(t->dpy, DefaultRootWindow(t->dpy),
20			  0, 0, t->width, t->height, 0,
21			  DefaultDepth(t->dpy, DefaultScreen(t->dpy)),
22			  InputOutput,
23			  DefaultVisual(t->dpy, DefaultScreen(t->dpy)),
24			  CWOverrideRedirect, &attr);
25	XMapWindow(t->dpy, w);
26
27	return w;
28}
29
30static Display *out_display(int argc, char **argv)
31{
32	Display *dpy;
33	const char *name = NULL;
34	int i;
35
36	for (i = 0; i < argc; i++) {
37		if (strncmp(argv[i], "-d", 2) == 0) {
38			if (argv[i][2] == '\0') {
39				if (i+1 < argc) {
40					name = argv[i+1];
41					i++;
42				}
43			} else
44				name = argv[i] + 2;
45		}
46	}
47
48	if (name == NULL)
49		name = getenv("DISPLAY");
50	if (name == NULL)
51		name = ":0"; /* useful default */
52
53	dpy = XOpenDisplay(name);
54	if (dpy == NULL)
55		die("unable to open out display %s\n", name);
56
57	printf("Opened connection to %s for testing.\n", name);
58	return dpy;
59}
60
61static Display *ref_display(int width, int height, int depth)
62{
63	Display *dpy;
64	char buf[160];
65	const char *name;
66	int try;
67
68	name = getenv("REF_DISPLAY");
69	if (name) {
70		dpy = XOpenDisplay(name);
71		if (dpy == NULL)
72			die("unable to open reference display %s\n", name);
73
74		printf("Opened connection to %s for reference.\n", name);
75		return dpy;
76	}
77
78	snprintf(buf, sizeof(buf),
79		 "Xvfb -ac -terminate -screen 0 %dx%dx%d :99 >/dev/null 2>&1 &",
80		 width, height, depth);
81	if (system(buf))
82		die("unable to spawn '%s' for reference display\n", buf);
83
84	try = 0;
85	while (try++ < 1000) {
86		dpy = XOpenDisplay(":99");
87		if (dpy)
88			break;
89		usleep(1000);
90	}
91
92	if (dpy == NULL)
93		die("unable to open reference display\n");
94
95	return dpy;
96}
97
98static void shm_setup(struct test_display *d)
99{
100	int major, minor;
101	int size;
102
103	XShmQueryVersion(d->dpy, &major, &minor, &d->has_shm_pixmaps);
104	if (major == 0 && minor == 0)
105		die("XSHM not supported\n");
106
107	size = d->width * d->height * 4;
108	d->max_shm_size = size;
109
110	d->shm.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600);
111	if (d->shm.shmid == -1)
112		die("failed to allocated %d bytes for a shm segment\n", size);
113
114	d->shm.shmaddr = shmat(d->shm.shmid, NULL, 0);
115	d->shm.readOnly = 0;
116	XShmAttach(d->dpy, &d->shm);
117	XSync(d->dpy, 1);
118}
119
120static void default_setup(struct test_display *dpy)
121{
122	dpy->width = WidthOfScreen(DefaultScreenOfDisplay(dpy->dpy));
123	dpy->height = HeightOfScreen(DefaultScreenOfDisplay(dpy->dpy));
124	dpy->format =
125		XRenderFindVisualFormat(dpy->dpy,
126					DefaultVisual(dpy->dpy,
127						      DefaultScreen(dpy->dpy)));
128	dpy->depth = DefaultDepth(dpy->dpy, DefaultScreen(dpy->dpy));
129}
130
131static void test_get_displays(int argc, char **argv,
132			      struct test_display *out,
133			      struct test_display *ref)
134{
135	out->dpy = out_display(argc, argv);
136	default_setup(out);
137	shm_setup(out);
138	out->root = get_root(out);
139	out->target = OUT;
140
141	ref->dpy = ref_display(out->width, out->height, out->depth);
142	default_setup(ref);
143	shm_setup(ref);
144	ref->root = get_root(ref);
145	ref->target = REF;
146}
147
148void test_init(struct test *test, int argc, char **argv)
149{
150	memset(test, 0, sizeof(*test));
151	test_get_displays(argc, argv, &test->out, &test->ref);
152}
153
154void test_timer_start(struct test_display *t, struct timespec *tv)
155{
156	clock_gettime(CLOCK_MONOTONIC, tv);
157}
158
159double test_timer_stop(struct test_display *t, struct timespec *tv)
160{
161	XImage *image;
162	struct timespec now;
163
164	image = XGetImage(t->dpy, t->root, 0, 0, 1, 1, AllPlanes, ZPixmap);
165	clock_gettime(CLOCK_MONOTONIC, &now);
166	XDestroyImage(image);
167
168	return (now.tv_sec - tv->tv_sec) + 1e-9*(now.tv_nsec - tv->tv_nsec);
169}
170