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