1 /*
2 * Copyright (c) 2015 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <X11/Xlib.h>
30 #include <X11/Xatom.h>
31 #include <X11/Xlib-xcb.h>
32 #include <X11/xshmfence.h>
33 #include <X11/Xutil.h>
34 #include <X11/Xlibint.h>
35 #include <X11/extensions/Xcomposite.h>
36 #include <X11/extensions/Xdamage.h>
37 #include <X11/extensions/dpms.h>
38 #include <X11/extensions/randr.h>
39 #include <X11/extensions/Xrandr.h>
40 #include <xcb/xcb.h>
41 #include <xcb/present.h>
42 #include <xcb/dri3.h>
43 #include <xcb/xfixes.h>
44 #include <xf86drm.h>
45 #include <i915_drm.h>
46
47 #include <stdio.h>
48 #include <string.h>
49 #include <fcntl.h>
50 #include <unistd.h>
51 #include <assert.h>
52 #include <errno.h>
53 #include <setjmp.h>
54 #include <signal.h>
55 #include <sys/wait.h>
56
57 #include "dri3.h"
58
59 static int _x_error_occurred;
60 static uint32_t stamp;
61
62 struct list {
63 struct list *next, *prev;
64 };
65
66 static void
list_init(struct list * list)67 list_init(struct list *list)
68 {
69 list->next = list->prev = list;
70 }
71
72 static inline void
__list_add(struct list * entry,struct list * prev,struct list * next)73 __list_add(struct list *entry,
74 struct list *prev,
75 struct list *next)
76 {
77 next->prev = entry;
78 entry->next = next;
79 entry->prev = prev;
80 prev->next = entry;
81 }
82
83 static inline void
list_add(struct list * entry,struct list * head)84 list_add(struct list *entry, struct list *head)
85 {
86 __list_add(entry, head, head->next);
87 }
88
89 static inline void
__list_del(struct list * prev,struct list * next)90 __list_del(struct list *prev, struct list *next)
91 {
92 next->prev = prev;
93 prev->next = next;
94 }
95
96 static inline void
_list_del(struct list * entry)97 _list_del(struct list *entry)
98 {
99 __list_del(entry->prev, entry->next);
100 }
101
102 static inline void
list_move(struct list * list,struct list * head)103 list_move(struct list *list, struct list *head)
104 {
105 if (list->prev != head) {
106 _list_del(list);
107 list_add(list, head);
108 }
109 }
110
111 #define __container_of(ptr, sample, member) \
112 (void *)((char *)(ptr) - ((char *)&(sample)->member - (char *)(sample)))
113
114 #define list_for_each_entry(pos, head, member) \
115 for (pos = __container_of((head)->next, pos, member); \
116 &pos->member != (head); \
117 pos = __container_of(pos->member.next, pos, member))
118
119 static int
_check_error_handler(Display * display,XErrorEvent * event)120 _check_error_handler(Display *display,
121 XErrorEvent *event)
122 {
123 if (_x_error_occurred < 0)
124 return True;
125
126 printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n",
127 DisplayString(display),
128 event->serial,
129 event->error_code,
130 event->request_code,
131 event->minor_code);
132 _x_error_occurred++;
133 return False; /* ignored */
134 }
135
elapsed(const struct timespec * start,const struct timespec * end)136 static double elapsed(const struct timespec *start,
137 const struct timespec *end)
138 {
139 return 1e6*(end->tv_sec - start->tv_sec) + (end->tv_nsec - start->tv_nsec)/1000;
140 }
141
142 struct buffer {
143 struct list link;
144 Pixmap pixmap;
145 struct dri3_fence fence;
146 int fd;
147 int busy;
148 int id;
149 };
150
151 #define DRI3 1
152 #define NOCOPY 2
153 #define ASYNC 4
run(Display * dpy,Window win,const char * name,unsigned options)154 static void run(Display *dpy, Window win, const char *name, unsigned options)
155 {
156 xcb_connection_t *c = XGetXCBConnection(dpy);
157 struct timespec start, end;
158 #define N_BACK 8
159 char test_name[128];
160 struct buffer buffer[N_BACK];
161 struct list mru;
162 Window root;
163 unsigned int width, height;
164 unsigned border, depth;
165 unsigned present_flags = 0;
166 xcb_xfixes_region_t update = 0;
167 int completed = 0;
168 int queued = 0;
169 uint32_t eid = 0;
170 void *Q = NULL;
171 int i, n;
172
173 list_init(&mru);
174
175 XGetGeometry(dpy, win,
176 &root, &i, &n, &width, &height, &border, &depth);
177
178 _x_error_occurred = 0;
179
180 for (n = 0; n < N_BACK; n++) {
181 buffer[n].pixmap = xcb_generate_id(c);
182 xcb_create_pixmap(c, depth, buffer[n].pixmap, win,
183 width, height);
184 buffer[n].fence.xid = 0;
185 buffer[n].fd = -1;
186 buffer[n].id = n;
187 if (options & DRI3) {
188 xcb_dri3_buffer_from_pixmap_reply_t *reply;
189 int *fds;
190
191 if (dri3_create_fence(dpy, win, &buffer[n].fence))
192 return;
193
194 reply = xcb_dri3_buffer_from_pixmap_reply (c,
195 xcb_dri3_buffer_from_pixmap(c, buffer[n].pixmap),
196 NULL);
197 if (reply == NULL)
198 return;
199
200 fds = xcb_dri3_buffer_from_pixmap_reply_fds (c, reply);
201 buffer[n].fd = fds[0];
202 free(reply);
203
204 /* start idle */
205 xshmfence_trigger(buffer[n].fence.addr);
206 }
207 buffer[n].busy = 0;
208 list_add(&buffer[n].link, &mru);
209 }
210 if (options & ASYNC)
211 present_flags |= XCB_PRESENT_OPTION_ASYNC;
212 if (options & NOCOPY) {
213 update = xcb_generate_id(c);
214 xcb_xfixes_create_region(c, update, 0, NULL);
215 present_flags |= XCB_PRESENT_OPTION_COPY;
216 }
217
218 if (!(options & DRI3)) {
219 eid = xcb_generate_id(c);
220 xcb_present_select_input(c, eid, win,
221 (options & NOCOPY ? 0 : XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY) |
222 XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
223 Q = xcb_register_for_special_xge(c, &xcb_present_id, eid, &stamp);
224 }
225
226 clock_gettime(CLOCK_MONOTONIC, &start);
227 do {
228 for (n = 0; n < 1000; n++) {
229 struct buffer *tmp = NULL, *b = NULL;
230 retry:
231 list_for_each_entry(tmp, &mru, link) {
232 if (tmp->fence.xid)
233 tmp->busy = !xshmfence_query(tmp->fence.addr);
234 if (!tmp->busy) {
235 b = tmp;
236 break;
237 }
238 }
239 if (options & DRI3) {
240 if (b == NULL)
241 goto retry;
242
243 xshmfence_reset(b->fence.addr);
244 queued--;
245 completed++;
246 } else while (b == NULL) {
247 xcb_present_generic_event_t *ev;
248
249 ev = (xcb_present_generic_event_t *)
250 xcb_wait_for_special_event(c, Q);
251 if (ev == NULL)
252 abort();
253
254 do {
255 switch (ev->evtype) {
256 case XCB_PRESENT_COMPLETE_NOTIFY:
257 completed++;
258 queued--;
259 break;
260
261 case XCB_PRESENT_EVENT_IDLE_NOTIFY:
262 {
263 xcb_present_idle_notify_event_t *ie = (xcb_present_idle_notify_event_t *)ev;
264 assert(ie->serial < N_BACK);
265 buffer[ie->serial].busy = 0;
266 if (b == NULL)
267 b = &buffer[ie->serial];
268 break;
269 }
270 }
271 free(ev);
272 } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, Q)));
273 }
274
275 b->busy = (options & NOCOPY) == 0;
276 xcb_present_pixmap(c, win, b->pixmap, b->id,
277 0, /* valid */
278 update, /* update */
279 0, /* x_off */
280 0, /* y_off */
281 None,
282 None, /* wait fence */
283 b->fence.xid,
284 present_flags,
285 0, /* target msc */
286 0, /* divisor */
287 0, /* remainder */
288 0, NULL);
289 list_move(&b->link, &mru);
290 queued++;
291 xcb_flush(c);
292 }
293 clock_gettime(CLOCK_MONOTONIC, &end);
294 } while (end.tv_sec < start.tv_sec + 10);
295
296 if (options & DRI3) {
297 struct buffer *b = NULL;
298 XID pixmap;
299
300 pixmap = xcb_generate_id(c);
301 xcb_create_pixmap(c, depth, pixmap, win, width, height);
302 xcb_present_pixmap(c, win, pixmap, 0xdeadbeef,
303 0, /* valid */
304 None, /* update */
305 0, /* x_off */
306 0, /* y_off */
307 None,
308 None, /* wait fence */
309 None,
310 0,
311 0, /* target msc */
312 0, /* divisor */
313 0, /* remainder */
314 0, NULL);
315 xcb_flush(c);
316
317 list_for_each_entry(b, &mru, link)
318 xshmfence_await(b->fence.addr);
319
320 xcb_free_pixmap(c, pixmap);
321 completed += queued;
322 } else while (queued) {
323 xcb_present_generic_event_t *ev;
324
325 ev = (xcb_present_generic_event_t *)
326 xcb_wait_for_special_event(c, Q);
327 if (ev == NULL)
328 abort();
329
330 do {
331 switch (ev->evtype) {
332 case XCB_PRESENT_COMPLETE_NOTIFY:
333 completed++;
334 queued--;
335 break;
336
337 case XCB_PRESENT_EVENT_IDLE_NOTIFY:
338 break;
339 }
340 free(ev);
341 } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, Q)));
342 }
343 clock_gettime(CLOCK_MONOTONIC, &end);
344
345 if (update)
346 xcb_xfixes_destroy_region(c, update);
347 for (n = 0; n < N_BACK; n++) {
348 if (buffer[n].fence.xid)
349 dri3_fence_free(dpy, &buffer[n].fence);
350 if (buffer[n].fd != -1)
351 close(buffer[n].fd);
352 xcb_free_pixmap(c, buffer[n].pixmap);
353 }
354
355 if (Q) {
356 xcb_discard_reply(c, xcb_present_select_input_checked(c, eid, win, 0).sequence);
357 XSync(dpy, True);
358 xcb_unregister_for_special_event(c, Q);
359 }
360
361 test_name[0] = '\0';
362 if (options) {
363 snprintf(test_name, sizeof(test_name), "(%s%s%s )",
364 options & NOCOPY ? " no-copy" : "",
365 options & DRI3 ? " dri3" : "",
366 options & ASYNC ? " async" : "");
367 }
368 printf("%s%s: Completed %d presents in %.1fs, %.3fus each (%.1f FPS)\n",
369 name, test_name,
370 completed, elapsed(&start, &end) / 1000000,
371 elapsed(&start, &end) / completed,
372 completed / (elapsed(&start, &end) / 1000000));
373 }
374
375 struct perpixel {
376 Window win;
377 struct buffer buffer[N_BACK];
378 struct list mru;
379 uint32_t eid;
380 void *Q;
381 int queued;
382 };
383
perpixel(Display * dpy,int max_width,int max_height,unsigned options)384 static void perpixel(Display *dpy,
385 int max_width, int max_height, unsigned options)
386 {
387 //const int sz = max_width * max_height;
388 const int sz = 1048;
389 struct perpixel *pp;
390 xcb_connection_t *c = XGetXCBConnection(dpy);
391 struct timespec start, end;
392 char test_name[128];
393 unsigned present_flags = 0;
394 xcb_xfixes_region_t update = 0;
395 int completed = 0;
396 int i, n;
397
398 pp = calloc(sz, sizeof(*pp));
399 if (!pp)
400 return;
401
402 for (i = 0; i < sz; i++) {
403 XSetWindowAttributes attr = { .override_redirect = 1 };
404 int depth = DefaultDepth(dpy, DefaultScreen(dpy));
405 pp[i].win = XCreateWindow(dpy, DefaultRootWindow(dpy),
406 i % max_width, i / max_width, 1, 1, 0, depth,
407 InputOutput,
408 DefaultVisual(dpy, DefaultScreen(dpy)),
409 CWOverrideRedirect, &attr);
410 XMapWindow(dpy, pp[i].win);
411 list_init(&pp[i].mru);
412 for (n = 0; n < N_BACK; n++) {
413 pp[i].buffer[n].pixmap = xcb_generate_id(c);
414 xcb_create_pixmap(c, depth, pp[i].buffer[n].pixmap,
415 pp[i].win, 1, 1);
416 pp[i].buffer[n].fence.xid = 0;
417 pp[i].buffer[n].fd = -1;
418 pp[i].buffer[n].id = n;
419 if (options & DRI3) {
420 xcb_dri3_buffer_from_pixmap_reply_t *reply;
421 int *fds;
422
423 if (dri3_create_fence(dpy, pp[i].win, &pp[i].buffer[n].fence))
424 return;
425
426 reply = xcb_dri3_buffer_from_pixmap_reply(c,
427 xcb_dri3_buffer_from_pixmap(c, pp[i].buffer[n].pixmap),
428 NULL);
429 if (reply == NULL)
430 return;
431
432 fds = xcb_dri3_buffer_from_pixmap_reply_fds(c, reply);
433 pp[i].buffer[n].fd = fds[0];
434 free(reply);
435
436 /* start idle */
437 xshmfence_trigger(pp[i].buffer[n].fence.addr);
438 }
439 pp[i].buffer[n].busy = 0;
440 list_add(&pp[i].buffer[n].link, &pp[i].mru);
441 }
442
443 if (!(options & DRI3)) {
444 pp[i].eid = xcb_generate_id(c);
445 xcb_present_select_input(c, pp[i].eid, pp[i].win,
446 (options & NOCOPY ? 0 : XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY) |
447 XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
448 pp[i].Q = xcb_register_for_special_xge(c, &xcb_present_id, pp[i].eid, &stamp);
449 }
450 pp[i].queued = 0;
451 }
452
453 XSync(dpy, True);
454 _x_error_occurred = 0;
455
456 if (options & ASYNC)
457 present_flags |= XCB_PRESENT_OPTION_ASYNC;
458 if (options & NOCOPY) {
459 update = xcb_generate_id(c);
460 xcb_xfixes_create_region(c, update, 0, NULL);
461 present_flags |= XCB_PRESENT_OPTION_COPY;
462 }
463
464 clock_gettime(CLOCK_MONOTONIC, &start);
465 do {
466 for (i = 0; i < sz; i++) {
467 struct buffer *tmp = NULL, *b = NULL;
468 retry:
469 list_for_each_entry(tmp, &pp[i].mru, link) {
470 if (tmp->fence.xid)
471 tmp->busy = !xshmfence_query(tmp->fence.addr);
472 if (!tmp->busy) {
473 b = tmp;
474 break;
475 }
476 }
477 if (options & DRI3) {
478 if (b == NULL)
479 goto retry;
480
481 xshmfence_reset(b->fence.addr);
482 pp[i].queued--;
483 completed++;
484 } else while (b == NULL) {
485 xcb_present_generic_event_t *ev;
486
487 ev = (xcb_present_generic_event_t *)
488 xcb_wait_for_special_event(c, pp[i].Q);
489 if (ev == NULL)
490 abort();
491
492 do {
493 switch (ev->evtype) {
494 case XCB_PRESENT_COMPLETE_NOTIFY:
495 completed++;
496 pp[i].queued--;
497 break;
498
499 case XCB_PRESENT_EVENT_IDLE_NOTIFY:
500 {
501 xcb_present_idle_notify_event_t *ie = (xcb_present_idle_notify_event_t *)ev;
502 assert(ie->serial < N_BACK);
503 pp[i].buffer[ie->serial].busy = 0;
504 if (b == NULL)
505 b = &pp[i].buffer[ie->serial];
506 break;
507 }
508 }
509 free(ev);
510 } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, pp[i].Q)));
511 }
512
513 b->busy = (options & NOCOPY) == 0;
514 xcb_present_pixmap(c, pp[i].win, b->pixmap, b->id,
515 0, /* valid */
516 update, /* update */
517 0, /* x_off */
518 0, /* y_off */
519 None,
520 None, /* wait fence */
521 b->fence.xid,
522 present_flags,
523 0, /* target msc */
524 0, /* divisor */
525 0, /* remainder */
526 0, NULL);
527 list_move(&b->link, &pp[i].mru);
528 pp[i].queued++;
529 }
530 xcb_flush(c);
531 clock_gettime(CLOCK_MONOTONIC, &end);
532 } while (end.tv_sec < start.tv_sec + 10);
533
534 for (i = 0; i < sz; i++) {
535 if (options & DRI3) {
536 int depth = DefaultDepth(dpy, DefaultScreen(dpy));
537 struct buffer *b = NULL;
538 XID pixmap;
539
540 pixmap = xcb_generate_id(c);
541 xcb_create_pixmap(c, depth, pixmap, pp[i].win, 1, 1);
542 xcb_present_pixmap(c, pp[i].win, pixmap, 0xdeadbeef,
543 0, /* valid */
544 None, /* update */
545 0, /* x_off */
546 0, /* y_off */
547 None,
548 None, /* wait fence */
549 None,
550 0,
551 0, /* target msc */
552 0, /* divisor */
553 0, /* remainder */
554 0, NULL);
555 xcb_flush(c);
556
557 list_for_each_entry(b, &pp[i].mru, link)
558 xshmfence_await(b->fence.addr);
559
560 xcb_free_pixmap(c, pixmap);
561 completed += pp[i].queued;
562 } else while (pp[i].queued) {
563 xcb_present_generic_event_t *ev;
564
565 ev = (xcb_present_generic_event_t *)
566 xcb_wait_for_special_event(c, pp[i].Q);
567 if (ev == NULL)
568 abort();
569
570 do {
571 switch (ev->evtype) {
572 case XCB_PRESENT_COMPLETE_NOTIFY:
573 completed++;
574 pp[i].queued--;
575 break;
576
577 case XCB_PRESENT_EVENT_IDLE_NOTIFY:
578 break;
579 }
580 free(ev);
581 } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, pp[i].Q)));
582 }
583 }
584 clock_gettime(CLOCK_MONOTONIC, &end);
585
586 if (update)
587 xcb_xfixes_destroy_region(c, update);
588
589 for (i = 0; i < sz; i++) {
590 for (n = 0; n < N_BACK; n++) {
591 if (pp[i].buffer[n].fence.xid)
592 dri3_fence_free(dpy, &pp[i].buffer[n].fence);
593 if (pp[i].buffer[n].fd != -1)
594 close(pp[i].buffer[n].fd);
595 xcb_free_pixmap(c, pp[i].buffer[n].pixmap);
596 }
597
598 if (pp[i].Q) {
599 xcb_discard_reply(c, xcb_present_select_input_checked(c, pp[i].eid, pp[i].win, 0).sequence);
600 XSync(dpy, True);
601 xcb_unregister_for_special_event(c, pp[i].Q);
602 }
603
604 XDestroyWindow(dpy, pp[i].win);
605 }
606 free(pp);
607
608 test_name[0] = '\0';
609 if (options) {
610 snprintf(test_name, sizeof(test_name), "(%s%s%s )",
611 options & NOCOPY ? " no-copy" : "",
612 options & DRI3 ? " dri3" : "",
613 options & ASYNC ? " async" : "");
614 }
615 printf("%s%s: Completed %d presents in %.1fs, %.3fus each (%.1f FPS)\n",
616 __func__, test_name,
617 completed, elapsed(&start, &end) / 1000000,
618 elapsed(&start, &end) / completed,
619 completed / (elapsed(&start, &end) / 1000000));
620 }
621
isqrt(int x)622 static int isqrt(int x)
623 {
624 int i;
625
626 for (i = 2; i*i < x; i++)
627 ;
628 return i;
629 }
630
631 struct sibling {
632 pthread_t thread;
633 Display *dpy;
634 int x, y;
635 int width, height;
636 unsigned options;
637 };
638
sibling(void * arg)639 static void *sibling(void *arg)
640 {
641 struct sibling *s = arg;
642 XSetWindowAttributes attr = { .override_redirect = 1 };
643 Window win = XCreateWindow(s->dpy, DefaultRootWindow(s->dpy),
644 s->x, s->y, s->width, s->height, 0,
645 DefaultDepth(s->dpy, DefaultScreen(s->dpy)),
646 InputOutput,
647 DefaultVisual(s->dpy, DefaultScreen(s->dpy)),
648 CWOverrideRedirect, &attr);
649 XMapWindow(s->dpy, win);
650 run(s->dpy, win, "sibling", s->options);
651 return NULL;
652 }
653
siblings(Display * dpy,int max_width,int max_height,int ncpus,unsigned options)654 static void siblings(Display *dpy,
655 int max_width, int max_height, int ncpus, unsigned options)
656 {
657 int sq_ncpus = isqrt(ncpus);
658 int width = max_width / sq_ncpus;
659 int height = max_height/ sq_ncpus;
660 struct sibling s[ncpus];
661 int child;
662
663 if (ncpus <= 1)
664 return;
665
666 for (child = 0; child < ncpus; child++) {
667 s[child].dpy = dpy;
668 s[child].x = (child % sq_ncpus) * width;
669 s[child].y = (child / sq_ncpus) * height;
670 s[child].width = width;
671 s[child].height = height;
672 s[child].options = options;
673 pthread_create(&s[child].thread, NULL, sibling, &s[child]);
674 }
675
676 for (child = 0; child < ncpus; child++)
677 pthread_join(s[child].thread, NULL);
678 }
679
cousins(int max_width,int max_height,int ncpus,unsigned options)680 static void cousins(int max_width, int max_height, int ncpus, unsigned options)
681 {
682 int sq_ncpus = isqrt(ncpus);
683 int width = max_width / sq_ncpus;
684 int height = max_height/ sq_ncpus;
685 int child;
686
687 if (ncpus <= 1)
688 return;
689
690 for (child = 0; child < ncpus; child++) {
691 for (; fork() == 0; exit(0)) {
692 int x = (child % sq_ncpus) * width;
693 int y = (child / sq_ncpus) * height;
694 XSetWindowAttributes attr = { .override_redirect = 1 };
695 Display *dpy = XOpenDisplay(NULL);
696 Window win = XCreateWindow(dpy, DefaultRootWindow(dpy),
697 x, y, width, height, 0,
698 DefaultDepth(dpy, DefaultScreen(dpy)),
699 InputOutput,
700 DefaultVisual(dpy, DefaultScreen(dpy)),
701 CWOverrideRedirect, &attr);
702 XMapWindow(dpy, win);
703 run(dpy, win, "cousin", options);
704 }
705 }
706
707 while (child) {
708 int status = -1;
709 pid_t pid = wait(&status);
710 if (pid == -1)
711 continue;
712 child--;
713 }
714 }
715
has_present(Display * dpy)716 static int has_present(Display *dpy)
717 {
718 xcb_connection_t *c = XGetXCBConnection(dpy);
719 xcb_generic_error_t *error = NULL;
720 void *reply;
721
722 reply = xcb_present_query_version_reply(c,
723 xcb_present_query_version(c,
724 XCB_PRESENT_MAJOR_VERSION,
725 XCB_PRESENT_MINOR_VERSION),
726 &error);
727
728 free(reply);
729 free(error);
730 if (reply == NULL) {
731 fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy));
732 return 0;
733 }
734
735 return 1;
736 }
737
has_composite(Display * dpy)738 static int has_composite(Display *dpy)
739 {
740 int event, error;
741 int major, minor;
742
743 if (!XDamageQueryExtension (dpy, &event, &error))
744 return 0;
745
746 if (!XCompositeQueryExtension(dpy, &event, &error))
747 return 0;
748
749 XCompositeQueryVersion(dpy, &major, &minor);
750
751 return major > 0 || minor >= 4;
752 }
753
dri3_query_version(Display * dpy,int * major,int * minor)754 static int dri3_query_version(Display *dpy, int *major, int *minor)
755 {
756 xcb_connection_t *c = XGetXCBConnection(dpy);
757 xcb_dri3_query_version_reply_t *reply;
758 xcb_generic_error_t *error;
759
760 *major = *minor = -1;
761
762 reply = xcb_dri3_query_version_reply(c,
763 xcb_dri3_query_version(c,
764 XCB_DRI3_MAJOR_VERSION,
765 XCB_DRI3_MINOR_VERSION),
766 &error);
767 free(error);
768 if (reply == NULL)
769 return -1;
770
771 *major = reply->major_version;
772 *minor = reply->minor_version;
773 free(reply);
774
775 return 0;
776 }
777
has_dri3(Display * dpy)778 static int has_dri3(Display *dpy)
779 {
780 const xcb_query_extension_reply_t *ext;
781 int major, minor;
782
783 ext = xcb_get_extension_data(XGetXCBConnection(dpy), &xcb_dri3_id);
784 if (ext == NULL || !ext->present)
785 return 0;
786
787 if (dri3_query_version(dpy, &major, &minor) < 0)
788 return 0;
789
790 return major >= 0;
791 }
792
has_xfixes(Display * dpy)793 static int has_xfixes(Display *dpy)
794 {
795 xcb_connection_t *c = XGetXCBConnection(dpy);
796 const xcb_query_extension_reply_t *ext;
797 void *reply;
798
799 ext = xcb_get_extension_data(c, &xcb_xfixes_id);
800 if (ext == NULL || !ext->present)
801 return 0;
802
803 reply = xcb_xfixes_query_version_reply(c,
804 xcb_xfixes_query_version(c,
805 XCB_XFIXES_MAJOR_VERSION,
806 XCB_XFIXES_MINOR_VERSION),
807 NULL);
808 free(reply);
809
810 return reply != NULL;
811 }
812
_XRRGetScreenResourcesCurrent(Display * dpy,Window window)813 static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
814 {
815 XRRScreenResources *res;
816
817 res = XRRGetScreenResourcesCurrent(dpy, window);
818 if (res == NULL)
819 res = XRRGetScreenResources(dpy, window);
820
821 return res;
822 }
823
lookup_mode(XRRScreenResources * res,int id)824 static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
825 {
826 int i;
827
828 for (i = 0; i < res->nmode; i++) {
829 if (res->modes[i].id == id)
830 return &res->modes[i];
831 }
832
833 return NULL;
834 }
835
fullscreen(Display * dpy,Window win)836 static void fullscreen(Display *dpy, Window win)
837 {
838 Atom atom = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
839 XChangeProperty(dpy, win,
840 XInternAtom(dpy, "_NET_WM_STATE", False),
841 XA_ATOM, 32, PropModeReplace,
842 (unsigned char *)&atom, 1);
843 }
844
loop(Display * dpy,XRRScreenResources * res,unsigned options)845 static void loop(Display *dpy, XRRScreenResources *res, unsigned options)
846 {
847 Window root = DefaultRootWindow(dpy);
848 Window win;
849 XSetWindowAttributes attr;
850 int i, j;
851
852 attr.override_redirect = 1;
853
854 run(dpy, root, "off", options);
855 XSync(dpy, True);
856
857 for (i = 0; i < res->noutput; i++) {
858 XRROutputInfo *output;
859 XRRModeInfo *mode;
860
861 output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
862 if (output == NULL)
863 continue;
864
865 mode = NULL;
866 if (res->nmode)
867 mode = lookup_mode(res, output->modes[0]);
868
869 for (j = 0; mode && j < 2*output->ncrtc; j++) {
870 int c = j;
871 if (c >= output->ncrtc)
872 c = 2*output->ncrtc - j - 1;
873
874 printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld: %dx%d\n",
875 i, c, (long)res->outputs[i], (long)output->crtcs[c],
876 mode->width, mode->height);
877 XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime,
878 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
879
880 run(dpy, root, "root", options);
881 XSync(dpy, True);
882
883 win = XCreateWindow(dpy, root,
884 0, 0, mode->width, mode->height, 0,
885 DefaultDepth(dpy, DefaultScreen(dpy)),
886 InputOutput,
887 DefaultVisual(dpy, DefaultScreen(dpy)),
888 CWOverrideRedirect, &attr);
889 fullscreen(dpy, win);
890 XMapWindow(dpy, win);
891 run(dpy, win, "fullscreen", options);
892 XDestroyWindow(dpy, win);
893 XSync(dpy, True);
894
895 win = XCreateWindow(dpy, root,
896 0, 0, mode->width, mode->height, 0,
897 DefaultDepth(dpy, DefaultScreen(dpy)),
898 InputOutput,
899 DefaultVisual(dpy, DefaultScreen(dpy)),
900 CWOverrideRedirect, &attr);
901 XMapWindow(dpy, win);
902 run(dpy, win, "windowed", options);
903 XDestroyWindow(dpy, win);
904 XSync(dpy, True);
905
906 if (has_composite(dpy)) {
907 Damage damage;
908
909 _x_error_occurred = 0;
910 win = XCreateWindow(dpy, root,
911 0, 0, mode->width, mode->height, 0,
912 DefaultDepth(dpy, DefaultScreen(dpy)),
913 InputOutput,
914 DefaultVisual(dpy, DefaultScreen(dpy)),
915 CWOverrideRedirect, &attr);
916 XCompositeRedirectWindow(dpy, win, CompositeRedirectManual);
917 damage = XDamageCreate(dpy, win, XDamageReportNonEmpty);
918 XMapWindow(dpy, win);
919 XSync(dpy, True);
920 if (!_x_error_occurred)
921 run(dpy, win, "composited", options);
922 XDamageDestroy(dpy, damage);
923 XDestroyWindow(dpy, win);
924 XSync(dpy, True);
925 }
926
927 win = XCreateWindow(dpy, root,
928 0, 0, mode->width/2, mode->height/2, 0,
929 DefaultDepth(dpy, DefaultScreen(dpy)),
930 InputOutput,
931 DefaultVisual(dpy, DefaultScreen(dpy)),
932 CWOverrideRedirect, &attr);
933 XMapWindow(dpy, win);
934 run(dpy, win, "half", options);
935 XDestroyWindow(dpy, win);
936 XSync(dpy, True);
937
938 perpixel(dpy, mode->width, mode->height, options);
939
940 siblings(dpy, mode->width, mode->height,
941 sysconf(_SC_NPROCESSORS_ONLN),
942 options);
943
944 cousins(mode->width, mode->height,
945 sysconf(_SC_NPROCESSORS_ONLN),
946 options);
947
948 XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime,
949 0, 0, None, RR_Rotate_0, NULL, 0);
950 }
951
952 XRRFreeOutputInfo(output);
953 }
954
955 }
956
main(void)957 int main(void)
958 {
959 Display *dpy;
960 XRRScreenResources *res;
961 XRRCrtcInfo **original_crtc;
962 int i;
963
964 XInitThreads();
965
966 dpy = XOpenDisplay(NULL);
967 if (dpy == NULL)
968 return 77;
969
970 if (!has_present(dpy))
971 return 77;
972
973 if (DPMSQueryExtension(dpy, &i, &i))
974 DPMSDisable(dpy);
975
976 signal(SIGALRM, SIG_IGN);
977 XSetErrorHandler(_check_error_handler);
978
979 res = NULL;
980 if (XRRQueryVersion(dpy, &i, &i))
981 res = _XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy));
982 if (res == NULL)
983 return 77;
984
985 original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
986 for (i = 0; i < res->ncrtc; i++)
987 original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
988
989 printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc);
990 for (i = 0; i < res->ncrtc; i++)
991 XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
992 0, 0, None, RR_Rotate_0, NULL, 0);
993
994 loop(dpy, res, 0);
995 loop(dpy, res, ASYNC);
996 if (has_xfixes(dpy))
997 loop(dpy, res, NOCOPY);
998 if (has_dri3(dpy)) {
999 loop(dpy, res, DRI3);
1000 loop(dpy, res, DRI3 | ASYNC);
1001 }
1002
1003 for (i = 0; i < res->ncrtc; i++)
1004 XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
1005 original_crtc[i]->x,
1006 original_crtc[i]->y,
1007 original_crtc[i]->mode,
1008 original_crtc[i]->rotation,
1009 original_crtc[i]->outputs,
1010 original_crtc[i]->noutput);
1011
1012 if (DPMSQueryExtension(dpy, &i, &i))
1013 DPMSEnable(dpy);
1014 return 0;
1015 }
1016