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;
55 GLXDrawable current_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)
71 return ctx->previous_context != ctx->context;
75 _glx_get_current_drawable (cairo_glx_context_t *ctx)
77 if (ctx->base.current_target == NULL ||
78 _cairo_gl_surface_is_texture (ctx->base.current_target)) {
79 return ctx->dummy_window;
82 return ((cairo_glx_surface_t *) ctx->base.current_target)->win;
86 _glx_query_current_state (cairo_glx_context_t * ctx)
88 ctx->previous_context = glXGetCurrentContext ();
92 _glx_acquire (void *abstract_ctx)
94 cairo_glx_context_t *ctx = abstract_ctx;
95 GLXDrawable current_drawable = _glx_get_current_drawable (ctx);
97 _glx_query_current_state (ctx);
98 if (!_context_acquisition_changed_glx_state (ctx))
101 _cairo_gl_context_reset (&ctx->base);
102 glXMakeCurrent (ctx->display, current_drawable, ctx->context);
103 ctx->current_drawable = current_drawable;
107 _glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface)
109 cairo_glx_context_t *ctx = abstract_ctx;
110 cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
112 /* Set the window as the target of our context. */
113 if (ctx->current_drawable != surface->win) {
114 glXMakeCurrent (ctx->display, surface->win, ctx->context);
115 ctx->current_drawable = surface->win;
120 _glx_release (void *abstract_ctx)
122 cairo_glx_context_t *ctx = abstract_ctx;
124 if (ctx->has_multithread_makecurrent || !ctx->base.thread_aware ||
125 !_context_acquisition_changed_glx_state (ctx)) {
129 glXMakeCurrent (ctx->display, None, None);
130 ctx->current_drawable = None;
134 _glx_swap_buffers (void *abstract_ctx,
135 cairo_gl_surface_t *abstract_surface)
137 cairo_glx_context_t *ctx = abstract_ctx;
138 cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
140 glXSwapBuffers (ctx->display, surface->win);
144 _glx_destroy (void *abstract_ctx)
146 cairo_glx_context_t *ctx = abstract_ctx;
148 if (ctx->dummy_window != None)
149 XDestroyWindow (ctx->display, ctx->dummy_window);
151 glXMakeCurrent (ctx->display, None, None);
154 static cairo_status_t
155 _glx_dummy_window (Display *dpy, GLXContext gl_ctx, Window *dummy)
157 int attr[3] = { GLX_FBCONFIG_ID, 0, None };
161 XSetWindowAttributes swa;
165 /* Create a dummy window created for the target GLX context that we can
166 * use to query the available GL/GLX extensions.
168 glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]);
171 config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt);
172 if (unlikely (cnt == 0))
173 return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
175 vi = glXGetVisualFromFBConfig (dpy, config[0]);
178 if (unlikely (vi == NULL))
179 return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
181 cmap = XCreateColormap (dpy,
182 RootWindow (dpy, vi->screen),
186 swa.border_pixel = 0;
187 win = XCreateWindow (dpy, RootWindow (dpy, vi->screen),
192 CWBorderPixel | CWColormap, &swa);
193 XFreeColormap (dpy, cmap);
197 if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) {
198 XDestroyWindow (dpy, win);
199 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
203 return CAIRO_STATUS_SUCCESS;
206 static cairo_gl_generic_func_t
207 _cairo_glx_get_proc_address (void *data, const char *name)
211 cairo_gl_generic_func_t func;
214 { (cairo_gl_generic_func_t)glActiveTexture, "glActiveTexture" },
215 { (cairo_gl_generic_func_t)glBindTexture, "glBindTexture" },
216 { (cairo_gl_generic_func_t)glBlendFunc, "glBlendFunc" },
217 { (cairo_gl_generic_func_t)glBlendFuncSeparate,"glBlendFuncSeparate"},
218 { (cairo_gl_generic_func_t)glClear, "glClear" },
219 { (cairo_gl_generic_func_t)glClearColor, "glClearColor" },
220 { (cairo_gl_generic_func_t)glClearStencil, "glClearStencil" },
221 { (cairo_gl_generic_func_t)glColorMask, "glColorMask" },
222 { (cairo_gl_generic_func_t)glDeleteTextures,"glDeleteTextures" },
223 { (cairo_gl_generic_func_t)glDepthMask, "glDepthMask" },
224 { (cairo_gl_generic_func_t)glDisable, "glDisable" },
225 { (cairo_gl_generic_func_t)glDrawArrays, "glDrawArrays" },
226 { (cairo_gl_generic_func_t)glDrawBuffer, "glDrawBuffer" },
227 { (cairo_gl_generic_func_t)glDrawElements, "glDrawElements" },
228 { (cairo_gl_generic_func_t)glEnable, "glEnable" },
229 { (cairo_gl_generic_func_t)glGenTextures, "glGenTextures" },
230 { (cairo_gl_generic_func_t)glGetBooleanv, "glGetBooleanv" },
231 { (cairo_gl_generic_func_t)glGetError, "glGetError" },
232 { (cairo_gl_generic_func_t)glGetFloatv, "glGetFloatv" },
233 { (cairo_gl_generic_func_t)glGetIntegerv, "glGetIntegerv" },
234 { (cairo_gl_generic_func_t)glGetString, "glGetString" },
235 { (cairo_gl_generic_func_t)glFlush, "glFlush" },
236 { (cairo_gl_generic_func_t)glPixelStorei, "glPixelStorei" },
237 { (cairo_gl_generic_func_t)glReadBuffer, "glReadBuffer" },
238 { (cairo_gl_generic_func_t)glReadPixels, "glReadPixels" },
239 { (cairo_gl_generic_func_t)glScissor, "glScissor" },
240 { (cairo_gl_generic_func_t)glStencilFunc, "glStencilFunc" },
241 { (cairo_gl_generic_func_t)glStencilMask, "glStencilMask" },
242 { (cairo_gl_generic_func_t)glStencilOp, "glStencilOp" },
243 { (cairo_gl_generic_func_t)glTexImage2D, "glTexImage2D" },
244 { (cairo_gl_generic_func_t)glTexSubImage2D, "glTexSubImage2D" },
245 { (cairo_gl_generic_func_t)glTexParameteri, "glTexParameteri" },
246 { (cairo_gl_generic_func_t)glViewport, "glViewport" },
250 for (i = 0; func_map[i].name; i++) {
251 if (! strcmp (func_map[i].name, name))
252 return func_map[i].func;
255 return glXGetProcAddress (name);
259 cairo_glx_device_create (Display *dpy, GLXContext gl_ctx)
261 cairo_glx_context_t *ctx;
262 cairo_status_t status;
264 const char *glx_extensions;
266 ctx = calloc (1, sizeof (cairo_glx_context_t));
267 if (unlikely (ctx == NULL))
268 return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
270 /* glx_dummy_window will call glXMakeCurrent, so we need to
271 * query the current state of the context now. */
272 _glx_query_current_state (ctx);
274 status = _glx_dummy_window (dpy, gl_ctx, &dummy);
275 if (unlikely (status)) {
277 return _cairo_gl_context_create_in_error (status);
280 ctx->current_drawable = dummy;
283 ctx->dummy_window = dummy;
284 ctx->context = gl_ctx;
286 ctx->base.acquire = _glx_acquire;
287 ctx->base.release = _glx_release;
288 ctx->base.make_current = _glx_make_current;
289 ctx->base.swap_buffers = _glx_swap_buffers;
290 ctx->base.destroy = _glx_destroy;
292 status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
293 (cairo_gl_get_proc_addr_func_t) _cairo_glx_get_proc_address,
295 if (unlikely (status)) {
297 return _cairo_gl_context_create_in_error (status);
300 status = _cairo_gl_context_init (&ctx->base);
301 if (unlikely (status)) {
303 return _cairo_gl_context_create_in_error (status);
306 glx_extensions = glXQueryExtensionsString (dpy, DefaultScreen (dpy));
307 if (strstr(glx_extensions, "GLX_MESA_multithread_makecurrent")) {
308 ctx->has_multithread_makecurrent = TRUE;
311 ctx->base.release (ctx);
313 return &ctx->base.base;
317 cairo_glx_device_get_display (cairo_device_t *device)
319 cairo_glx_context_t *ctx;
321 if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
322 _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
326 ctx = (cairo_glx_context_t *) device;
332 cairo_glx_device_get_context (cairo_device_t *device)
334 cairo_glx_context_t *ctx;
336 if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
337 _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
341 ctx = (cairo_glx_context_t *) device;
347 cairo_gl_surface_create_for_window (cairo_device_t *device,
352 cairo_glx_surface_t *surface;
354 if (unlikely (device->status))
355 return _cairo_surface_create_in_error (device->status);
357 if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
358 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
360 if (width <= 0 || height <= 0)
361 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
363 surface = calloc (1, sizeof (cairo_glx_surface_t));
364 if (unlikely (surface == NULL))
365 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
367 _cairo_gl_surface_init (device, &surface->base,
368 CAIRO_CONTENT_COLOR_ALPHA, width, height);
371 return &surface->base.base;