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