1fe8aea9eSmrg/*
2fe8aea9eSmrg * Copyright (c) 2015 Intel Corporation
3fe8aea9eSmrg *
4fe8aea9eSmrg * Permission is hereby granted, free of charge, to any person obtaining a
5fe8aea9eSmrg * copy of this software and associated documentation files (the "Software"),
6fe8aea9eSmrg * to deal in the Software without restriction, including without limitation
7fe8aea9eSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8fe8aea9eSmrg * and/or sell copies of the Software, and to permit persons to whom the
9fe8aea9eSmrg * Software is furnished to do so, subject to the following conditions:
10fe8aea9eSmrg *
11fe8aea9eSmrg * The above copyright notice and this permission notice (including the next
12fe8aea9eSmrg * paragraph) shall be included in all copies or substantial portions of the
13fe8aea9eSmrg * Software.
14fe8aea9eSmrg *
15fe8aea9eSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16fe8aea9eSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17fe8aea9eSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18fe8aea9eSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19fe8aea9eSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20fe8aea9eSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21fe8aea9eSmrg * SOFTWARE.
22fe8aea9eSmrg *
23fe8aea9eSmrg */
24fe8aea9eSmrg
25fe8aea9eSmrg#ifdef HAVE_CONFIG_H
26fe8aea9eSmrg#include "config.h"
27fe8aea9eSmrg#endif
28fe8aea9eSmrg
29fe8aea9eSmrg#include <X11/Xlib.h>
30fe8aea9eSmrg#include <X11/Xatom.h>
31fe8aea9eSmrg#include <X11/Xlib-xcb.h>
32fe8aea9eSmrg#include <X11/xshmfence.h>
33fe8aea9eSmrg#include <X11/Xutil.h>
34fe8aea9eSmrg#include <X11/Xlibint.h>
35fe8aea9eSmrg#include <X11/extensions/Xcomposite.h>
36fe8aea9eSmrg#include <X11/extensions/Xdamage.h>
37fe8aea9eSmrg#include <X11/extensions/dpms.h>
38fe8aea9eSmrg#include <X11/extensions/randr.h>
39fe8aea9eSmrg#include <X11/extensions/Xrandr.h>
40fe8aea9eSmrg#include <xcb/xcb.h>
41fe8aea9eSmrg#include <xcb/present.h>
42fe8aea9eSmrg#include <xcb/dri3.h>
43fe8aea9eSmrg#include <xcb/xfixes.h>
44fe8aea9eSmrg#include <xf86drm.h>
45fe8aea9eSmrg#include <i915_drm.h>
46fe8aea9eSmrg
47fe8aea9eSmrg#include <stdio.h>
48fe8aea9eSmrg#include <string.h>
49fe8aea9eSmrg#include <fcntl.h>
50fe8aea9eSmrg#include <unistd.h>
51fe8aea9eSmrg#include <assert.h>
52fe8aea9eSmrg#include <errno.h>
53fe8aea9eSmrg#include <setjmp.h>
54fe8aea9eSmrg#include <signal.h>
55fe8aea9eSmrg
56fe8aea9eSmrgstruct dri3_fence {
57fe8aea9eSmrg	XID xid;
58fe8aea9eSmrg	void *addr;
59fe8aea9eSmrg};
60fe8aea9eSmrg
61fe8aea9eSmrgstatic int _x_error_occurred;
62fe8aea9eSmrgstatic uint32_t stamp;
63fe8aea9eSmrg
64fe8aea9eSmrgstruct list {
65fe8aea9eSmrg    struct list *next, *prev;
66fe8aea9eSmrg};
67fe8aea9eSmrg
68fe8aea9eSmrgstatic void
69fe8aea9eSmrglist_init(struct list *list)
70fe8aea9eSmrg{
71fe8aea9eSmrg    list->next = list->prev = list;
72fe8aea9eSmrg}
73fe8aea9eSmrg
74fe8aea9eSmrgstatic inline void
75fe8aea9eSmrg__list_add(struct list *entry,
76fe8aea9eSmrg	    struct list *prev,
77fe8aea9eSmrg	    struct list *next)
78fe8aea9eSmrg{
79fe8aea9eSmrg    next->prev = entry;
80fe8aea9eSmrg    entry->next = next;
81fe8aea9eSmrg    entry->prev = prev;
82fe8aea9eSmrg    prev->next = entry;
83fe8aea9eSmrg}
84fe8aea9eSmrg
85fe8aea9eSmrgstatic inline void
86fe8aea9eSmrglist_add(struct list *entry, struct list *head)
87fe8aea9eSmrg{
88fe8aea9eSmrg    __list_add(entry, head, head->next);
89fe8aea9eSmrg}
90fe8aea9eSmrg
91fe8aea9eSmrgstatic inline void
92fe8aea9eSmrg__list_del(struct list *prev, struct list *next)
93fe8aea9eSmrg{
94fe8aea9eSmrg	next->prev = prev;
95fe8aea9eSmrg	prev->next = next;
96fe8aea9eSmrg}
97fe8aea9eSmrg
98fe8aea9eSmrgstatic inline void
99fe8aea9eSmrg_list_del(struct list *entry)
100fe8aea9eSmrg{
101fe8aea9eSmrg    __list_del(entry->prev, entry->next);
102fe8aea9eSmrg}
103fe8aea9eSmrg
104fe8aea9eSmrgstatic inline void
105fe8aea9eSmrglist_move(struct list *list, struct list *head)
106fe8aea9eSmrg{
107fe8aea9eSmrg	if (list->prev != head) {
108fe8aea9eSmrg		_list_del(list);
109fe8aea9eSmrg		list_add(list, head);
110fe8aea9eSmrg	}
111fe8aea9eSmrg}
112fe8aea9eSmrg
113fe8aea9eSmrg#define __container_of(ptr, sample, member)				\
114fe8aea9eSmrg    (void *)((char *)(ptr) - ((char *)&(sample)->member - (char *)(sample)))
115fe8aea9eSmrg
116fe8aea9eSmrg#define list_for_each_entry(pos, head, member)				\
117fe8aea9eSmrg    for (pos = __container_of((head)->next, pos, member);		\
118fe8aea9eSmrg	 &pos->member != (head);					\
119fe8aea9eSmrg	 pos = __container_of(pos->member.next, pos, member))
120fe8aea9eSmrg
121fe8aea9eSmrgstatic int
122fe8aea9eSmrg_check_error_handler(Display     *display,
123fe8aea9eSmrg		     XErrorEvent *event)
124fe8aea9eSmrg{
125fe8aea9eSmrg	printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n",
126fe8aea9eSmrg	       DisplayString(display),
127fe8aea9eSmrg	       event->serial,
128fe8aea9eSmrg	       event->error_code,
129fe8aea9eSmrg	       event->request_code,
130fe8aea9eSmrg	       event->minor_code);
131fe8aea9eSmrg	_x_error_occurred++;
132fe8aea9eSmrg	return False; /* ignored */
133fe8aea9eSmrg}
134fe8aea9eSmrg
135fe8aea9eSmrgstatic int dri3_create_fence(Display *dpy,
136fe8aea9eSmrg			     Pixmap pixmap,
137fe8aea9eSmrg			     struct dri3_fence *fence)
138fe8aea9eSmrg{
139fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
140fe8aea9eSmrg	struct dri3_fence f;
141fe8aea9eSmrg	int fd;
142fe8aea9eSmrg
143fe8aea9eSmrg	fd = xshmfence_alloc_shm();
144fe8aea9eSmrg	if (fd < 0)
145fe8aea9eSmrg		return -1;
146fe8aea9eSmrg
147fe8aea9eSmrg	f.addr = xshmfence_map_shm(fd);
148fe8aea9eSmrg	if (f.addr == NULL) {
149fe8aea9eSmrg		close(fd);
150fe8aea9eSmrg		return -1;
151fe8aea9eSmrg	}
152fe8aea9eSmrg
153fe8aea9eSmrg	f.xid = xcb_generate_id(c);
154fe8aea9eSmrg	xcb_dri3_fence_from_fd(c, pixmap, f.xid, 0, fd);
155fe8aea9eSmrg
156fe8aea9eSmrg	*fence = f;
157fe8aea9eSmrg	return 0;
158fe8aea9eSmrg}
159fe8aea9eSmrg
160fe8aea9eSmrgstatic double elapsed(const struct timespec *start,
161fe8aea9eSmrg		      const struct timespec *end)
162fe8aea9eSmrg{
163fe8aea9eSmrg	return 1e6*(end->tv_sec - start->tv_sec) + (end->tv_nsec - start->tv_nsec)/1000;
164fe8aea9eSmrg}
165fe8aea9eSmrg
166fe8aea9eSmrgstruct buffer {
167fe8aea9eSmrg	struct list link;
168fe8aea9eSmrg	Pixmap pixmap;
169fe8aea9eSmrg	struct dri3_fence fence;
170fe8aea9eSmrg	int fd;
171fe8aea9eSmrg	int busy;
172fe8aea9eSmrg};
173fe8aea9eSmrg
174fe8aea9eSmrgstatic void run(Display *dpy, Window win)
175fe8aea9eSmrg{
176fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
177fe8aea9eSmrg	struct timespec start, end;
178fe8aea9eSmrg#define N_BACK 8
179fe8aea9eSmrg	struct buffer buffer[N_BACK];
180fe8aea9eSmrg	struct list mru;
181fe8aea9eSmrg	Window root;
182fe8aea9eSmrg	unsigned int width, height;
183fe8aea9eSmrg	unsigned border, depth;
184fe8aea9eSmrg	unsigned present_flags = XCB_PRESENT_OPTION_ASYNC;
185fe8aea9eSmrg	xcb_xfixes_region_t update = 0;
186fe8aea9eSmrg	int completed = 0;
187fe8aea9eSmrg	int queued = 0;
188fe8aea9eSmrg	uint32_t eid;
189fe8aea9eSmrg	void *Q;
190fe8aea9eSmrg	int i, n;
191fe8aea9eSmrg
192fe8aea9eSmrg	list_init(&mru);
193fe8aea9eSmrg
194fe8aea9eSmrg	XGetGeometry(dpy, win,
195fe8aea9eSmrg		     &root, &i, &n, &width, &height, &border, &depth);
196fe8aea9eSmrg
197fe8aea9eSmrg	_x_error_occurred = 0;
198fe8aea9eSmrg
199fe8aea9eSmrg	for (n = 0; n < N_BACK; n++) {
200fe8aea9eSmrg		xcb_dri3_buffer_from_pixmap_reply_t *reply;
201fe8aea9eSmrg		int *fds;
202fe8aea9eSmrg
203fe8aea9eSmrg		buffer[n].pixmap =
204fe8aea9eSmrg			XCreatePixmap(dpy, win, width, height, depth);
205fe8aea9eSmrg		buffer[n].fence.xid = 0;
206fe8aea9eSmrg		buffer[n].fd = -1;
207fe8aea9eSmrg
208fe8aea9eSmrg		if (dri3_create_fence(dpy, win, &buffer[n].fence))
209fe8aea9eSmrg			return;
210fe8aea9eSmrg
211fe8aea9eSmrg		reply = xcb_dri3_buffer_from_pixmap_reply (c,
212fe8aea9eSmrg							   xcb_dri3_buffer_from_pixmap(c, buffer[n].pixmap),
213fe8aea9eSmrg							   NULL);
214fe8aea9eSmrg		if (reply == NULL)
215fe8aea9eSmrg			return;
216fe8aea9eSmrg
217fe8aea9eSmrg		fds = xcb_dri3_buffer_from_pixmap_reply_fds (c, reply);
218fe8aea9eSmrg		buffer[n].fd = fds[0];
219fe8aea9eSmrg		free(reply);
220fe8aea9eSmrg
221fe8aea9eSmrg		/* start idle */
222fe8aea9eSmrg		xshmfence_trigger(buffer[n].fence.addr);
223fe8aea9eSmrg		buffer[n].busy = 0;
224fe8aea9eSmrg		list_add(&buffer[n].link, &mru);
225fe8aea9eSmrg	}
226fe8aea9eSmrg
227fe8aea9eSmrg	eid = xcb_generate_id(c);
228fe8aea9eSmrg	xcb_present_select_input(c, eid, win,
229fe8aea9eSmrg                                 XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY |
230fe8aea9eSmrg                                 XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
231fe8aea9eSmrg	Q = xcb_register_for_special_xge(c, &xcb_present_id, eid, &stamp);
232fe8aea9eSmrg
233fe8aea9eSmrg	clock_gettime(CLOCK_MONOTONIC, &start);
234fe8aea9eSmrg	do {
235fe8aea9eSmrg		for (n = 0; n < 1000; n++) {
236fe8aea9eSmrg			struct buffer *tmp, *b = NULL;
237fe8aea9eSmrg			list_for_each_entry(tmp, &mru, link) {
238fe8aea9eSmrg				if (!tmp->busy) {
239fe8aea9eSmrg					b = tmp;
240fe8aea9eSmrg					break;
241fe8aea9eSmrg				}
242fe8aea9eSmrg			}
243fe8aea9eSmrg			while (b == NULL) {
244fe8aea9eSmrg				xcb_present_generic_event_t *ev;
245fe8aea9eSmrg
246fe8aea9eSmrg				ev = (xcb_present_generic_event_t *)
247fe8aea9eSmrg					xcb_wait_for_special_event(c, Q);
248fe8aea9eSmrg				if (ev == NULL)
249fe8aea9eSmrg					abort();
250fe8aea9eSmrg
251fe8aea9eSmrg				do {
252fe8aea9eSmrg					switch (ev->evtype) {
253fe8aea9eSmrg					case XCB_PRESENT_COMPLETE_NOTIFY:
254fe8aea9eSmrg						completed++;
255fe8aea9eSmrg						queued--;
256fe8aea9eSmrg						break;
257fe8aea9eSmrg
258fe8aea9eSmrg					case XCB_PRESENT_EVENT_IDLE_NOTIFY:
259fe8aea9eSmrg						{
260fe8aea9eSmrg							xcb_present_idle_notify_event_t *ie = (xcb_present_idle_notify_event_t *)ev;
261fe8aea9eSmrg							assert(ie->serial < N_BACK);
262fe8aea9eSmrg							buffer[ie->serial].busy = 0;
263fe8aea9eSmrg							if (b == NULL)
264fe8aea9eSmrg								b = &buffer[ie->serial];
265fe8aea9eSmrg							break;
266fe8aea9eSmrg						}
267fe8aea9eSmrg					}
268fe8aea9eSmrg					free(ev);
269fe8aea9eSmrg				} while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, Q)));
270fe8aea9eSmrg			}
271fe8aea9eSmrg
272fe8aea9eSmrg			b->busy = 1;
273fe8aea9eSmrg			if (b->fence.xid) {
274fe8aea9eSmrg				xshmfence_await(b->fence.addr);
275fe8aea9eSmrg				xshmfence_reset(b->fence.addr);
276fe8aea9eSmrg			}
277fe8aea9eSmrg			xcb_present_pixmap(c, win, b->pixmap, b - buffer,
278fe8aea9eSmrg					   0, /* valid */
279fe8aea9eSmrg					   update, /* update */
280fe8aea9eSmrg					   0, /* x_off */
281fe8aea9eSmrg					   0, /* y_off */
282fe8aea9eSmrg					   None,
283fe8aea9eSmrg					   None, /* wait fence */
284fe8aea9eSmrg					   b->fence.xid,
285fe8aea9eSmrg					   present_flags,
286fe8aea9eSmrg					   0, /* target msc */
287fe8aea9eSmrg					   0, /* divisor */
288fe8aea9eSmrg					   0, /* remainder */
289fe8aea9eSmrg					   0, NULL);
290fe8aea9eSmrg			list_move(&b->link, &mru);
291fe8aea9eSmrg			queued++;
292fe8aea9eSmrg			xcb_flush(c);
293fe8aea9eSmrg		}
294fe8aea9eSmrg		clock_gettime(CLOCK_MONOTONIC, &end);
295fe8aea9eSmrg	} while (end.tv_sec < start.tv_sec + 10);
296fe8aea9eSmrg
297fe8aea9eSmrg	while (queued) {
298fe8aea9eSmrg		xcb_present_generic_event_t *ev;
299fe8aea9eSmrg
300fe8aea9eSmrg		ev = (xcb_present_generic_event_t *)
301fe8aea9eSmrg			xcb_wait_for_special_event(c, Q);
302fe8aea9eSmrg		if (ev == NULL)
303fe8aea9eSmrg			abort();
304fe8aea9eSmrg
305fe8aea9eSmrg		do {
306fe8aea9eSmrg			switch (ev->evtype) {
307fe8aea9eSmrg			case XCB_PRESENT_COMPLETE_NOTIFY:
308fe8aea9eSmrg				completed++;
309fe8aea9eSmrg				queued--;
310fe8aea9eSmrg				break;
311fe8aea9eSmrg
312fe8aea9eSmrg			case XCB_PRESENT_EVENT_IDLE_NOTIFY:
313fe8aea9eSmrg				break;
314fe8aea9eSmrg			}
315fe8aea9eSmrg			free(ev);
316fe8aea9eSmrg		} while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, Q)));
317fe8aea9eSmrg	}
318fe8aea9eSmrg	clock_gettime(CLOCK_MONOTONIC, &end);
319fe8aea9eSmrg
320fe8aea9eSmrg	printf("%f\n", completed / (elapsed(&start, &end) / 1000000));
321fe8aea9eSmrg}
322fe8aea9eSmrg
323fe8aea9eSmrgstatic int has_present(Display *dpy)
324fe8aea9eSmrg{
325fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
326fe8aea9eSmrg	xcb_generic_error_t *error = NULL;
327fe8aea9eSmrg	void *reply;
328fe8aea9eSmrg
329fe8aea9eSmrg	reply = xcb_present_query_version_reply(c,
330fe8aea9eSmrg						xcb_present_query_version(c,
331fe8aea9eSmrg									  XCB_PRESENT_MAJOR_VERSION,
332fe8aea9eSmrg									  XCB_PRESENT_MINOR_VERSION),
333fe8aea9eSmrg						&error);
334fe8aea9eSmrg
335fe8aea9eSmrg	free(reply);
336fe8aea9eSmrg	free(error);
337fe8aea9eSmrg	if (reply == NULL) {
338fe8aea9eSmrg		fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy));
339fe8aea9eSmrg		return 0;
340fe8aea9eSmrg	}
341fe8aea9eSmrg
342fe8aea9eSmrg	return 1;
343fe8aea9eSmrg}
344fe8aea9eSmrg
345fe8aea9eSmrgstatic int has_composite(Display *dpy)
346fe8aea9eSmrg{
347fe8aea9eSmrg	int event, error;
348fe8aea9eSmrg	int major, minor;
349fe8aea9eSmrg
350fe8aea9eSmrg	if (!XDamageQueryExtension (dpy, &event, &error))
351fe8aea9eSmrg		return 0;
352fe8aea9eSmrg
353fe8aea9eSmrg	if (!XCompositeQueryExtension(dpy, &event, &error))
354fe8aea9eSmrg		return 0;
355fe8aea9eSmrg
356fe8aea9eSmrg	XCompositeQueryVersion(dpy, &major, &minor);
357fe8aea9eSmrg
358fe8aea9eSmrg	return major > 0 || minor >= 4;
359fe8aea9eSmrg}
360fe8aea9eSmrg
361fe8aea9eSmrgstatic inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
362fe8aea9eSmrg{
363fe8aea9eSmrg	XRRScreenResources *res;
364fe8aea9eSmrg
365fe8aea9eSmrg	res = XRRGetScreenResourcesCurrent(dpy, window);
366fe8aea9eSmrg	if (res == NULL)
367fe8aea9eSmrg		res = XRRGetScreenResources(dpy, window);
368fe8aea9eSmrg
369fe8aea9eSmrg	return res;
370fe8aea9eSmrg}
371fe8aea9eSmrg
372fe8aea9eSmrgstatic XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
373fe8aea9eSmrg{
374fe8aea9eSmrg	int i;
375fe8aea9eSmrg
376fe8aea9eSmrg	for (i = 0; i < res->nmode; i++) {
377fe8aea9eSmrg		if (res->modes[i].id == id)
378fe8aea9eSmrg			return &res->modes[i];
379fe8aea9eSmrg	}
380fe8aea9eSmrg
381fe8aea9eSmrg	return NULL;
382fe8aea9eSmrg}
383fe8aea9eSmrg
384fe8aea9eSmrgstatic void fullscreen(Display *dpy, Window win)
385fe8aea9eSmrg{
386fe8aea9eSmrg	Atom atom = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
387fe8aea9eSmrg	XChangeProperty(dpy, win,
388fe8aea9eSmrg			XInternAtom(dpy, "_NET_WM_STATE", False),
389fe8aea9eSmrg			XA_ATOM, 32, PropModeReplace,
390fe8aea9eSmrg			(unsigned char *)&atom, 1);
391fe8aea9eSmrg}
392fe8aea9eSmrg
393fe8aea9eSmrgstatic int dri3_query_version(Display *dpy, int *major, int *minor)
394fe8aea9eSmrg{
395fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
396fe8aea9eSmrg	xcb_dri3_query_version_reply_t *reply;
397fe8aea9eSmrg	xcb_generic_error_t *error;
398fe8aea9eSmrg
399fe8aea9eSmrg	*major = *minor = -1;
400fe8aea9eSmrg
401fe8aea9eSmrg	reply = xcb_dri3_query_version_reply(c,
402fe8aea9eSmrg					     xcb_dri3_query_version(c,
403fe8aea9eSmrg								    XCB_DRI3_MAJOR_VERSION,
404fe8aea9eSmrg								    XCB_DRI3_MINOR_VERSION),
405fe8aea9eSmrg					     &error);
406fe8aea9eSmrg	free(error);
407fe8aea9eSmrg	if (reply == NULL)
408fe8aea9eSmrg		return -1;
409fe8aea9eSmrg
410fe8aea9eSmrg	*major = reply->major_version;
411fe8aea9eSmrg	*minor = reply->minor_version;
412fe8aea9eSmrg	free(reply);
413fe8aea9eSmrg
414fe8aea9eSmrg	return 0;
415fe8aea9eSmrg}
416fe8aea9eSmrg
417fe8aea9eSmrgstatic int has_dri3(Display *dpy)
418fe8aea9eSmrg{
419fe8aea9eSmrg	const xcb_query_extension_reply_t *ext;
420fe8aea9eSmrg	int major, minor;
421fe8aea9eSmrg
422fe8aea9eSmrg	ext = xcb_get_extension_data(XGetXCBConnection(dpy), &xcb_dri3_id);
423fe8aea9eSmrg	if (ext == NULL || !ext->present)
424fe8aea9eSmrg		return 0;
425fe8aea9eSmrg
426fe8aea9eSmrg	if (dri3_query_version(dpy, &major, &minor) < 0)
427fe8aea9eSmrg		return 0;
428fe8aea9eSmrg
429fe8aea9eSmrg	return major >= 0;
430fe8aea9eSmrg}
431fe8aea9eSmrg
432fe8aea9eSmrgint main(int argc, char **argv)
433fe8aea9eSmrg{
434fe8aea9eSmrg	Display *dpy;
435fe8aea9eSmrg	Window root, win;
436fe8aea9eSmrg	XRRScreenResources *res;
437fe8aea9eSmrg	XRRCrtcInfo **original_crtc;
438fe8aea9eSmrg	XSetWindowAttributes attr;
439fe8aea9eSmrg	enum window { ROOT, FULLSCREEN, WINDOW } w = FULLSCREEN;
440fe8aea9eSmrg	enum visible {REDIRECTED, NORMAL } v = NORMAL;
441fe8aea9eSmrg	enum display { OFF, ON } d = OFF;
442fe8aea9eSmrg	int width, height;
443fe8aea9eSmrg	int i;
444fe8aea9eSmrg
445fe8aea9eSmrg	while ((i = getopt(argc, argv, "d:v:w:")) != -1) {
446fe8aea9eSmrg		switch (i) {
447fe8aea9eSmrg		case 'd':
448fe8aea9eSmrg			if (strcmp(optarg, "off") == 0)
449fe8aea9eSmrg				d = OFF;
450fe8aea9eSmrg			else if (strcmp(optarg, "on") == 0)
451fe8aea9eSmrg				d = ON;
452fe8aea9eSmrg			else
453fe8aea9eSmrg				abort();
454fe8aea9eSmrg			break;
455fe8aea9eSmrg
456fe8aea9eSmrg		case 'v':
457fe8aea9eSmrg			if (strcmp(optarg, "redirected") == 0)
458fe8aea9eSmrg				v = REDIRECTED;
459fe8aea9eSmrg			else if (strcmp(optarg, "normal") == 0)
460fe8aea9eSmrg				v = NORMAL;
461fe8aea9eSmrg			else
462fe8aea9eSmrg				abort();
463fe8aea9eSmrg			break;
464fe8aea9eSmrg
465fe8aea9eSmrg		case 'w':
466fe8aea9eSmrg			if (strcmp(optarg, "fullscreen") == 0)
467fe8aea9eSmrg				w = FULLSCREEN;
468fe8aea9eSmrg			else if (strcmp(optarg, "window") == 0)
469fe8aea9eSmrg				w = WINDOW;
470fe8aea9eSmrg			else if (strcmp(optarg, "root") == 0)
471fe8aea9eSmrg				w = ROOT;
472fe8aea9eSmrg			else
473fe8aea9eSmrg				abort();
474fe8aea9eSmrg			break;
475fe8aea9eSmrg		}
476fe8aea9eSmrg	}
477fe8aea9eSmrg
478fe8aea9eSmrg	attr.override_redirect = 1;
479fe8aea9eSmrg
480fe8aea9eSmrg	dpy = XOpenDisplay(NULL);
481fe8aea9eSmrg	if (dpy == NULL)
482fe8aea9eSmrg		return 77;
483fe8aea9eSmrg
484fe8aea9eSmrg	width = DisplayWidth(dpy, DefaultScreen(dpy));
485fe8aea9eSmrg	height = DisplayHeight(dpy, DefaultScreen(dpy));
486fe8aea9eSmrg
487fe8aea9eSmrg	if (!has_present(dpy))
488fe8aea9eSmrg		return 77;
489fe8aea9eSmrg
490fe8aea9eSmrg	if (!has_dri3(dpy))
491fe8aea9eSmrg		return 77;
492fe8aea9eSmrg
493fe8aea9eSmrg	if (DPMSQueryExtension(dpy, &i, &i))
494fe8aea9eSmrg		DPMSDisable(dpy);
495fe8aea9eSmrg
496fe8aea9eSmrg	root = DefaultRootWindow(dpy);
497fe8aea9eSmrg
498fe8aea9eSmrg	signal(SIGALRM, SIG_IGN);
499fe8aea9eSmrg	XSetErrorHandler(_check_error_handler);
500fe8aea9eSmrg
501fe8aea9eSmrg	res = NULL;
502fe8aea9eSmrg	if (XRRQueryVersion(dpy, &i, &i))
503fe8aea9eSmrg		res = _XRRGetScreenResourcesCurrent(dpy, root);
504fe8aea9eSmrg	if (res == NULL)
505fe8aea9eSmrg		return 77;
506fe8aea9eSmrg
507fe8aea9eSmrg	if (v == REDIRECTED && !has_composite(dpy))
508fe8aea9eSmrg		return 77;
509fe8aea9eSmrg
510fe8aea9eSmrg	original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
511fe8aea9eSmrg	for (i = 0; i < res->ncrtc; i++)
512fe8aea9eSmrg		original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
513fe8aea9eSmrg
514fe8aea9eSmrg	for (i = 0; i < res->ncrtc; i++)
515fe8aea9eSmrg		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
516fe8aea9eSmrg				 0, 0, None, RR_Rotate_0, NULL, 0);
517fe8aea9eSmrg
518fe8aea9eSmrg	if (d != OFF) {
519fe8aea9eSmrg		for (i = 0; i < res->noutput; i++) {
520fe8aea9eSmrg			XRROutputInfo *output;
521fe8aea9eSmrg			XRRModeInfo *mode;
522fe8aea9eSmrg
523fe8aea9eSmrg			output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
524fe8aea9eSmrg			if (output == NULL)
525fe8aea9eSmrg				continue;
526fe8aea9eSmrg
527fe8aea9eSmrg			mode = NULL;
528fe8aea9eSmrg			if (res->nmode)
529fe8aea9eSmrg				mode = lookup_mode(res, output->modes[0]);
530fe8aea9eSmrg			if (mode == NULL)
531fe8aea9eSmrg				continue;
532fe8aea9eSmrg
533fe8aea9eSmrg			XRRSetCrtcConfig(dpy, res, output->crtcs[0], CurrentTime,
534fe8aea9eSmrg					 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
535fe8aea9eSmrg			width = mode->width;
536fe8aea9eSmrg			height = mode->height;
537fe8aea9eSmrg			break;
538fe8aea9eSmrg		}
539fe8aea9eSmrg		if (i == res->noutput) {
540fe8aea9eSmrg			_x_error_occurred = 77;
541fe8aea9eSmrg			goto restore;
542fe8aea9eSmrg		}
543fe8aea9eSmrg	}
544fe8aea9eSmrg
545fe8aea9eSmrg	if (w == ROOT) {
546fe8aea9eSmrg		run(dpy, root);
547fe8aea9eSmrg	} else if (w == FULLSCREEN) {
548fe8aea9eSmrg		win = XCreateWindow(dpy, root,
549fe8aea9eSmrg				    0, 0, width, height, 0,
550fe8aea9eSmrg				    DefaultDepth(dpy, DefaultScreen(dpy)),
551fe8aea9eSmrg				    InputOutput,
552fe8aea9eSmrg				    DefaultVisual(dpy, DefaultScreen(dpy)),
553fe8aea9eSmrg				    CWOverrideRedirect, &attr);
554fe8aea9eSmrg		if (v == REDIRECTED) {
555fe8aea9eSmrg			XCompositeRedirectWindow(dpy, win, CompositeRedirectManual);
556fe8aea9eSmrg			XDamageCreate(dpy, win, XDamageReportRawRectangles);
557fe8aea9eSmrg		} else
558fe8aea9eSmrg			fullscreen(dpy, win);
559fe8aea9eSmrg		XMapWindow(dpy, win);
560fe8aea9eSmrg		run(dpy, win);
561fe8aea9eSmrg	} else if (w == WINDOW) {
562fe8aea9eSmrg		win = XCreateWindow(dpy, root,
563fe8aea9eSmrg				    0, 0, width/2, height/2, 0,
564fe8aea9eSmrg				    DefaultDepth(dpy, DefaultScreen(dpy)),
565fe8aea9eSmrg				    InputOutput,
566fe8aea9eSmrg				    DefaultVisual(dpy, DefaultScreen(dpy)),
567fe8aea9eSmrg				    CWOverrideRedirect, &attr);
568fe8aea9eSmrg		if (v == REDIRECTED) {
569fe8aea9eSmrg			XCompositeRedirectWindow(dpy, win, CompositeRedirectManual);
570fe8aea9eSmrg			XDamageCreate(dpy, win, XDamageReportRawRectangles);
571fe8aea9eSmrg		}
572fe8aea9eSmrg		XMapWindow(dpy, win);
573fe8aea9eSmrg		run(dpy, win);
574fe8aea9eSmrg	}
575fe8aea9eSmrg
576fe8aea9eSmrgrestore:
577fe8aea9eSmrg	for (i = 0; i < res->ncrtc; i++)
578fe8aea9eSmrg		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
579fe8aea9eSmrg				 0, 0, None, RR_Rotate_0, NULL, 0);
580fe8aea9eSmrg
581fe8aea9eSmrg	for (i = 0; i < res->ncrtc; i++)
582fe8aea9eSmrg		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
583fe8aea9eSmrg				 original_crtc[i]->x,
584fe8aea9eSmrg				 original_crtc[i]->y,
585fe8aea9eSmrg				 original_crtc[i]->mode,
586fe8aea9eSmrg				 original_crtc[i]->rotation,
587fe8aea9eSmrg				 original_crtc[i]->outputs,
588fe8aea9eSmrg				 original_crtc[i]->noutput);
589fe8aea9eSmrg
590fe8aea9eSmrg	if (DPMSQueryExtension(dpy, &i, &i))
591fe8aea9eSmrg		DPMSEnable(dpy);
592fe8aea9eSmrg
593fe8aea9eSmrg	XSync(dpy, True);
594fe8aea9eSmrg	return _x_error_occurred;
595fe8aea9eSmrg}
596