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/Xutil.h>
33fe8aea9eSmrg#include <X11/Xlibint.h>
34fe8aea9eSmrg#include <X11/extensions/dpms.h>
35fe8aea9eSmrg#include <X11/extensions/randr.h>
36fe8aea9eSmrg#include <X11/extensions/Xcomposite.h>
37fe8aea9eSmrg#include <X11/extensions/Xdamage.h>
38fe8aea9eSmrg#include <X11/extensions/Xrandr.h>
39fe8aea9eSmrg#include <xcb/xcb.h>
40fe8aea9eSmrg#include <xcb/dri2.h>
41fe8aea9eSmrg#include <xf86drm.h>
42fe8aea9eSmrg
43fe8aea9eSmrg#include <stdio.h>
44fe8aea9eSmrg#include <string.h>
45fe8aea9eSmrg#include <fcntl.h>
46fe8aea9eSmrg#include <unistd.h>
47fe8aea9eSmrg#include <assert.h>
48fe8aea9eSmrg#include <errno.h>
49fe8aea9eSmrg#include <setjmp.h>
50fe8aea9eSmrg#include <signal.h>
51fe8aea9eSmrg
52fe8aea9eSmrg#include <X11/Xlibint.h>
53fe8aea9eSmrg#include <X11/extensions/Xext.h>
54fe8aea9eSmrg#include <X11/extensions/extutil.h>
55fe8aea9eSmrg#include <X11/extensions/dri2proto.h>
56fe8aea9eSmrg#include <X11/extensions/dri2tokens.h>
57fe8aea9eSmrg#include <X11/extensions/Xfixes.h>
58fe8aea9eSmrg
59fe8aea9eSmrgstatic char dri2ExtensionName[] = DRI2_NAME;
60fe8aea9eSmrgstatic XExtensionInfo *dri2Info;
61fe8aea9eSmrgstatic XEXT_GENERATE_CLOSE_DISPLAY (DRI2CloseDisplay, dri2Info)
62fe8aea9eSmrg
63fe8aea9eSmrgstatic Bool
64fe8aea9eSmrgDRI2WireToEvent(Display *dpy, XEvent *event, xEvent *wire);
65fe8aea9eSmrgstatic Status
66fe8aea9eSmrgDRI2EventToWire(Display *dpy, XEvent *event, xEvent *wire);
67fe8aea9eSmrgstatic int
68fe8aea9eSmrgDRI2Error(Display *display, xError *err, XExtCodes *codes, int *ret_code);
69fe8aea9eSmrg
70fe8aea9eSmrgstatic /* const */ XExtensionHooks dri2ExtensionHooks = {
71fe8aea9eSmrg  NULL,                   /* create_gc */
72fe8aea9eSmrg  NULL,                   /* copy_gc */
73fe8aea9eSmrg  NULL,                   /* flush_gc */
74fe8aea9eSmrg  NULL,                   /* free_gc */
75fe8aea9eSmrg  NULL,                   /* create_font */
76fe8aea9eSmrg  NULL,                   /* free_font */
77fe8aea9eSmrg  DRI2CloseDisplay,       /* close_display */
78fe8aea9eSmrg  DRI2WireToEvent,        /* wire_to_event */
79fe8aea9eSmrg  DRI2EventToWire,        /* event_to_wire */
80fe8aea9eSmrg  DRI2Error,              /* error */
81fe8aea9eSmrg  NULL,                   /* error_string */
82fe8aea9eSmrg};
83fe8aea9eSmrg
84fe8aea9eSmrgstatic XEXT_GENERATE_FIND_DISPLAY (DRI2FindDisplay,
85fe8aea9eSmrg                                   dri2Info,
86fe8aea9eSmrg                                   dri2ExtensionName,
87fe8aea9eSmrg                                   &dri2ExtensionHooks,
88fe8aea9eSmrg                                   0, NULL)
89fe8aea9eSmrg
90fe8aea9eSmrgstatic Bool
91fe8aea9eSmrgDRI2WireToEvent(Display *dpy, XEvent *event, xEvent *wire)
92fe8aea9eSmrg{
93fe8aea9eSmrg   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
94fe8aea9eSmrg
95fe8aea9eSmrg   XextCheckExtension(dpy, info, dri2ExtensionName, False);
96fe8aea9eSmrg
97fe8aea9eSmrg   switch ((wire->u.u.type & 0x7f) - info->codes->first_event) {
98fe8aea9eSmrg#ifdef X_DRI2SwapBuffers
99fe8aea9eSmrg   case DRI2_BufferSwapComplete:
100fe8aea9eSmrg      return False;
101fe8aea9eSmrg#endif
102fe8aea9eSmrg#ifdef DRI2_InvalidateBuffers
103fe8aea9eSmrg   case DRI2_InvalidateBuffers:
104fe8aea9eSmrg      return False;
105fe8aea9eSmrg#endif
106fe8aea9eSmrg   default:
107fe8aea9eSmrg      /* client doesn't support server event */
108fe8aea9eSmrg      break;
109fe8aea9eSmrg   }
110fe8aea9eSmrg
111fe8aea9eSmrg   return False;
112fe8aea9eSmrg}
113fe8aea9eSmrg
114fe8aea9eSmrg/* We don't actually support this.  It doesn't make sense for clients to
115fe8aea9eSmrg * send each other DRI2 events.
116fe8aea9eSmrg */
117fe8aea9eSmrgstatic Status
118fe8aea9eSmrgDRI2EventToWire(Display *dpy, XEvent *event, xEvent *wire)
119fe8aea9eSmrg{
120fe8aea9eSmrg   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
121fe8aea9eSmrg
122fe8aea9eSmrg   XextCheckExtension(dpy, info, dri2ExtensionName, False);
123fe8aea9eSmrg
124fe8aea9eSmrg   switch (event->type) {
125fe8aea9eSmrg   default:
126fe8aea9eSmrg      /* client doesn't support server event */
127fe8aea9eSmrg      break;
128fe8aea9eSmrg   }
129fe8aea9eSmrg
130fe8aea9eSmrg   return Success;
131fe8aea9eSmrg}
132fe8aea9eSmrg
133fe8aea9eSmrgstatic int
134fe8aea9eSmrgDRI2Error(Display *display, xError *err, XExtCodes *codes, int *ret_code)
135fe8aea9eSmrg{
136fe8aea9eSmrg	if (err->majorCode == codes->major_opcode &&
137fe8aea9eSmrg	    err->errorCode == BadDrawable &&
138fe8aea9eSmrg	    err->minorCode == X_DRI2CopyRegion)
139fe8aea9eSmrg		return True;
140fe8aea9eSmrg
141fe8aea9eSmrg	/* If the X drawable was destroyed before the GLX drawable, the
142fe8aea9eSmrg	 * DRI2 drawble will be gone by the time we call
143fe8aea9eSmrg	 * DRI2DestroyDrawable.  So just ignore BadDrawable here. */
144fe8aea9eSmrg	if (err->majorCode == codes->major_opcode &&
145fe8aea9eSmrg	    err->errorCode == BadDrawable &&
146fe8aea9eSmrg	    err->minorCode == X_DRI2DestroyDrawable)
147fe8aea9eSmrg		return True;
148fe8aea9eSmrg
149fe8aea9eSmrg	/* If the server is non-local DRI2Connect will raise BadRequest.
150fe8aea9eSmrg	 * Swallow this so that DRI2Connect can signal this in its return code */
151fe8aea9eSmrg	if (err->majorCode == codes->major_opcode &&
152fe8aea9eSmrg	    err->minorCode == X_DRI2Connect &&
153fe8aea9eSmrg	    err->errorCode == BadRequest) {
154fe8aea9eSmrg		*ret_code = False;
155fe8aea9eSmrg		return True;
156fe8aea9eSmrg	}
157fe8aea9eSmrg
158fe8aea9eSmrg	return False;
159fe8aea9eSmrg}
160fe8aea9eSmrg
161fe8aea9eSmrgstatic Bool
162fe8aea9eSmrgDRI2QueryExtension(Display * dpy, int *eventBase, int *errorBase)
163fe8aea9eSmrg{
164fe8aea9eSmrg	XExtDisplayInfo *info = DRI2FindDisplay(dpy);
165fe8aea9eSmrg
166fe8aea9eSmrg	if (XextHasExtension(info)) {
167fe8aea9eSmrg		*eventBase = info->codes->first_event;
168fe8aea9eSmrg		*errorBase = info->codes->first_error;
169fe8aea9eSmrg		return True;
170fe8aea9eSmrg	}
171fe8aea9eSmrg
172fe8aea9eSmrg	return False;
173fe8aea9eSmrg}
174fe8aea9eSmrg
175fe8aea9eSmrgstatic Bool
176fe8aea9eSmrgDRI2Connect(Display * dpy, XID window, char **driverName, char **deviceName)
177fe8aea9eSmrg{
178fe8aea9eSmrg	XExtDisplayInfo *info = DRI2FindDisplay(dpy);
179fe8aea9eSmrg	xDRI2ConnectReply rep;
180fe8aea9eSmrg	xDRI2ConnectReq *req;
181fe8aea9eSmrg
182fe8aea9eSmrg	XextCheckExtension(dpy, info, dri2ExtensionName, False);
183fe8aea9eSmrg
184fe8aea9eSmrg	LockDisplay(dpy);
185fe8aea9eSmrg	GetReq(DRI2Connect, req);
186fe8aea9eSmrg	req->reqType = info->codes->major_opcode;
187fe8aea9eSmrg	req->dri2ReqType = X_DRI2Connect;
188fe8aea9eSmrg	req->window = window;
189fe8aea9eSmrg	req->driverType = DRI2DriverDRI;
190fe8aea9eSmrg	if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) {
191fe8aea9eSmrg		UnlockDisplay(dpy);
192fe8aea9eSmrg		SyncHandle();
193fe8aea9eSmrg		return False;
194fe8aea9eSmrg	}
195fe8aea9eSmrg
196fe8aea9eSmrg	if (rep.driverNameLength == 0 && rep.deviceNameLength == 0) {
197fe8aea9eSmrg		UnlockDisplay(dpy);
198fe8aea9eSmrg		SyncHandle();
199fe8aea9eSmrg		return False;
200fe8aea9eSmrg	}
201fe8aea9eSmrg
202fe8aea9eSmrg	*driverName = Xmalloc(rep.driverNameLength + 1);
203fe8aea9eSmrg	if (*driverName == NULL) {
204fe8aea9eSmrg		_XEatData(dpy,
205fe8aea9eSmrg			  ((rep.driverNameLength + 3) & ~3) +
206fe8aea9eSmrg			  ((rep.deviceNameLength + 3) & ~3));
207fe8aea9eSmrg		UnlockDisplay(dpy);
208fe8aea9eSmrg		SyncHandle();
209fe8aea9eSmrg		return False;
210fe8aea9eSmrg	}
211fe8aea9eSmrg	_XReadPad(dpy, *driverName, rep.driverNameLength);
212fe8aea9eSmrg	(*driverName)[rep.driverNameLength] = '\0';
213fe8aea9eSmrg
214fe8aea9eSmrg	*deviceName = Xmalloc(rep.deviceNameLength + 1);
215fe8aea9eSmrg	if (*deviceName == NULL) {
216fe8aea9eSmrg		Xfree(*driverName);
217fe8aea9eSmrg		_XEatData(dpy, ((rep.deviceNameLength + 3) & ~3));
218fe8aea9eSmrg		UnlockDisplay(dpy);
219fe8aea9eSmrg		SyncHandle();
220fe8aea9eSmrg		return False;
221fe8aea9eSmrg	}
222fe8aea9eSmrg	_XReadPad(dpy, *deviceName, rep.deviceNameLength);
223fe8aea9eSmrg	(*deviceName)[rep.deviceNameLength] = '\0';
224fe8aea9eSmrg
225fe8aea9eSmrg	UnlockDisplay(dpy);
226fe8aea9eSmrg	SyncHandle();
227fe8aea9eSmrg
228fe8aea9eSmrg	return True;
229fe8aea9eSmrg}
230fe8aea9eSmrg
231fe8aea9eSmrgstatic Bool
232fe8aea9eSmrgDRI2Authenticate(Display * dpy, XID window, unsigned int magic)
233fe8aea9eSmrg{
234fe8aea9eSmrg	XExtDisplayInfo *info = DRI2FindDisplay(dpy);
235fe8aea9eSmrg	xDRI2AuthenticateReq *req;
236fe8aea9eSmrg	xDRI2AuthenticateReply rep;
237fe8aea9eSmrg
238fe8aea9eSmrg	XextCheckExtension(dpy, info, dri2ExtensionName, False);
239fe8aea9eSmrg
240fe8aea9eSmrg	LockDisplay(dpy);
241fe8aea9eSmrg	GetReq(DRI2Authenticate, req);
242fe8aea9eSmrg	req->reqType = info->codes->major_opcode;
243fe8aea9eSmrg	req->dri2ReqType = X_DRI2Authenticate;
244fe8aea9eSmrg	req->window = window;
245fe8aea9eSmrg	req->magic = magic;
246fe8aea9eSmrg
247fe8aea9eSmrg	if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) {
248fe8aea9eSmrg		UnlockDisplay(dpy);
249fe8aea9eSmrg		SyncHandle();
250fe8aea9eSmrg		return False;
251fe8aea9eSmrg	}
252fe8aea9eSmrg
253fe8aea9eSmrg	UnlockDisplay(dpy);
254fe8aea9eSmrg	SyncHandle();
255fe8aea9eSmrg
256fe8aea9eSmrg	return rep.authenticated;
257fe8aea9eSmrg}
258fe8aea9eSmrg
259fe8aea9eSmrgstatic void
260fe8aea9eSmrgDRI2CreateDrawable(Display * dpy, XID drawable)
261fe8aea9eSmrg{
262fe8aea9eSmrg	XExtDisplayInfo *info = DRI2FindDisplay(dpy);
263fe8aea9eSmrg	xDRI2CreateDrawableReq *req;
264fe8aea9eSmrg
265fe8aea9eSmrg	XextSimpleCheckExtension(dpy, info, dri2ExtensionName);
266fe8aea9eSmrg
267fe8aea9eSmrg	LockDisplay(dpy);
268fe8aea9eSmrg	GetReq(DRI2CreateDrawable, req);
269fe8aea9eSmrg	req->reqType = info->codes->major_opcode;
270fe8aea9eSmrg	req->dri2ReqType = X_DRI2CreateDrawable;
271fe8aea9eSmrg	req->drawable = drawable;
272fe8aea9eSmrg	UnlockDisplay(dpy);
273fe8aea9eSmrg	SyncHandle();
274fe8aea9eSmrg}
275fe8aea9eSmrg
276fe8aea9eSmrgstatic void DRI2SwapInterval(Display *dpy, XID drawable, int interval)
277fe8aea9eSmrg{
278fe8aea9eSmrg    XExtDisplayInfo *info = DRI2FindDisplay(dpy);
279fe8aea9eSmrg    xDRI2SwapIntervalReq *req;
280fe8aea9eSmrg
281fe8aea9eSmrg    XextSimpleCheckExtension (dpy, info, dri2ExtensionName);
282fe8aea9eSmrg
283fe8aea9eSmrg    LockDisplay(dpy);
284fe8aea9eSmrg    GetReq(DRI2SwapInterval, req);
285fe8aea9eSmrg    req->reqType = info->codes->major_opcode;
286fe8aea9eSmrg    req->dri2ReqType = X_DRI2SwapInterval;
287fe8aea9eSmrg    req->drawable = drawable;
288fe8aea9eSmrg    req->interval = interval;
289fe8aea9eSmrg    UnlockDisplay(dpy);
290fe8aea9eSmrg    SyncHandle();
291fe8aea9eSmrg}
292fe8aea9eSmrg
293fe8aea9eSmrgstatic int _x_error_occurred;
294fe8aea9eSmrg
295fe8aea9eSmrgstatic int
296fe8aea9eSmrg_check_error_handler(Display     *display,
297fe8aea9eSmrg		     XErrorEvent *event)
298fe8aea9eSmrg{
299fe8aea9eSmrg	fprintf(stderr,
300fe8aea9eSmrg		"X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n",
301fe8aea9eSmrg	       DisplayString(display),
302fe8aea9eSmrg	       event->serial,
303fe8aea9eSmrg	       event->error_code,
304fe8aea9eSmrg	       event->request_code,
305fe8aea9eSmrg	       event->minor_code);
306fe8aea9eSmrg	_x_error_occurred++;
307fe8aea9eSmrg	return False; /* ignored */
308fe8aea9eSmrg}
309fe8aea9eSmrg
310fe8aea9eSmrgstatic double elapsed(const struct timespec *start,
311fe8aea9eSmrg		      const struct timespec *end)
312fe8aea9eSmrg{
313fe8aea9eSmrg	return 1e6*(end->tv_sec - start->tv_sec) + (end->tv_nsec - start->tv_nsec)/1000;
314fe8aea9eSmrg}
315fe8aea9eSmrg
316fe8aea9eSmrgstatic void run(Display *dpy, Window win)
317fe8aea9eSmrg{
318fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
319fe8aea9eSmrg	struct timespec start, end;
320fe8aea9eSmrg	int n, completed = 0;
321fe8aea9eSmrg
322fe8aea9eSmrg	clock_gettime(CLOCK_MONOTONIC, &start);
323fe8aea9eSmrg	do {
324fe8aea9eSmrg		for (n = 0; n < 1000; n++) {
325fe8aea9eSmrg			unsigned int attachments[] = { DRI2BufferBackLeft };
326fe8aea9eSmrg			unsigned int seq[2];
327fe8aea9eSmrg
328fe8aea9eSmrg			seq[0] = xcb_dri2_swap_buffers_unchecked(c, win,
329fe8aea9eSmrg								 0, 0, 0, 0, 0, 0).sequence;
330fe8aea9eSmrg
331fe8aea9eSmrg
332fe8aea9eSmrg			seq[1] = xcb_dri2_get_buffers_unchecked(c, win,
333fe8aea9eSmrg								1, 1, attachments).sequence;
334fe8aea9eSmrg
335fe8aea9eSmrg			xcb_flush(c);
336fe8aea9eSmrg			xcb_discard_reply(c, seq[0]);
337fe8aea9eSmrg			xcb_discard_reply(c, seq[1]);
338fe8aea9eSmrg			completed++;
339fe8aea9eSmrg		}
340fe8aea9eSmrg		clock_gettime(CLOCK_MONOTONIC, &end);
341fe8aea9eSmrg	} while (end.tv_sec < start.tv_sec + 10);
342fe8aea9eSmrg
343fe8aea9eSmrg	printf("%f\n", completed / (elapsed(&start, &end) / 1000000));
344fe8aea9eSmrg}
345fe8aea9eSmrg
346fe8aea9eSmrgstatic inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
347fe8aea9eSmrg{
348fe8aea9eSmrg	XRRScreenResources *res;
349fe8aea9eSmrg
350fe8aea9eSmrg	res = XRRGetScreenResourcesCurrent(dpy, window);
351fe8aea9eSmrg	if (res == NULL)
352fe8aea9eSmrg		res = XRRGetScreenResources(dpy, window);
353fe8aea9eSmrg
354fe8aea9eSmrg	return res;
355fe8aea9eSmrg}
356fe8aea9eSmrg
357fe8aea9eSmrgstatic XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
358fe8aea9eSmrg{
359fe8aea9eSmrg	int i;
360fe8aea9eSmrg
361fe8aea9eSmrg	for (i = 0; i < res->nmode; i++) {
362fe8aea9eSmrg		if (res->modes[i].id == id)
363fe8aea9eSmrg			return &res->modes[i];
364fe8aea9eSmrg	}
365fe8aea9eSmrg
366fe8aea9eSmrg	return NULL;
367fe8aea9eSmrg}
368fe8aea9eSmrg
369fe8aea9eSmrgstatic int dri2_open(Display *dpy)
370fe8aea9eSmrg{
371fe8aea9eSmrg	drm_auth_t auth;
372fe8aea9eSmrg	char *driver, *device;
373fe8aea9eSmrg	int fd;
374fe8aea9eSmrg
375fe8aea9eSmrg	if (!DRI2QueryExtension(dpy, &fd, &fd))
376fe8aea9eSmrg		return -1;
377fe8aea9eSmrg
378fe8aea9eSmrg	if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device))
379fe8aea9eSmrg		return -1;
380fe8aea9eSmrg
381fe8aea9eSmrg	fd = open(device, O_RDWR);
382fe8aea9eSmrg	if (fd < 0)
383fe8aea9eSmrg		return -1;
384fe8aea9eSmrg
385fe8aea9eSmrg	if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
386fe8aea9eSmrg		return -1;
387fe8aea9eSmrg
388fe8aea9eSmrg	if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic))
389fe8aea9eSmrg		return -1;
390fe8aea9eSmrg
391fe8aea9eSmrg	return fd;
392fe8aea9eSmrg}
393fe8aea9eSmrg
394fe8aea9eSmrgstatic void fullscreen(Display *dpy, Window win)
395fe8aea9eSmrg{
396fe8aea9eSmrg	Atom atom = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
397fe8aea9eSmrg	XChangeProperty(dpy, win,
398fe8aea9eSmrg			XInternAtom(dpy, "_NET_WM_STATE", False),
399fe8aea9eSmrg			XA_ATOM, 32, PropModeReplace,
400fe8aea9eSmrg			(unsigned char *)&atom, 1);
401fe8aea9eSmrg}
402fe8aea9eSmrg
403fe8aea9eSmrgstatic int has_composite(Display *dpy)
404fe8aea9eSmrg{
405fe8aea9eSmrg	int event, error;
406fe8aea9eSmrg	int major, minor;
407fe8aea9eSmrg
408fe8aea9eSmrg	if (!XDamageQueryExtension (dpy, &event, &error))
409fe8aea9eSmrg		return 0;
410fe8aea9eSmrg
411fe8aea9eSmrg	if (!XCompositeQueryExtension(dpy, &event, &error))
412fe8aea9eSmrg		return 0;
413fe8aea9eSmrg
414fe8aea9eSmrg	XCompositeQueryVersion(dpy, &major, &minor);
415fe8aea9eSmrg
416fe8aea9eSmrg	return major > 0 || minor >= 4;
417fe8aea9eSmrg}
418fe8aea9eSmrg
419fe8aea9eSmrgint main(int argc, char **argv)
420fe8aea9eSmrg{
421fe8aea9eSmrg	Display *dpy;
422fe8aea9eSmrg	Window root, win;
423fe8aea9eSmrg	XRRScreenResources *res;
424fe8aea9eSmrg	XRRCrtcInfo **original_crtc;
425fe8aea9eSmrg	XSetWindowAttributes attr;
426fe8aea9eSmrg	enum window { ROOT, FULLSCREEN, WINDOW } w = FULLSCREEN;
427fe8aea9eSmrg	enum visible {REDIRECTED, NORMAL } v = NORMAL;
428fe8aea9eSmrg	enum display { OFF, ON } d = OFF;
429fe8aea9eSmrg	int width, height;
430fe8aea9eSmrg	int i, fd;
431fe8aea9eSmrg	int c;
432fe8aea9eSmrg
433fe8aea9eSmrg	while ((c = getopt(argc, argv, "d:v:w:")) != -1) {
434fe8aea9eSmrg		switch (c) {
435fe8aea9eSmrg		case 'd':
436fe8aea9eSmrg			if (strcmp(optarg, "off") == 0)
437fe8aea9eSmrg				d = OFF;
438fe8aea9eSmrg			else if (strcmp(optarg, "on") == 0)
439fe8aea9eSmrg				d = ON;
440fe8aea9eSmrg			else
441fe8aea9eSmrg				abort();
442fe8aea9eSmrg			break;
443fe8aea9eSmrg
444fe8aea9eSmrg		case 'v':
445fe8aea9eSmrg			if (strcmp(optarg, "redirected") == 0)
446fe8aea9eSmrg				v = REDIRECTED;
447fe8aea9eSmrg			else if (strcmp(optarg, "normal") == 0)
448fe8aea9eSmrg				v = NORMAL;
449fe8aea9eSmrg			else
450fe8aea9eSmrg				abort();
451fe8aea9eSmrg			break;
452fe8aea9eSmrg
453fe8aea9eSmrg		case 'w':
454fe8aea9eSmrg			if (strcmp(optarg, "fullscreen") == 0)
455fe8aea9eSmrg				w = FULLSCREEN;
456fe8aea9eSmrg			else if (strcmp(optarg, "window") == 0)
457fe8aea9eSmrg				w = WINDOW;
458fe8aea9eSmrg			else if (strcmp(optarg, "root") == 0)
459fe8aea9eSmrg				w = ROOT;
460fe8aea9eSmrg			else
461fe8aea9eSmrg				abort();
462fe8aea9eSmrg			break;
463fe8aea9eSmrg		}
464fe8aea9eSmrg	}
465fe8aea9eSmrg
466fe8aea9eSmrg	attr.override_redirect = 1;
467fe8aea9eSmrg
468fe8aea9eSmrg	dpy = XOpenDisplay(NULL);
469fe8aea9eSmrg	if (dpy == NULL)
470fe8aea9eSmrg		return 77;
471fe8aea9eSmrg
472fe8aea9eSmrg	width = DisplayWidth(dpy, DefaultScreen(dpy));
473fe8aea9eSmrg	height = DisplayHeight(dpy, DefaultScreen(dpy));
474fe8aea9eSmrg
475fe8aea9eSmrg	fd = dri2_open(dpy);
476fe8aea9eSmrg	if (fd < 0)
477fe8aea9eSmrg		return 77;
478fe8aea9eSmrg
479fe8aea9eSmrg	if (DPMSQueryExtension(dpy, &i, &i))
480fe8aea9eSmrg		DPMSDisable(dpy);
481fe8aea9eSmrg
482fe8aea9eSmrg	root = DefaultRootWindow(dpy);
483fe8aea9eSmrg
484fe8aea9eSmrg	signal(SIGALRM, SIG_IGN);
485fe8aea9eSmrg	XSetErrorHandler(_check_error_handler);
486fe8aea9eSmrg
487fe8aea9eSmrg	res = NULL;
488fe8aea9eSmrg	if (XRRQueryVersion(dpy, &i, &i))
489fe8aea9eSmrg		res = _XRRGetScreenResourcesCurrent(dpy, root);
490fe8aea9eSmrg	if (res == NULL)
491fe8aea9eSmrg		return 77;
492fe8aea9eSmrg
493fe8aea9eSmrg	if (v == REDIRECTED && !has_composite(dpy))
494fe8aea9eSmrg		return 77;
495fe8aea9eSmrg
496fe8aea9eSmrg	original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
497fe8aea9eSmrg	for (i = 0; i < res->ncrtc; i++)
498fe8aea9eSmrg		original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
499fe8aea9eSmrg
500fe8aea9eSmrg	for (i = 0; i < res->ncrtc; i++)
501fe8aea9eSmrg		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
502fe8aea9eSmrg				 0, 0, None, RR_Rotate_0, NULL, 0);
503fe8aea9eSmrg
504fe8aea9eSmrg	DRI2CreateDrawable(dpy, root);
505fe8aea9eSmrg	DRI2SwapInterval(dpy, root, 0);
506fe8aea9eSmrg
507fe8aea9eSmrg	if (d != OFF) {
508fe8aea9eSmrg		for (i = 0; i < res->noutput; i++) {
509fe8aea9eSmrg			XRROutputInfo *output;
510fe8aea9eSmrg			XRRModeInfo *mode;
511fe8aea9eSmrg
512fe8aea9eSmrg			output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
513fe8aea9eSmrg			if (output == NULL)
514fe8aea9eSmrg				continue;
515fe8aea9eSmrg
516fe8aea9eSmrg			mode = NULL;
517fe8aea9eSmrg			if (res->nmode)
518fe8aea9eSmrg				mode = lookup_mode(res, output->modes[0]);
519fe8aea9eSmrg			if (mode == NULL)
520fe8aea9eSmrg				continue;
521fe8aea9eSmrg
522fe8aea9eSmrg			XRRSetCrtcConfig(dpy, res, output->crtcs[0], CurrentTime,
523fe8aea9eSmrg					 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
524fe8aea9eSmrg			width = mode->width;
525fe8aea9eSmrg			height = mode->height;
526fe8aea9eSmrg			break;
527fe8aea9eSmrg		}
528fe8aea9eSmrg		if (i == res->noutput) {
529fe8aea9eSmrg			_x_error_occurred = 77;
530fe8aea9eSmrg			goto restore;
531fe8aea9eSmrg		}
532fe8aea9eSmrg	}
533fe8aea9eSmrg
534fe8aea9eSmrg	if (w == ROOT) {
535fe8aea9eSmrg		run(dpy, root);
536fe8aea9eSmrg	} else if (w == FULLSCREEN) {
537fe8aea9eSmrg		win = XCreateWindow(dpy, root,
538fe8aea9eSmrg				    0, 0, width, height, 0,
539fe8aea9eSmrg				    DefaultDepth(dpy, DefaultScreen(dpy)),
540fe8aea9eSmrg				    InputOutput,
541fe8aea9eSmrg				    DefaultVisual(dpy, DefaultScreen(dpy)),
542fe8aea9eSmrg				    CWOverrideRedirect, &attr);
543fe8aea9eSmrg		DRI2CreateDrawable(dpy, win);
544fe8aea9eSmrg		DRI2SwapInterval(dpy, win, 0);
545fe8aea9eSmrg		if (v == REDIRECTED) {
546fe8aea9eSmrg			XCompositeRedirectWindow(dpy, win, CompositeRedirectManual);
547fe8aea9eSmrg			XDamageCreate(dpy, win, XDamageReportRawRectangles);
548fe8aea9eSmrg		} else
549fe8aea9eSmrg			fullscreen(dpy, win);
550fe8aea9eSmrg		XMapWindow(dpy, win);
551fe8aea9eSmrg		run(dpy, win);
552fe8aea9eSmrg	} else if (w == WINDOW) {
553fe8aea9eSmrg		win = XCreateWindow(dpy, root,
554fe8aea9eSmrg				    0, 0, width/2, height/2, 0,
555fe8aea9eSmrg				    DefaultDepth(dpy, DefaultScreen(dpy)),
556fe8aea9eSmrg				    InputOutput,
557fe8aea9eSmrg				    DefaultVisual(dpy, DefaultScreen(dpy)),
558fe8aea9eSmrg				    CWOverrideRedirect, &attr);
559fe8aea9eSmrg		DRI2CreateDrawable(dpy, win);
560fe8aea9eSmrg		DRI2SwapInterval(dpy, win, 0);
561fe8aea9eSmrg		if (v == REDIRECTED) {
562fe8aea9eSmrg			XCompositeRedirectWindow(dpy, win, CompositeRedirectManual);
563fe8aea9eSmrg			XDamageCreate(dpy, win, XDamageReportRawRectangles);
564fe8aea9eSmrg		}
565fe8aea9eSmrg		XMapWindow(dpy, win);
566fe8aea9eSmrg		run(dpy, win);
567fe8aea9eSmrg	}
568fe8aea9eSmrg
569fe8aea9eSmrgrestore:
570fe8aea9eSmrg	for (i = 0; i < res->ncrtc; i++)
571fe8aea9eSmrg		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
572fe8aea9eSmrg				 0, 0, None, RR_Rotate_0, NULL, 0);
573fe8aea9eSmrg
574fe8aea9eSmrg	for (i = 0; i < res->ncrtc; i++)
575fe8aea9eSmrg		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
576fe8aea9eSmrg				 original_crtc[i]->x,
577fe8aea9eSmrg				 original_crtc[i]->y,
578fe8aea9eSmrg				 original_crtc[i]->mode,
579fe8aea9eSmrg				 original_crtc[i]->rotation,
580fe8aea9eSmrg				 original_crtc[i]->outputs,
581fe8aea9eSmrg				 original_crtc[i]->noutput);
582fe8aea9eSmrg
583fe8aea9eSmrg	if (DPMSQueryExtension(dpy, &i, &i))
584fe8aea9eSmrg		DPMSEnable(dpy);
585fe8aea9eSmrg
586fe8aea9eSmrg	XSync(dpy, True);
587fe8aea9eSmrg	return _x_error_occurred;
588fe8aea9eSmrg}
589