1 /*
2  * Cogl
3  *
4  * A Low Level GPU Graphics and Utilities API
5  *
6  * Copyright (C) 2008,2009,2010 Intel Corporation.
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use, copy,
12  * modify, merge, publish, distribute, sublicense, and/or sell copies
13  * of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  *
28  * Authors:
29  *   Robert Bragg <robert@linux.intel.com>
30  */
31 
32 #ifdef HAVE_CONFIG_H
33 #include "cogl-config.h"
34 #endif
35 
36 #include "cogl-xlib-renderer.h"
37 #include "cogl-util.h"
38 #include "cogl-object.h"
39 
40 #include "cogl-output-private.h"
41 #include "cogl-renderer-private.h"
42 #include "cogl-xlib-renderer-private.h"
43 #include "cogl-x11-renderer-private.h"
44 #include "cogl-winsys-private.h"
45 #include "cogl-error-private.h"
46 #include "cogl-poll-private.h"
47 
48 #include <X11/Xlib.h>
49 #include <X11/extensions/Xdamage.h>
50 #include <X11/extensions/Xrandr.h>
51 
52 #include <stdlib.h>
53 #include <string.h>
54 
55 static char *_cogl_x11_display_name = NULL;
56 static GList *_cogl_xlib_renderers = NULL;
57 
58 static void
destroy_xlib_renderer_data(void * user_data)59 destroy_xlib_renderer_data (void *user_data)
60 {
61   CoglXlibRenderer *data = user_data;
62 
63   if (data->xvisinfo)
64     XFree (data->xvisinfo);
65 
66   g_slice_free (CoglXlibRenderer, user_data);
67 }
68 
69 CoglXlibRenderer *
_cogl_xlib_renderer_get_data(CoglRenderer * renderer)70 _cogl_xlib_renderer_get_data (CoglRenderer *renderer)
71 {
72   static CoglUserDataKey key;
73   CoglXlibRenderer *data;
74 
75   /* Constructs a CoglXlibRenderer struct on demand and attaches it to
76      the object using user data. It's done this way instead of using a
77      subclassing hierarchy in the winsys data because all EGL winsys's
78      need the EGL winsys data but only one of them wants the Xlib
79      data. */
80 
81   data = cogl_object_get_user_data (COGL_OBJECT (renderer), &key);
82 
83   if (data == NULL)
84     {
85       data = g_slice_new0 (CoglXlibRenderer);
86 
87       cogl_object_set_user_data (COGL_OBJECT (renderer),
88                                  &key,
89                                  data,
90                                  destroy_xlib_renderer_data);
91     }
92 
93   return data;
94 }
95 
96 static void
register_xlib_renderer(CoglRenderer * renderer)97 register_xlib_renderer (CoglRenderer *renderer)
98 {
99   GList *l;
100 
101   for (l = _cogl_xlib_renderers; l; l = l->next)
102     if (l->data == renderer)
103       return;
104 
105   _cogl_xlib_renderers = g_list_prepend (_cogl_xlib_renderers, renderer);
106 }
107 
108 static void
unregister_xlib_renderer(CoglRenderer * renderer)109 unregister_xlib_renderer (CoglRenderer *renderer)
110 {
111   _cogl_xlib_renderers = g_list_remove (_cogl_xlib_renderers, renderer);
112 }
113 
114 static CoglRenderer *
get_renderer_for_xdisplay(Display * xdpy)115 get_renderer_for_xdisplay (Display *xdpy)
116 {
117   GList *l;
118 
119   for (l = _cogl_xlib_renderers; l; l = l->next)
120     {
121       CoglRenderer *renderer = l->data;
122       CoglXlibRenderer *xlib_renderer =
123         _cogl_xlib_renderer_get_data (renderer);
124 
125       if (xlib_renderer->xdpy == xdpy)
126         return renderer;
127     }
128 
129   return NULL;
130 }
131 
132 static int
error_handler(Display * xdpy,XErrorEvent * error)133 error_handler (Display *xdpy,
134                XErrorEvent *error)
135 {
136   CoglRenderer *renderer;
137   CoglXlibRenderer *xlib_renderer;
138 
139   renderer = get_renderer_for_xdisplay (xdpy);
140 
141   xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
142   g_assert (xlib_renderer->trap_state);
143 
144   xlib_renderer->trap_state->trapped_error_code = error->error_code;
145 
146   return 0;
147 }
148 
149 void
_cogl_xlib_renderer_trap_errors(CoglRenderer * renderer,CoglXlibTrapState * state)150 _cogl_xlib_renderer_trap_errors (CoglRenderer *renderer,
151                                  CoglXlibTrapState *state)
152 {
153   CoglXlibRenderer *xlib_renderer;
154 
155   xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
156 
157   state->trapped_error_code = 0;
158   state->old_error_handler = XSetErrorHandler (error_handler);
159 
160   state->old_state = xlib_renderer->trap_state;
161   xlib_renderer->trap_state = state;
162 }
163 
164 int
_cogl_xlib_renderer_untrap_errors(CoglRenderer * renderer,CoglXlibTrapState * state)165 _cogl_xlib_renderer_untrap_errors (CoglRenderer *renderer,
166                                    CoglXlibTrapState *state)
167 {
168   CoglXlibRenderer *xlib_renderer;
169 
170   xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
171   g_assert (state == xlib_renderer->trap_state);
172 
173   XSetErrorHandler (state->old_error_handler);
174 
175   xlib_renderer->trap_state = state->old_state;
176 
177   return state->trapped_error_code;
178 }
179 
180 static Display *
assert_xlib_display(CoglRenderer * renderer,CoglError ** error)181 assert_xlib_display (CoglRenderer *renderer, CoglError **error)
182 {
183   Display *xdpy = cogl_xlib_renderer_get_foreign_display (renderer);
184   CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
185 
186   /* A foreign display may have already been set... */
187   if (xdpy)
188     {
189       xlib_renderer->xdpy = xdpy;
190       return xdpy;
191     }
192 
193   xdpy = XOpenDisplay (_cogl_x11_display_name);
194   if (xdpy == NULL)
195     {
196       _cogl_set_error (error,
197                    COGL_RENDERER_ERROR,
198                    COGL_RENDERER_ERROR_XLIB_DISPLAY_OPEN,
199                    "Failed to open X Display %s", _cogl_x11_display_name);
200       return NULL;
201     }
202 
203   xlib_renderer->xdpy = xdpy;
204   return xdpy;
205 }
206 
207 static int
compare_outputs(CoglOutput * a,CoglOutput * b)208 compare_outputs (CoglOutput *a,
209                  CoglOutput *b)
210 {
211   return strcmp (a->name, b->name);
212 }
213 
214 #define CSO(X) COGL_SUBPIXEL_ORDER_ ## X
215 static CoglSubpixelOrder subpixel_map[6][6] = {
216   { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR),
217     CSO(VERTICAL_RGB),   CSO(VERTICAL_BGR) },   /* 0 */
218   { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_RGB),   CSO(VERTICAL_BGR),
219     CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB) }, /* 90 */
220   { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB),
221     CSO(VERTICAL_BGR),   CSO(VERTICAL_RGB) },   /* 180 */
222   { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_BGR),   CSO(VERTICAL_RGB),
223     CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR) }, /* 270 */
224   { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB),
225     CSO(VERTICAL_RGB),   CSO(VERTICAL_BGR) },   /* Reflect_X */
226   { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR),
227     CSO(VERTICAL_BGR),   CSO(VERTICAL_RGB) },   /* Reflect_Y */
228 };
229 #undef CSO
230 
231 static void
update_outputs(CoglRenderer * renderer,CoglBool notify)232 update_outputs (CoglRenderer *renderer,
233                 CoglBool notify)
234 {
235   CoglXlibRenderer *xlib_renderer =
236     _cogl_xlib_renderer_get_data (renderer);
237   XRRScreenResources *resources;
238   CoglXlibTrapState state;
239   CoglBool error = FALSE;
240   GList *new_outputs = NULL;
241   GList *l, *m;
242   CoglBool changed = FALSE;
243   int i;
244 
245   xlib_renderer->outputs_update_serial = XNextRequest (xlib_renderer->xdpy);
246 
247   resources = XRRGetScreenResources (xlib_renderer->xdpy,
248                                      DefaultRootWindow (xlib_renderer->xdpy));
249 
250   _cogl_xlib_renderer_trap_errors (renderer, &state);
251 
252   for (i = 0; resources && i < resources->ncrtc && !error; i++)
253     {
254       XRRCrtcInfo *crtc_info = NULL;
255       XRROutputInfo *output_info = NULL;
256       CoglOutput *output;
257       float refresh_rate = 0;
258       int j;
259 
260       crtc_info = XRRGetCrtcInfo (xlib_renderer->xdpy,
261                                   resources, resources->crtcs[i]);
262       if (crtc_info == NULL)
263         {
264           error = TRUE;
265           goto next;
266         }
267 
268       if (crtc_info->mode == None)
269         goto next;
270 
271       for (j = 0; j < resources->nmode; j++)
272         {
273           if (resources->modes[j].id == crtc_info->mode)
274             refresh_rate = (resources->modes[j].dotClock /
275                             ((float)resources->modes[j].hTotal *
276                              resources->modes[j].vTotal));
277         }
278 
279       output_info = XRRGetOutputInfo (xlib_renderer->xdpy,
280                                       resources,
281                                       crtc_info->outputs[0]);
282       if (output_info == NULL)
283         {
284           error = TRUE;
285           goto next;
286         }
287 
288       output = _cogl_output_new (output_info->name);
289       output->x = crtc_info->x;
290       output->y = crtc_info->y;
291       output->width = crtc_info->width;
292       output->height = crtc_info->height;
293       if ((crtc_info->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0)
294         {
295           output->mm_width = output_info->mm_height;
296           output->mm_height = output_info->mm_width;
297         }
298       else
299         {
300           output->mm_width = output_info->mm_width;
301           output->mm_height = output_info->mm_height;
302         }
303 
304       output->refresh_rate = refresh_rate;
305 
306       switch (output_info->subpixel_order)
307         {
308         case SubPixelUnknown:
309         default:
310           output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
311           break;
312         case SubPixelNone:
313           output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
314           break;
315         case SubPixelHorizontalRGB:
316           output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
317           break;
318         case SubPixelHorizontalBGR:
319           output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
320           break;
321         case SubPixelVerticalRGB:
322           output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
323           break;
324         case SubPixelVerticalBGR:
325           output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
326           break;
327         }
328 
329       output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
330 
331       /* Handle the effect of rotation and reflection on subpixel order (ugh) */
332       for (j = 0; j < 6; j++)
333         {
334           if ((crtc_info->rotation & (1 << j)) != 0)
335             output->subpixel_order = subpixel_map[j][output->subpixel_order];
336         }
337 
338       new_outputs = g_list_prepend (new_outputs, output);
339 
340     next:
341       if (crtc_info != NULL)
342         XFree (crtc_info);
343 
344       if (output_info != NULL)
345         XFree (output_info);
346     }
347 
348   XFree (resources);
349 
350   if (!error)
351     {
352       new_outputs = g_list_sort (new_outputs, (GCompareFunc)compare_outputs);
353 
354       l = new_outputs;
355       m = renderer->outputs;
356 
357       while (l || m)
358         {
359           int cmp;
360           CoglOutput *output_l = l ? (CoglOutput *)l->data : NULL;
361           CoglOutput *output_m = m ? (CoglOutput *)m->data : NULL;
362 
363           if (l && m)
364             cmp = compare_outputs (output_l, output_m);
365           else if (l)
366             cmp = -1;
367           else
368             cmp = 1;
369 
370           if (cmp == 0)
371             {
372               GList *m_next = m->next;
373 
374               if (!_cogl_output_values_equal (output_l, output_m))
375                 {
376                   renderer->outputs = g_list_remove_link (renderer->outputs, m);
377                   renderer->outputs = g_list_insert_before (renderer->outputs,
378                                                             m_next, output_l);
379                   cogl_object_ref (output_l);
380 
381                   changed = TRUE;
382                 }
383 
384               l = l->next;
385               m = m_next;
386             }
387           else if (cmp < 0)
388             {
389               renderer->outputs =
390                 g_list_insert_before (renderer->outputs, m, output_l);
391               cogl_object_ref (output_l);
392               changed = TRUE;
393               l = l->next;
394             }
395           else
396             {
397               GList *m_next = m->next;
398               renderer->outputs = g_list_remove_link (renderer->outputs, m);
399               changed = TRUE;
400               m = m_next;
401             }
402         }
403     }
404 
405   g_list_free_full (new_outputs, (GDestroyNotify)cogl_object_unref);
406   _cogl_xlib_renderer_untrap_errors (renderer, &state);
407 
408   if (changed)
409     {
410       const CoglWinsysVtable *winsys = renderer->winsys_vtable;
411 
412       if (notify)
413         COGL_NOTE (WINSYS, "Outputs changed:");
414       else
415         COGL_NOTE (WINSYS, "Outputs:");
416 
417       for (l = renderer->outputs; l; l = l->next)
418         {
419           CoglOutput *output = l->data;
420           const char *subpixel_string;
421 
422           switch (output->subpixel_order)
423             {
424             case COGL_SUBPIXEL_ORDER_UNKNOWN:
425             default:
426               subpixel_string = "unknown";
427               break;
428             case COGL_SUBPIXEL_ORDER_NONE:
429               subpixel_string = "none";
430               break;
431             case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB:
432               subpixel_string = "horizontal_rgb";
433               break;
434             case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR:
435               subpixel_string = "horizontal_bgr";
436               break;
437             case COGL_SUBPIXEL_ORDER_VERTICAL_RGB:
438               subpixel_string = "vertical_rgb";
439               break;
440             case COGL_SUBPIXEL_ORDER_VERTICAL_BGR:
441               subpixel_string = "vertical_bgr";
442               break;
443             }
444 
445           COGL_NOTE (WINSYS,
446                      " %10s: +%d+%dx%dx%d mm=%dx%d dpi=%.1fx%.1f "
447                      "subpixel_order=%s refresh_rate=%.3f",
448                      output->name,
449                      output->x, output->y, output->width, output->height,
450                      output->mm_width, output->mm_height,
451                      output->width / (output->mm_width / 25.4),
452                      output->height / (output->mm_height / 25.4),
453                      subpixel_string,
454                      output->refresh_rate);
455         }
456 
457       if (notify && winsys->renderer_outputs_changed != NULL)
458         winsys->renderer_outputs_changed (renderer);
459     }
460 }
461 
462 static CoglFilterReturn
randr_filter(XEvent * event,void * data)463 randr_filter (XEvent *event,
464               void   *data)
465 {
466   CoglRenderer *renderer = data;
467   CoglXlibRenderer *xlib_renderer =
468     _cogl_xlib_renderer_get_data (renderer);
469   CoglX11Renderer *x11_renderer =
470     (CoglX11Renderer *) xlib_renderer;
471 
472   if (x11_renderer->randr_base != -1 &&
473       (event->xany.type == x11_renderer->randr_base + RRScreenChangeNotify ||
474        event->xany.type == x11_renderer->randr_base + RRNotify) &&
475       event->xany.serial >= xlib_renderer->outputs_update_serial)
476     update_outputs (renderer, TRUE);
477 
478   return COGL_FILTER_CONTINUE;
479 }
480 
481 static int64_t
prepare_xlib_events_timeout(void * user_data)482 prepare_xlib_events_timeout (void *user_data)
483 {
484   CoglRenderer *renderer = user_data;
485   CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
486 
487   return XPending (xlib_renderer->xdpy) ? 0 : -1;
488 }
489 
490 static void
dispatch_xlib_events(void * user_data,int revents)491 dispatch_xlib_events (void *user_data, int revents)
492 {
493   CoglRenderer *renderer = user_data;
494   CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
495 
496   if (renderer->xlib_enable_event_retrieval)
497     while (XPending (xlib_renderer->xdpy))
498       {
499         XEvent xevent;
500 
501         XNextEvent (xlib_renderer->xdpy, &xevent);
502 
503         cogl_xlib_renderer_handle_event (renderer, &xevent);
504       }
505 }
506 
507 CoglBool
_cogl_xlib_renderer_connect(CoglRenderer * renderer,CoglError ** error)508 _cogl_xlib_renderer_connect (CoglRenderer *renderer, CoglError **error)
509 {
510   CoglXlibRenderer *xlib_renderer =
511     _cogl_xlib_renderer_get_data (renderer);
512   CoglX11Renderer *x11_renderer =
513     (CoglX11Renderer *) xlib_renderer;
514   int damage_error;
515   int randr_error;
516 
517   if (!assert_xlib_display (renderer, error))
518     return FALSE;
519 
520   if (getenv ("COGL_X11_SYNC"))
521     XSynchronize (xlib_renderer->xdpy, TRUE);
522 
523   /* Check whether damage events are supported on this display */
524   if (!XDamageQueryExtension (xlib_renderer->xdpy,
525                               &x11_renderer->damage_base,
526                               &damage_error))
527     x11_renderer->damage_base = -1;
528 
529   /* Check whether randr is supported on this display */
530   if (!XRRQueryExtension (xlib_renderer->xdpy,
531                           &x11_renderer->randr_base,
532                           &randr_error))
533     x11_renderer->randr_base = -1;
534 
535   xlib_renderer->trap_state = NULL;
536 
537   if (renderer->xlib_enable_event_retrieval)
538     {
539       _cogl_poll_renderer_add_fd (renderer,
540                                   ConnectionNumber (xlib_renderer->xdpy),
541                                   COGL_POLL_FD_EVENT_IN,
542                                   prepare_xlib_events_timeout,
543                                   dispatch_xlib_events,
544                                   renderer);
545     }
546 
547   XRRSelectInput(xlib_renderer->xdpy,
548                  DefaultRootWindow (xlib_renderer->xdpy),
549                  RRScreenChangeNotifyMask
550                  | RRCrtcChangeNotifyMask
551                  | RROutputPropertyNotifyMask);
552   update_outputs (renderer, FALSE);
553 
554   register_xlib_renderer (renderer);
555 
556   cogl_xlib_renderer_add_filter (renderer,
557                                  randr_filter,
558                                  renderer);
559 
560   return TRUE;
561 }
562 
563 void
_cogl_xlib_renderer_disconnect(CoglRenderer * renderer)564 _cogl_xlib_renderer_disconnect (CoglRenderer *renderer)
565 {
566   CoglXlibRenderer *xlib_renderer =
567     _cogl_xlib_renderer_get_data (renderer);
568 
569   g_list_free_full (renderer->outputs, (GDestroyNotify)cogl_object_unref);
570   renderer->outputs = NULL;
571 
572   if (!renderer->foreign_xdpy && xlib_renderer->xdpy)
573     XCloseDisplay (xlib_renderer->xdpy);
574 
575   unregister_xlib_renderer (renderer);
576 }
577 
578 Display *
cogl_xlib_renderer_get_display(CoglRenderer * renderer)579 cogl_xlib_renderer_get_display (CoglRenderer *renderer)
580 {
581   CoglXlibRenderer *xlib_renderer;
582 
583   _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL);
584 
585   xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
586 
587   return xlib_renderer->xdpy;
588 }
589 
590 CoglFilterReturn
cogl_xlib_renderer_handle_event(CoglRenderer * renderer,XEvent * event)591 cogl_xlib_renderer_handle_event (CoglRenderer *renderer,
592                                  XEvent *event)
593 {
594   return _cogl_renderer_handle_native_event (renderer, event);
595 }
596 
597 void
cogl_xlib_renderer_add_filter(CoglRenderer * renderer,CoglXlibFilterFunc func,void * data)598 cogl_xlib_renderer_add_filter (CoglRenderer *renderer,
599                                CoglXlibFilterFunc func,
600                                void *data)
601 {
602   _cogl_renderer_add_native_filter (renderer,
603                                     (CoglNativeFilterFunc)func, data);
604 }
605 
606 void
cogl_xlib_renderer_remove_filter(CoglRenderer * renderer,CoglXlibFilterFunc func,void * data)607 cogl_xlib_renderer_remove_filter (CoglRenderer *renderer,
608                                   CoglXlibFilterFunc func,
609                                   void *data)
610 {
611   _cogl_renderer_remove_native_filter (renderer,
612                                        (CoglNativeFilterFunc)func, data);
613 }
614 
615 int64_t
_cogl_xlib_renderer_get_dispatch_timeout(CoglRenderer * renderer)616 _cogl_xlib_renderer_get_dispatch_timeout (CoglRenderer *renderer)
617 {
618   CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
619 
620   if (renderer->xlib_enable_event_retrieval)
621     {
622       if (XPending (xlib_renderer->xdpy))
623         return 0;
624       else
625         return -1;
626     }
627   else
628     return -1;
629 }
630 
631 CoglOutput *
_cogl_xlib_renderer_output_for_rectangle(CoglRenderer * renderer,int x,int y,int width,int height)632 _cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer,
633                                           int x,
634                                           int y,
635                                           int width,
636                                           int height)
637 {
638   int max_overlap = 0;
639   CoglOutput *max_overlapped = NULL;
640   GList *l;
641   int xa1 = x, xa2 = x + width;
642   int ya1 = y, ya2 = y + height;
643 
644   for (l = renderer->outputs; l; l = l->next)
645     {
646       CoglOutput *output = l->data;
647       int xb1 = output->x, xb2 = output->x + output->width;
648       int yb1 = output->y, yb2 = output->y + output->height;
649 
650       int overlap_x = MIN(xa2, xb2) - MAX(xa1, xb1);
651       int overlap_y = MIN(ya2, yb2) - MAX(ya1, yb1);
652 
653       if (overlap_x > 0 && overlap_y > 0)
654         {
655           int overlap = overlap_x * overlap_y;
656           if (overlap > max_overlap)
657             {
658               max_overlap = overlap;
659               max_overlapped = output;
660             }
661         }
662     }
663 
664   return max_overlapped;
665 }
666 
667 XVisualInfo *
cogl_xlib_renderer_get_visual_info(CoglRenderer * renderer)668 cogl_xlib_renderer_get_visual_info (CoglRenderer *renderer)
669 {
670   CoglXlibRenderer *xlib_renderer;
671 
672   _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL);
673 
674   xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
675 
676   return xlib_renderer->xvisinfo;
677 }
678