Tizen 2.0 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 (const char                *name,
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                                      void                     **closure)
155 {
156     int rgba_attribs[] = { GLX_RGBA,
157                            GLX_RED_SIZE, 1,
158                            GLX_GREEN_SIZE, 1,
159                            GLX_BLUE_SIZE, 1,
160                            GLX_ALPHA_SIZE, 1,
161                            GLX_DOUBLEBUFFER,
162                            None };
163
164     int msaa_attribs[] = { GLX_RGBA,
165                            GLX_RED_SIZE, 1,
166                            GLX_GREEN_SIZE, 1,
167                            GLX_BLUE_SIZE, 1,
168                            GLX_ALPHA_SIZE, 1,
169                            GLX_STENCIL_SIZE, 1,
170                            GLX_SAMPLES, 4,
171                            GLX_SAMPLE_BUFFERS, 1,
172                            GLX_DOUBLEBUFFER,
173                            None };
174
175     XVisualInfo *vi;
176     GLXContext ctx;
177     gl_target_closure_t *gltc;
178     cairo_surface_t *surface;
179     Display *dpy;
180     XSetWindowAttributes attr;
181
182     gltc = calloc (1, sizeof (gl_target_closure_t));
183     *closure = gltc;
184
185     width = ceil (width);
186     height = ceil (height);
187
188     if (width == 0)
189         width = 1;
190     if (height == 0)
191         height = 1;
192
193     dpy = XOpenDisplay (NULL);
194     gltc->dpy = dpy;
195     if (!gltc->dpy) {
196         fprintf (stderr, "Failed to open display: %s\n", XDisplayName(0));
197         free (gltc);
198         return NULL;
199     }
200
201     if (mode == CAIRO_BOILERPLATE_MODE_TEST)
202         XSynchronize (gltc->dpy, 1);
203
204     vi = glXChooseVisual (dpy, DefaultScreen (dpy), msaa_attribs);
205
206     if (vi == NULL)
207         vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
208
209     if (vi == NULL) {
210         fprintf (stderr, "Failed to create RGBA, double-buffered visual\n");
211         XCloseDisplay (dpy);
212         free (gltc);
213         return NULL;
214     }
215
216     attr.colormap = XCreateColormap (dpy,
217                                      RootWindow (dpy, vi->screen),
218                                      vi->visual,
219                                      AllocNone);
220     attr.border_pixel = 0;
221     attr.override_redirect = True;
222     gltc->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy), 0, 0,
223                                     width, height, 0, vi->depth,
224                                     InputOutput, vi->visual,
225                                     CWOverrideRedirect | CWBorderPixel | CWColormap,
226                                     &attr);
227     XMapWindow (dpy, gltc->drawable);
228
229     ctx = glXCreateContext (dpy, vi, NULL, True);
230     XFree (vi);
231
232     gltc->ctx = ctx;
233     gltc->device = cairo_glx_device_create (dpy, ctx);
234
235     gltc->surface = surface = cairo_gl_surface_create_for_window (gltc->device,
236                                                                   gltc->drawable,
237                                                                   width, height);
238     if (cairo_surface_status (surface))
239         _cairo_boilerplate_gl_cleanup (gltc);
240
241     return surface;
242 }
243
244 static cairo_surface_t *
245 _cairo_boilerplate_gl_create_window_db (const char                *name,
246                                         cairo_content_t            content,
247                                         double                     width,
248                                         double                     height,
249                                         double                     max_width,
250                                         double                     max_height,
251                                         cairo_boilerplate_mode_t   mode,
252                                         void                     **closure)
253 {
254     int rgba_attribs[] = { GLX_RGBA,
255                            GLX_RED_SIZE, 1,
256                            GLX_GREEN_SIZE, 1,
257                            GLX_BLUE_SIZE, 1,
258                            GLX_ALPHA_SIZE, 1,
259                            GLX_DOUBLEBUFFER,
260                            None };
261
262     int msaa_attribs[] = { GLX_RGBA,
263                            GLX_RED_SIZE, 1,
264                            GLX_GREEN_SIZE, 1,
265                            GLX_BLUE_SIZE, 1,
266                            GLX_ALPHA_SIZE, 1,
267                            GLX_STENCIL_SIZE, 1,
268                            GLX_SAMPLES, 4,
269                            GLX_SAMPLE_BUFFERS, 1,
270                            GLX_DOUBLEBUFFER,
271                            None };
272
273     XVisualInfo *vi;
274     GLXContext ctx;
275     gl_target_closure_t *gltc;
276     cairo_surface_t *surface;
277     Display *dpy;
278     XSetWindowAttributes attr;
279     cairo_status_t status;
280
281     gltc = calloc (1, sizeof (gl_target_closure_t));
282     *closure = gltc;
283
284     width = ceil (width);
285     height = ceil (height);
286
287     if (width == 0)
288         width = 1;
289     if (height == 0)
290         height = 1;
291
292     dpy = XOpenDisplay (NULL);
293     gltc->dpy = dpy;
294     if (!gltc->dpy) {
295         fprintf (stderr, "Failed to open display: %s\n", XDisplayName(0));
296         free (gltc);
297         return NULL;
298     }
299
300     if (mode == CAIRO_BOILERPLATE_MODE_TEST)
301         XSynchronize (gltc->dpy, 1);
302
303     vi = glXChooseVisual (dpy, DefaultScreen (dpy), msaa_attribs);
304
305     if (vi == NULL)
306         vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
307
308     if (vi == NULL) {
309         fprintf (stderr, "Failed to create RGBA, double-buffered visual\n");
310         XCloseDisplay (dpy);
311         free (gltc);
312         return NULL;
313     }
314
315     attr.colormap = XCreateColormap (dpy,
316                                      RootWindow (dpy, vi->screen),
317                                      vi->visual,
318                                      AllocNone);
319     attr.border_pixel = 0;
320     attr.override_redirect = True;
321     gltc->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy), 0, 0,
322                                     width, height, 0, vi->depth,
323                                     InputOutput, vi->visual,
324                                     CWOverrideRedirect | CWBorderPixel | CWColormap,
325                                     &attr);
326     XMapWindow (dpy, gltc->drawable);
327
328     ctx = glXCreateContext (dpy, vi, NULL, True);
329     XFree (vi);
330
331     gltc->ctx = ctx;
332     gltc->device = cairo_glx_device_create (dpy, ctx);
333
334     gltc->surface = cairo_gl_surface_create_for_window (gltc->device,
335                                                         gltc->drawable,
336                                                         width, height);
337     surface = cairo_surface_create_similar (gltc->surface, content, width, height);
338     status = cairo_surface_set_user_data (surface, &gl_closure_key, gltc, NULL);
339     if (status == CAIRO_STATUS_SUCCESS)
340         return surface;
341
342     cairo_surface_destroy (surface);
343     _cairo_boilerplate_gl_cleanup (gltc);
344     return cairo_boilerplate_surface_create_in_error (status);
345 }
346
347 static cairo_status_t
348 _cairo_boilerplate_gl_finish_window (cairo_surface_t *surface)
349 {
350     gl_target_closure_t *gltc = cairo_surface_get_user_data (surface,
351                                                              &gl_closure_key);
352
353     if (gltc != NULL && gltc->surface != NULL) {
354         cairo_t *cr;
355
356         cr = cairo_create (gltc->surface);
357         cairo_surface_set_device_offset (surface, 0, 0);
358         cairo_set_source_surface (cr, surface, 0, 0);
359         cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
360         cairo_paint (cr);
361         cairo_destroy (cr);
362
363         surface = gltc->surface;
364     }
365
366     cairo_gl_surface_swapbuffers (surface);
367     return CAIRO_STATUS_SUCCESS;
368 }
369
370 static void
371 _cairo_boilerplate_gl_synchronize (void *closure)
372 {
373     gl_target_closure_t *gltc = closure;
374
375     if (cairo_device_acquire (gltc->device))
376         return;
377
378     glFinish ();
379
380     cairo_device_release (gltc->device);
381 }
382
383 static char *
384 _cairo_boilerplate_gl_describe (void *closure)
385 {
386     gl_target_closure_t *gltc = closure;
387     char *s;
388     const GLubyte *vendor, *renderer, *version;
389
390     if (cairo_device_acquire (gltc->device))
391         return NULL;
392
393     vendor   = glGetString (GL_VENDOR);
394     renderer = glGetString (GL_RENDERER);
395     version  = glGetString (GL_VERSION);
396
397     xasprintf (&s, "%s %s %s", vendor, renderer, version);
398
399     cairo_device_release (gltc->device);
400
401     return s;
402 }
403
404 static const cairo_boilerplate_target_t targets[] = {
405     {
406         "gl", "gl", NULL, NULL,
407         CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
408         "cairo_gl_surface_create",
409         _cairo_boilerplate_gl_create_surface,
410         cairo_surface_create_similar,
411         NULL, NULL,
412         _cairo_boilerplate_get_image_surface,
413         cairo_surface_write_to_png,
414         _cairo_boilerplate_gl_cleanup,
415         _cairo_boilerplate_gl_synchronize,
416         _cairo_boilerplate_gl_describe,
417         TRUE, FALSE, FALSE
418     },
419     {
420         "gl", "gl", NULL, NULL,
421         CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR, 1,
422         "cairo_gl_surface_create",
423         _cairo_boilerplate_gl_create_surface,
424         cairo_surface_create_similar,
425         NULL, NULL,
426         _cairo_boilerplate_get_image_surface,
427         cairo_surface_write_to_png,
428         _cairo_boilerplate_gl_cleanup,
429         _cairo_boilerplate_gl_synchronize,
430         _cairo_boilerplate_gl_describe,
431         FALSE, FALSE, FALSE
432     },
433     {
434         "gl-window", "gl", NULL, NULL,
435         CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
436         "cairo_gl_surface_create_for_window",
437         _cairo_boilerplate_gl_create_window,
438         cairo_surface_create_similar,
439         NULL,
440         _cairo_boilerplate_gl_finish_window,
441         _cairo_boilerplate_get_image_surface,
442         cairo_surface_write_to_png,
443         _cairo_boilerplate_gl_cleanup,
444         _cairo_boilerplate_gl_synchronize,
445         _cairo_boilerplate_gl_describe,
446         FALSE, FALSE, FALSE
447     },
448     {
449         "gl-window&", "gl", NULL, NULL,
450         CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
451         "cairo_gl_surface_create_for_window",
452         _cairo_boilerplate_gl_create_window_db,
453         cairo_surface_create_similar,
454         NULL,
455         _cairo_boilerplate_gl_finish_window,
456         _cairo_boilerplate_get_image_surface,
457         cairo_surface_write_to_png,
458         _cairo_boilerplate_gl_cleanup,
459         _cairo_boilerplate_gl_synchronize,
460         _cairo_boilerplate_gl_describe,
461         FALSE, FALSE, FALSE
462     },
463 };
464 CAIRO_BOILERPLATE (gl, targets)