1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2009 Eric Anholt
4 * Copyright © 2009 Chris Wilson
5 * Copyright © 2005 Red Hat, Inc
7 * This library is free software; you can redistribute it and/or
8 * modify it either under the terms of the GNU Lesser General Public
9 * License version 2.1 as published by the Free Software Foundation
10 * (the "LGPL") or, at your option, under the terms of the Mozilla
11 * Public License Version 1.1 (the "MPL"). If you do not alter this
12 * notice, a recipient may use your version of this file under either
13 * the MPL or the LGPL.
15 * You should have received a copy of the LGPL along with this library
16 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
18 * You should have received a copy of the MPL along with this library
19 * in the file COPYING-MPL-1.1
21 * The contents of this file are subject to the Mozilla Public License
22 * Version 1.1 (the "License"); you may not use this file except in
23 * compliance with the License. You may obtain a copy of the License at
24 * http://www.mozilla.org/MPL/
26 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28 * the specific language governing rights and limitations.
30 * The Original Code is the cairo graphics library.
32 * The Initial Developer of the Original Code is Red Hat, Inc.
35 * Carl Worth <cworth@cworth.org>
36 * Chris Wilson <chris@chris-wilson.co.uk>
41 #include "cairo-gl-private.h"
43 #include "cairo-error-private.h"
45 #include <X11/Xutil.h>
47 /* XXX needs hooking into XCloseDisplay() */
49 typedef struct _cairo_glx_context
{
50 cairo_gl_context_t base
;
56 GLXDrawable previous_drawable
;
57 GLXContext previous_context
;
59 cairo_bool_t has_multithread_makecurrent
;
60 } cairo_glx_context_t
;
62 typedef struct _cairo_glx_surface
{
63 cairo_gl_surface_t base
;
66 } cairo_glx_surface_t
;
69 _context_acquisition_changed_glx_state (cairo_glx_context_t
*ctx
,
70 GLXDrawable current_drawable
)
72 return ctx
->previous_drawable
!= current_drawable
||
73 ctx
->previous_context
!= ctx
->context
;
77 _glx_get_current_drawable (cairo_glx_context_t
*ctx
)
79 if (ctx
->base
.current_target
== NULL
||
80 _cairo_gl_surface_is_texture (ctx
->base
.current_target
)) {
81 return ctx
->dummy_window
;
84 return ((cairo_glx_surface_t
*) ctx
->base
.current_target
)->win
;
88 _glx_query_current_state (cairo_glx_context_t
* ctx
)
90 ctx
->previous_drawable
= glXGetCurrentDrawable ();
91 ctx
->previous_context
= glXGetCurrentContext ();
93 /* If any of the values were none, assume they are all none. Not all
94 drivers seem well behaved when it comes to using these values across
96 if (ctx
->previous_drawable
== None
||
97 ctx
->previous_context
== None
) {
98 ctx
->previous_drawable
= None
;
99 ctx
->previous_context
= None
;
104 _glx_acquire (void *abstract_ctx
)
106 cairo_glx_context_t
*ctx
= abstract_ctx
;
107 GLXDrawable current_drawable
= _glx_get_current_drawable (ctx
);
109 _glx_query_current_state (ctx
);
110 if (!_context_acquisition_changed_glx_state (ctx
, current_drawable
))
113 glXMakeCurrent (ctx
->display
, current_drawable
, ctx
->context
);
117 _glx_make_current (void *abstract_ctx
, cairo_gl_surface_t
*abstract_surface
)
119 cairo_glx_context_t
*ctx
= abstract_ctx
;
120 cairo_glx_surface_t
*surface
= (cairo_glx_surface_t
*) abstract_surface
;
122 /* Set the window as the target of our context. */
123 glXMakeCurrent (ctx
->display
, surface
->win
, ctx
->context
);
127 _glx_release (void *abstract_ctx
)
129 cairo_glx_context_t
*ctx
= abstract_ctx
;
131 if (ctx
->has_multithread_makecurrent
|| !ctx
->base
.thread_aware
||
132 !_context_acquisition_changed_glx_state (ctx
,
133 _glx_get_current_drawable (ctx
))) {
137 glXMakeCurrent (ctx
->display
, None
, None
);
141 _glx_swap_buffers (void *abstract_ctx
,
142 cairo_gl_surface_t
*abstract_surface
)
144 cairo_glx_context_t
*ctx
= abstract_ctx
;
145 cairo_glx_surface_t
*surface
= (cairo_glx_surface_t
*) abstract_surface
;
147 glXSwapBuffers (ctx
->display
, surface
->win
);
151 _glx_destroy (void *abstract_ctx
)
153 cairo_glx_context_t
*ctx
= abstract_ctx
;
155 if (ctx
->dummy_window
!= None
)
156 XDestroyWindow (ctx
->display
, ctx
->dummy_window
);
158 glXMakeCurrent (ctx
->display
, None
, None
);
161 static cairo_status_t
162 _glx_dummy_window (Display
*dpy
, GLXContext gl_ctx
, Window
*dummy
)
164 int attr
[3] = { GLX_FBCONFIG_ID
, 0, None
};
168 XSetWindowAttributes swa
;
172 /* Create a dummy window created for the target GLX context that we can
173 * use to query the available GL/GLX extensions.
175 glXQueryContext (dpy
, gl_ctx
, GLX_FBCONFIG_ID
, &attr
[1]);
178 config
= glXChooseFBConfig (dpy
, DefaultScreen (dpy
), attr
, &cnt
);
179 if (unlikely (cnt
== 0))
180 return _cairo_error (CAIRO_STATUS_INVALID_FORMAT
);
182 vi
= glXGetVisualFromFBConfig (dpy
, config
[0]);
185 if (unlikely (vi
== NULL
))
186 return _cairo_error (CAIRO_STATUS_INVALID_FORMAT
);
188 cmap
= XCreateColormap (dpy
,
189 RootWindow (dpy
, vi
->screen
),
193 swa
.border_pixel
= 0;
194 win
= XCreateWindow (dpy
, RootWindow (dpy
, vi
->screen
),
199 CWBorderPixel
| CWColormap
, &swa
);
200 XFreeColormap (dpy
, cmap
);
204 if (unlikely (! glXMakeCurrent (dpy
, win
, gl_ctx
))) {
205 XDestroyWindow (dpy
, win
);
206 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
210 return CAIRO_STATUS_SUCCESS
;
214 cairo_glx_device_create (Display
*dpy
, GLXContext gl_ctx
)
216 cairo_glx_context_t
*ctx
;
217 cairo_status_t status
;
219 const char *glx_extensions
;
221 ctx
= calloc (1, sizeof (cairo_glx_context_t
));
222 if (unlikely (ctx
== NULL
))
223 return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY
);
225 /* glx_dummy_window will call glXMakeCurrent, so we need to
226 * query the current state of the context now. */
227 _glx_query_current_state (ctx
);
229 status
= _glx_dummy_window (dpy
, gl_ctx
, &dummy
);
230 if (unlikely (status
)) {
232 return _cairo_gl_context_create_in_error (status
);
236 ctx
->dummy_window
= dummy
;
237 ctx
->context
= gl_ctx
;
239 ctx
->base
.acquire
= _glx_acquire
;
240 ctx
->base
.release
= _glx_release
;
241 ctx
->base
.make_current
= _glx_make_current
;
242 ctx
->base
.swap_buffers
= _glx_swap_buffers
;
243 ctx
->base
.destroy
= _glx_destroy
;
245 status
= _cairo_gl_dispatch_init (&ctx
->base
.dispatch
,
246 (cairo_gl_get_proc_addr_func_t
) glXGetProcAddress
);
247 if (unlikely (status
)) {
249 return _cairo_gl_context_create_in_error (status
);
252 status
= _cairo_gl_context_init (&ctx
->base
);
253 if (unlikely (status
)) {
255 return _cairo_gl_context_create_in_error (status
);
258 glx_extensions
= glXQueryExtensionsString (dpy
, DefaultScreen (dpy
));
259 if (strstr(glx_extensions
, "GLX_MESA_multithread_makecurrent")) {
260 ctx
->has_multithread_makecurrent
= TRUE
;
263 ctx
->base
.release (ctx
);
265 return &ctx
->base
.base
;
269 cairo_glx_device_get_display (cairo_device_t
*device
)
271 cairo_glx_context_t
*ctx
;
273 if (device
->backend
->type
!= CAIRO_DEVICE_TYPE_GL
) {
274 _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH
);
278 ctx
= (cairo_glx_context_t
*) device
;
284 cairo_glx_device_get_context (cairo_device_t
*device
)
286 cairo_glx_context_t
*ctx
;
288 if (device
->backend
->type
!= CAIRO_DEVICE_TYPE_GL
) {
289 _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH
);
293 ctx
= (cairo_glx_context_t
*) device
;
299 cairo_gl_surface_create_for_window (cairo_device_t
*device
,
304 cairo_glx_surface_t
*surface
;
306 if (unlikely (device
->status
))
307 return _cairo_surface_create_in_error (device
->status
);
309 if (device
->backend
->type
!= CAIRO_DEVICE_TYPE_GL
)
310 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH
));
312 if (width
<= 0 || height
<= 0)
313 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE
));
315 surface
= calloc (1, sizeof (cairo_glx_surface_t
));
316 if (unlikely (surface
== NULL
))
317 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY
));
319 _cairo_gl_surface_init (device
, &surface
->base
,
320 CAIRO_CONTENT_COLOR_ALPHA
, width
, height
);
323 return &surface
->base
.base
;