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