1 /*
2 Copyright 2012-2020 David Robillard <d@drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "types.h"
18 #include "x11.h"
19
20 #include "pugl/cairo.h"
21 #include "pugl/pugl.h"
22
23 #include <X11/Xutil.h>
24 #include <cairo-xlib.h>
25 #include <cairo.h>
26
27 #include <stdlib.h>
28
29 typedef struct {
30 cairo_surface_t* back;
31 cairo_surface_t* front;
32 cairo_t* cr;
33 } PuglX11CairoSurface;
34
35 static void
puglX11CairoClose(PuglView * view)36 puglX11CairoClose(PuglView* view)
37 {
38 PuglInternals* const impl = view->impl;
39 PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
40
41 cairo_surface_destroy(surface->front);
42 cairo_surface_destroy(surface->back);
43 surface->front = surface->back = NULL;
44 }
45
46 static PuglStatus
puglX11CairoOpen(PuglView * view)47 puglX11CairoOpen(PuglView* view)
48 {
49 PuglInternals* const impl = view->impl;
50 PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
51
52 surface->back = cairo_xlib_surface_create(impl->display,
53 impl->win,
54 impl->vi->visual,
55 (int)view->frame.width,
56 (int)view->frame.height);
57
58 surface->front =
59 cairo_surface_create_similar(surface->back,
60 cairo_surface_get_content(surface->back),
61 (int)view->frame.width,
62 (int)view->frame.height);
63
64 if (cairo_surface_status(surface->back) ||
65 cairo_surface_status(surface->front)) {
66 puglX11CairoClose(view);
67 return PUGL_CREATE_CONTEXT_FAILED;
68 }
69
70 return PUGL_SUCCESS;
71 }
72
73 static PuglStatus
puglX11CairoCreate(PuglView * view)74 puglX11CairoCreate(PuglView* view)
75 {
76 PuglInternals* const impl = view->impl;
77
78 impl->surface = (cairo_surface_t*)calloc(1, sizeof(PuglX11CairoSurface));
79
80 return PUGL_SUCCESS;
81 }
82
83 static PuglStatus
puglX11CairoDestroy(PuglView * view)84 puglX11CairoDestroy(PuglView* view)
85 {
86 PuglInternals* const impl = view->impl;
87 PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
88
89 puglX11CairoClose(view);
90 free(surface);
91
92 return PUGL_SUCCESS;
93 }
94
95 static PuglStatus
puglX11CairoEnter(PuglView * view,const PuglEventExpose * expose)96 puglX11CairoEnter(PuglView* view, const PuglEventExpose* expose)
97 {
98 PuglInternals* const impl = view->impl;
99 PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
100 PuglStatus st = PUGL_SUCCESS;
101
102 if (expose && !(st = puglX11CairoOpen(view))) {
103 surface->cr = cairo_create(surface->front);
104
105 if (cairo_status(surface->cr)) {
106 st = PUGL_CREATE_CONTEXT_FAILED;
107 }
108 }
109
110 return st;
111 }
112
113 static PuglStatus
puglX11CairoLeave(PuglView * view,const PuglEventExpose * expose)114 puglX11CairoLeave(PuglView* view, const PuglEventExpose* expose)
115 {
116 PuglInternals* const impl = view->impl;
117 PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
118
119 if (expose) {
120 // Destroy front context and create a new one for drawing to the back
121 cairo_destroy(surface->cr);
122 surface->cr = cairo_create(surface->back);
123
124 // Clip to expose region
125 cairo_rectangle(
126 surface->cr, expose->x, expose->y, expose->width, expose->height);
127 cairo_clip(surface->cr);
128
129 // Paint front onto back
130 cairo_set_source_surface(surface->cr, surface->front, 0, 0);
131 cairo_paint(surface->cr);
132
133 // Flush to X and close everything
134 cairo_destroy(surface->cr);
135 cairo_surface_flush(surface->back);
136 puglX11CairoClose(view);
137 surface->cr = NULL;
138 }
139
140 return PUGL_SUCCESS;
141 }
142
143 static void*
puglX11CairoGetContext(PuglView * view)144 puglX11CairoGetContext(PuglView* view)
145 {
146 PuglInternals* const impl = view->impl;
147 PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
148
149 return surface->cr;
150 }
151
152 const PuglBackend*
puglCairoBackend(void)153 puglCairoBackend(void)
154 {
155 static const PuglBackend backend = {puglX11StubConfigure,
156 puglX11CairoCreate,
157 puglX11CairoDestroy,
158 puglX11CairoEnter,
159 puglX11CairoLeave,
160 puglX11CairoGetContext};
161
162 return &backend;
163 }
164