tizen 2.3.1 release
[framework/graphics/cairo.git] / boilerplate / cairo-boilerplate-glx.c
1 /* Cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Chris Wilson
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it either under the terms of the GNU Lesser General Public
7  * License version 2.1 as published by the Free Software Foundation
8  * (the "LGPL") or, at your option, under the terms of the Mozilla
9  * Public License Version 1.1 (the "MPL"). If you do not alter this
10  * notice, a recipient may use your version of this file under either
11  * the MPL or the LGPL.
12  *
13  * You should have received a copy of the LGPL along with this library
14  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16  * You should have received a copy of the MPL along with this library
17  * in the file COPYING-MPL-1.1
18  *
19  * The contents of this file are subject to the Mozilla Public License
20  * Version 1.1 (the "License"); you may not use this file except in
21  * compliance with the License. You may obtain a copy of the License at
22  * http://www.mozilla.org/MPL/
23  *
24  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26  * the specific language governing rights and limitations.
27  *
28  * The Original Code is the cairo graphics library.
29  *
30  * The Initial Developer of the Original Code is Chris Wilson.
31  */
32
33 #include "cairo-boilerplate-private.h"
34
35 #include <cairo-gl.h>
36
37 #include <X11/X.h>
38 #include <X11/Xutil.h> /* for XDestroyImage */
39
40 static const cairo_user_data_key_t gl_closure_key;
41
42 typedef struct _gl_target_closure {
43     Display *dpy;
44     int screen;
45     Window drawable;
46
47     GLXContext ctx;
48     cairo_device_t *device;
49     cairo_surface_t *surface;
50 } gl_target_closure_t;
51
52 static void
53 _cairo_boilerplate_gl_cleanup (void *closure)
54 {
55     gl_target_closure_t *gltc = closure;
56
57     cairo_device_finish (gltc->device);
58     cairo_device_destroy (gltc->device);
59
60     glXDestroyContext (gltc->dpy, gltc->ctx);
61
62     if (gltc->drawable)
63         XDestroyWindow (gltc->dpy, gltc->drawable);
64     XCloseDisplay (gltc->dpy);
65
66     free (gltc);
67 }
68
69 static cairo_surface_t *
70 _cairo_boilerplate_gl_create_surface (const char                *name,
71                                       cairo_content_t            content,
72                                       double                     width,
73                                       double                     height,
74                                       double                     max_width,
75                                       double                     max_height,
76                                       cairo_boilerplate_mode_t   mode,
77                                       void                     **closure)
78 {
79     int rgba_attribs[] = { GLX_RGBA,
80                            GLX_RED_SIZE, 1,
81                            GLX_GREEN_SIZE, 1,
82                            GLX_BLUE_SIZE, 1,
83                            GLX_ALPHA_SIZE, 1,
84                            GLX_DOUBLEBUFFER,
85                            None };
86     int rgb_attribs[] = { GLX_RGBA,
87                           GLX_RED_SIZE, 1,
88                           GLX_GREEN_SIZE, 1,
89                           GLX_BLUE_SIZE, 1,
90                           GLX_DOUBLEBUFFER,
91                           None };
92     XVisualInfo *visinfo;
93     GLXContext ctx;
94     gl_target_closure_t *gltc;
95     cairo_surface_t *surface;
96     Display *dpy;
97
98     gltc = calloc (1, sizeof (gl_target_closure_t));
99     *closure = gltc;
100
101     width = ceil (width);
102     height = ceil (height);
103
104     if (width == 0)
105         width = 1;
106     if (height == 0)
107         height = 1;
108
109     dpy = XOpenDisplay (NULL);
110     gltc->dpy = dpy;
111     if (!gltc->dpy) {
112         fprintf (stderr, "Failed to open display: %s\n", XDisplayName(0));
113         free (gltc);
114         return NULL;
115     }
116
117     if (mode == CAIRO_BOILERPLATE_MODE_TEST)
118         XSynchronize (gltc->dpy, 1);
119
120     if (content == CAIRO_CONTENT_COLOR)
121         visinfo = glXChooseVisual (dpy, DefaultScreen (dpy), rgb_attribs);
122     else
123         visinfo = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
124
125     if (visinfo == NULL) {
126         fprintf (stderr, "Failed to create RGB, double-buffered visual\n");
127         XCloseDisplay (dpy);
128         free (gltc);
129         return NULL;
130     }
131
132     ctx = glXCreateContext (dpy, visinfo, NULL, True);
133     XFree (visinfo);
134
135     gltc->ctx = ctx;
136     gltc->device = cairo_glx_device_create (dpy, ctx);
137
138     gltc->surface = surface = cairo_gl_surface_create (gltc->device,
139                                                        content, width, height);
140     if (cairo_surface_status (surface))
141         _cairo_boilerplate_gl_cleanup (gltc);
142
143     return surface;
144 }
145
146 static cairo_surface_t *
147 _cairo_boilerplate_gl_create_window_common (int                         rgba_attribs[],
148                                             cairo_content_t             content,
149                                             double                      width,
150                                             double                      height,
151                                             double                      max_width,
152                                             double                      max_height,
153                                             cairo_boilerplate_mode_t    mode,
154                                             gl_target_closure_t         *gltc)
155 {
156     XVisualInfo *vi;
157     GLXContext ctx;
158     cairo_surface_t *surface;
159     Display *dpy;
160     XSetWindowAttributes attr;
161
162     width = ceil (width);
163     height = ceil (height);
164
165     if (width == 0)
166         width = 1;
167     if (height == 0)
168         height = 1;
169
170     dpy = XOpenDisplay (NULL);
171     gltc->dpy = dpy;
172     if (!gltc->dpy) {
173         fprintf (stderr, "Failed to open display: %s\n", XDisplayName(0));
174         free (gltc);
175         return NULL;
176     }
177
178     if (mode == CAIRO_BOILERPLATE_MODE_TEST)
179         XSynchronize (gltc->dpy, 1);
180
181     vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
182     if (vi == NULL) {
183         fprintf (stderr, "Failed to create RGBA, double-buffered visual\n");
184         XCloseDisplay (dpy);
185         free (gltc);
186         return NULL;
187     }
188
189     attr.colormap = XCreateColormap (dpy,
190                                      RootWindow (dpy, vi->screen),
191                                      vi->visual,
192                                      AllocNone);
193     attr.border_pixel = 0;
194     attr.override_redirect = True;
195     gltc->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy), 0, 0,
196                                     width, height, 0, vi->depth,
197                                     InputOutput, vi->visual,
198                                     CWOverrideRedirect | CWBorderPixel | CWColormap,
199                                     &attr);
200     XMapWindow (dpy, gltc->drawable);
201
202     ctx = glXCreateContext (dpy, vi, NULL, True);
203     XFree (vi);
204
205     gltc->ctx = ctx;
206     gltc->device = cairo_glx_device_create (dpy, ctx);
207
208     gltc->surface = surface = cairo_gl_surface_create_for_window (gltc->device,
209                                                                   gltc->drawable,
210                                                                   width, height);
211     if (cairo_surface_status (surface)) {
212         _cairo_boilerplate_gl_cleanup (gltc);
213         return NULL;
214     }
215     return surface;
216 }
217
218 static cairo_surface_t *
219 _cairo_boilerplate_gl_create_window (const char                *name,
220                                      cairo_content_t            content,
221                                      double                     width,
222                                      double                     height,
223                                      double                     max_width,
224                                      double                     max_height,
225                                      cairo_boilerplate_mode_t   mode,
226                                      void                     **closure)
227 {
228     gl_target_closure_t *gltc;
229
230     int rgba_attribs[] = { GLX_RGBA,
231                            GLX_RED_SIZE, 1,
232                            GLX_GREEN_SIZE, 1,
233                            GLX_BLUE_SIZE, 1,
234                            GLX_ALPHA_SIZE, 1,
235                            GLX_DOUBLEBUFFER,
236                            None };
237
238     gltc = calloc (1, sizeof (gl_target_closure_t));
239     *closure = gltc;
240
241     return _cairo_boilerplate_gl_create_window_common (rgba_attribs, content,
242                                                        width, height,
243                                                        max_width, max_height,
244                                                        mode, gltc);
245 }
246
247 static cairo_surface_t *
248 _cairo_boilerplate_gl_create_window_msaa (const char                   *name,
249                                           cairo_content_t               content,
250                                           double                        width,
251                                           double                        height,
252                                           double                        max_width,
253                                           double                        max_height,
254                                           cairo_boilerplate_mode_t      mode,
255                                           void                        **closure)
256 {
257     gl_target_closure_t *gltc;
258
259     int rgba_attribs[] = { GLX_RGBA,
260                            GLX_RED_SIZE, 1,
261                            GLX_GREEN_SIZE, 1,
262                            GLX_BLUE_SIZE, 1,
263                            GLX_ALPHA_SIZE, 1,
264                            GLX_STENCIL_SIZE, 1,
265                            GLX_SAMPLES, 4,
266                            GLX_SAMPLE_BUFFERS, 1,
267                            GLX_DOUBLEBUFFER,
268                            None };
269
270     gltc = calloc (1, sizeof (gl_target_closure_t));
271     *closure = gltc;
272     return _cairo_boilerplate_gl_create_window_common (rgba_attribs, content,
273                                                        width, height,
274                                                        max_width, max_height,
275                                                        mode, gltc);
276
277 }
278
279 static cairo_surface_t *
280 _cairo_boilerplate_gl_create_window_db (const char                *name,
281                                         cairo_content_t            content,
282                                         double                     width,
283                                         double                     height,
284                                         double                     max_width,
285                                         double                     max_height,
286                                         cairo_boilerplate_mode_t   mode,
287                                         void                     **closure)
288 {
289     cairo_status_t status;
290     cairo_surface_t *surface;
291     gl_target_closure_t *gltc;
292
293     int rgba_attribs[] = { GLX_RGBA,
294                            GLX_RED_SIZE, 1,
295                            GLX_GREEN_SIZE, 1,
296                            GLX_BLUE_SIZE, 1,
297                            GLX_ALPHA_SIZE, 1,
298                            GLX_DOUBLEBUFFER,
299                            None };
300
301     gltc = calloc (1, sizeof (gl_target_closure_t));
302     *closure = gltc;
303
304     surface = _cairo_boilerplate_gl_create_window_common (rgba_attribs, content,
305                                                           width, height,
306                                                           max_width, max_height,
307                                                           mode, gltc);
308
309    if (! surface)
310         return NULL;
311
312     surface = cairo_surface_create_similar (gltc->surface, content, width, height);
313     status = cairo_surface_set_user_data (surface, &gl_closure_key, gltc, NULL);
314     if (status == CAIRO_STATUS_SUCCESS)
315         return surface;
316
317     cairo_surface_destroy (surface);
318     _cairo_boilerplate_gl_cleanup (gltc);
319     return cairo_boilerplate_surface_create_in_error (status);
320 }
321
322 static cairo_status_t
323 _cairo_boilerplate_gl_finish_window (cairo_surface_t *surface)
324 {
325     gl_target_closure_t *gltc = cairo_surface_get_user_data (surface,
326                                                              &gl_closure_key);
327
328     if (gltc != NULL && gltc->surface != NULL) {
329         cairo_t *cr;
330
331         cr = cairo_create (gltc->surface);
332         cairo_surface_set_device_offset (surface, 0, 0);
333         cairo_set_source_surface (cr, surface, 0, 0);
334         cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
335         cairo_paint (cr);
336         cairo_destroy (cr);
337
338         surface = gltc->surface;
339     }
340
341     cairo_gl_surface_swapbuffers (surface);
342     return CAIRO_STATUS_SUCCESS;
343 }
344
345 static void
346 _cairo_boilerplate_gl_synchronize (void *closure)
347 {
348     gl_target_closure_t *gltc = closure;
349
350     if (cairo_device_acquire (gltc->device))
351         return;
352
353     glFinish ();
354
355     cairo_device_release (gltc->device);
356 }
357
358 static char *
359 _cairo_boilerplate_gl_describe (void *closure)
360 {
361     gl_target_closure_t *gltc = closure;
362     char *s;
363     const GLubyte *vendor, *renderer, *version;
364
365     if (cairo_device_acquire (gltc->device))
366         return NULL;
367
368     vendor   = glGetString (GL_VENDOR);
369     renderer = glGetString (GL_RENDERER);
370     version  = glGetString (GL_VERSION);
371
372     xasprintf (&s, "%s %s %s", vendor, renderer, version);
373
374     cairo_device_release (gltc->device);
375
376     return s;
377 }
378
379 static const cairo_boilerplate_target_t targets[] = {
380     {
381         "gl", "gl", NULL, NULL,
382         CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
383         "cairo_gl_surface_create",
384         _cairo_boilerplate_gl_create_surface,
385         cairo_surface_create_similar,
386         NULL, NULL,
387         _cairo_boilerplate_get_image_surface,
388         cairo_surface_write_to_png,
389         _cairo_boilerplate_gl_cleanup,
390         _cairo_boilerplate_gl_synchronize,
391         _cairo_boilerplate_gl_describe,
392         TRUE, FALSE, FALSE
393     },
394     {
395         "gl", "gl", NULL, NULL,
396         CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR, 1,
397         "cairo_gl_surface_create",
398         _cairo_boilerplate_gl_create_surface,
399         cairo_surface_create_similar,
400         NULL, NULL,
401         _cairo_boilerplate_get_image_surface,
402         cairo_surface_write_to_png,
403         _cairo_boilerplate_gl_cleanup,
404         _cairo_boilerplate_gl_synchronize,
405         _cairo_boilerplate_gl_describe,
406         FALSE, FALSE, FALSE
407     },
408     {
409         "gl-window", "gl", NULL, NULL,
410         CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
411         "cairo_gl_surface_create_for_window",
412         _cairo_boilerplate_gl_create_window,
413         cairo_surface_create_similar,
414         NULL,
415         _cairo_boilerplate_gl_finish_window,
416         _cairo_boilerplate_get_image_surface,
417         cairo_surface_write_to_png,
418         _cairo_boilerplate_gl_cleanup,
419         _cairo_boilerplate_gl_synchronize,
420         _cairo_boilerplate_gl_describe,
421         FALSE, FALSE, FALSE
422     },
423     {
424         "gl-window-msaa", "gl", NULL, NULL,
425         CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
426         "cairo_gl_surface_create_for_window",
427         _cairo_boilerplate_gl_create_window_msaa,
428         cairo_surface_create_similar,
429         NULL,
430         _cairo_boilerplate_gl_finish_window,
431         _cairo_boilerplate_get_image_surface,
432         cairo_surface_write_to_png,
433         _cairo_boilerplate_gl_cleanup,
434         _cairo_boilerplate_gl_synchronize,
435         _cairo_boilerplate_gl_describe,
436         FALSE, FALSE, FALSE
437     },
438     {
439         "gl-window&", "gl", NULL, NULL,
440         CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
441         "cairo_gl_surface_create_for_window",
442         _cairo_boilerplate_gl_create_window_db,
443         cairo_surface_create_similar,
444         NULL,
445         _cairo_boilerplate_gl_finish_window,
446         _cairo_boilerplate_get_image_surface,
447         cairo_surface_write_to_png,
448         _cairo_boilerplate_gl_cleanup,
449         _cairo_boilerplate_gl_synchronize,
450         _cairo_boilerplate_gl_describe,
451         FALSE, FALSE, FALSE
452     },
453 };
454 CAIRO_BOILERPLATE (gl, targets)