tizen 2.3.1 release
[framework/graphics/cairo.git] / src / cairo-glx-context.c
1 /* cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Eric Anholt
4  * Copyright © 2009 Chris Wilson
5  * Copyright © 2005 Red Hat, Inc
6  *
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.
14  *
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
20  *
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/
25  *
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.
29  *
30  * The Original Code is the cairo graphics library.
31  *
32  * The Initial Developer of the Original Code is Red Hat, Inc.
33  *
34  * Contributor(s):
35  *      Carl Worth <cworth@cworth.org>
36  *      Chris Wilson <chris@chris-wilson.co.uk>
37  */
38
39 #include "cairoint.h"
40
41 #include "cairo-gl-private.h"
42
43 #include "cairo-error-private.h"
44
45 #include <X11/Xutil.h>
46
47 /* XXX needs hooking into XCloseDisplay() */
48
49 typedef struct _cairo_glx_context {
50     cairo_gl_context_t base;
51
52     Display *display;
53     Window dummy_window;
54     GLXContext context;
55     GLXDrawable current_drawable;
56
57     GLXContext previous_context;
58
59     cairo_bool_t has_multithread_makecurrent;
60 } cairo_glx_context_t;
61
62 typedef struct _cairo_glx_surface {
63     cairo_gl_surface_t base;
64
65     Window win;
66 } cairo_glx_surface_t;
67
68 static cairo_bool_t
69 _context_acquisition_changed_glx_state (cairo_glx_context_t *ctx)
70 {
71     return ctx->previous_context != ctx->context;
72 }
73
74 static GLXDrawable
75 _glx_get_current_drawable (cairo_glx_context_t *ctx)
76 {
77     if (ctx->base.current_target == NULL ||
78         _cairo_gl_surface_is_texture (ctx->base.current_target)) {
79         return ctx->dummy_window;
80     }
81
82     return ((cairo_glx_surface_t *) ctx->base.current_target)->win;
83 }
84
85 static void *
86 _glx_query_current_state (cairo_glx_context_t * ctx)
87 {
88     ctx->previous_context = glXGetCurrentContext ();
89 }
90
91 static void
92 _glx_acquire (void *abstract_ctx)
93 {
94     cairo_glx_context_t *ctx = abstract_ctx;
95     GLXDrawable current_drawable = _glx_get_current_drawable (ctx);
96
97     _glx_query_current_state (ctx);
98     if (!_context_acquisition_changed_glx_state (ctx))
99         return;
100
101     _cairo_gl_context_reset (&ctx->base);
102     glXMakeCurrent (ctx->display, current_drawable, ctx->context);
103     ctx->current_drawable = current_drawable;
104 }
105
106 static void
107 _glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface)
108 {
109     cairo_glx_context_t *ctx = abstract_ctx;
110     cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
111
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;
116     }
117 }
118
119 static void
120 _glx_release (void *abstract_ctx)
121 {
122     cairo_glx_context_t *ctx = abstract_ctx;
123
124     if (ctx->has_multithread_makecurrent || !ctx->base.thread_aware ||
125         !_context_acquisition_changed_glx_state (ctx)) {
126         return;
127     }
128
129     glXMakeCurrent (ctx->display, None, None);
130     ctx->current_drawable = None;
131 }
132
133 static void
134 _glx_swap_buffers (void *abstract_ctx,
135                    cairo_gl_surface_t *abstract_surface)
136 {
137     cairo_glx_context_t *ctx = abstract_ctx;
138     cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
139
140     glXSwapBuffers (ctx->display, surface->win);
141 }
142
143 static void
144 _glx_destroy (void *abstract_ctx)
145 {
146     cairo_glx_context_t *ctx = abstract_ctx;
147
148     if (ctx->dummy_window != None)
149         XDestroyWindow (ctx->display, ctx->dummy_window);
150
151     glXMakeCurrent (ctx->display, None, None);
152 }
153
154 static cairo_status_t
155 _glx_dummy_window (Display *dpy, GLXContext gl_ctx, Window *dummy)
156 {
157     int attr[3] = { GLX_FBCONFIG_ID, 0, None };
158     GLXFBConfig *config;
159     XVisualInfo *vi;
160     Colormap cmap;
161     XSetWindowAttributes swa;
162     Window win = None;
163     int cnt;
164
165     /* Create a dummy window created for the target GLX context that we can
166      * use to query the available GL/GLX extensions.
167      */
168     glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]);
169
170     cnt = 0;
171     config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt);
172     if (unlikely (cnt == 0))
173         return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
174
175     vi = glXGetVisualFromFBConfig (dpy, config[0]);
176     XFree (config);
177
178     if (unlikely (vi == NULL))
179         return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
180
181     cmap = XCreateColormap (dpy,
182                             RootWindow (dpy, vi->screen),
183                             vi->visual,
184                             AllocNone);
185     swa.colormap = cmap;
186     swa.border_pixel = 0;
187     win = XCreateWindow (dpy, RootWindow (dpy, vi->screen),
188                          -1, -1, 1, 1, 0,
189                          vi->depth,
190                          InputOutput,
191                          vi->visual,
192                          CWBorderPixel | CWColormap, &swa);
193     XFreeColormap (dpy, cmap);
194     XFree (vi);
195
196     XFlush (dpy);
197     if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) {
198         XDestroyWindow (dpy, win);
199         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
200     }
201
202     *dummy = win;
203     return CAIRO_STATUS_SUCCESS;
204 }
205
206 static cairo_gl_generic_func_t
207 _cairo_glx_get_proc_address (void *data, const char *name)
208 {
209     int i;
210     struct {
211         cairo_gl_generic_func_t func;
212         const char *name;
213     } func_map[] = {
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"            },
247     { NULL,                                      NULL                   }
248     };
249
250     for (i = 0; func_map[i].name; i++) {
251         if (! strcmp (func_map[i].name, name))
252             return func_map[i].func;
253     }
254
255   return glXGetProcAddress (name);
256 }
257
258 cairo_device_t *
259 cairo_glx_device_create (Display *dpy, GLXContext gl_ctx)
260 {
261     cairo_glx_context_t *ctx;
262     cairo_status_t status;
263     Window dummy = None;
264     const char *glx_extensions;
265
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);
269
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);
273
274     status = _glx_dummy_window (dpy, gl_ctx, &dummy);
275     if (unlikely (status)) {
276         free (ctx);
277         return _cairo_gl_context_create_in_error (status);
278     }
279
280     ctx->current_drawable = dummy;
281
282     ctx->display = dpy;
283     ctx->dummy_window = dummy;
284     ctx->context = gl_ctx;
285
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;
291
292     status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
293                                       (cairo_gl_get_proc_addr_func_t) _cairo_glx_get_proc_address,
294                                       NULL);
295     if (unlikely (status)) {
296         free (ctx);
297         return _cairo_gl_context_create_in_error (status);
298     }
299
300     status = _cairo_gl_context_init (&ctx->base);
301     if (unlikely (status)) {
302         free (ctx);
303         return _cairo_gl_context_create_in_error (status);
304     }
305
306     glx_extensions = glXQueryExtensionsString (dpy, DefaultScreen (dpy));
307     if (strstr(glx_extensions, "GLX_MESA_multithread_makecurrent")) {
308         ctx->has_multithread_makecurrent = TRUE;
309     }
310
311     ctx->base.release (ctx);
312
313     return &ctx->base.base;
314 }
315
316 Display *
317 cairo_glx_device_get_display (cairo_device_t *device)
318 {
319     cairo_glx_context_t *ctx;
320
321     if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
322         _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
323         return NULL;
324     }
325
326     ctx = (cairo_glx_context_t *) device;
327
328     return ctx->display;
329 }
330
331 GLXContext
332 cairo_glx_device_get_context (cairo_device_t *device)
333 {
334     cairo_glx_context_t *ctx;
335
336     if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
337         _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
338         return NULL;
339     }
340
341     ctx = (cairo_glx_context_t *) device;
342
343     return ctx->context;
344 }
345
346 cairo_surface_t *
347 cairo_gl_surface_create_for_window (cairo_device_t      *device,
348                                     Window               win,
349                                     int                  width,
350                                     int                  height)
351 {
352     cairo_glx_surface_t *surface;
353
354     if (unlikely (device->status))
355         return _cairo_surface_create_in_error (device->status);
356
357     if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
358         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
359
360     if (width <= 0 || height <= 0)
361         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
362
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));
366
367     _cairo_gl_surface_init (device, &surface->base,
368                             CAIRO_CONTENT_COLOR_ALPHA, width, height);
369     surface->win = win;
370
371     return &surface->base.base;
372 }