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