1 /* GDK - The GIMP Drawing Kit
2 *
3 * Copyright © 2018 Benjamin Otte
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "config.h"
20
21 #include "gdkcairocontext-wayland.h"
22
23 #include "gdkprivate-wayland.h"
24
25 #include "gdkinternals.h"
26 #include "gdkprofilerprivate.h"
27
28 static const cairo_user_data_key_t gdk_wayland_cairo_context_key;
29 static const cairo_user_data_key_t gdk_wayland_cairo_region_key;
30
G_DEFINE_TYPE(GdkWaylandCairoContext,gdk_wayland_cairo_context,GDK_TYPE_CAIRO_CONTEXT)31 G_DEFINE_TYPE (GdkWaylandCairoContext, gdk_wayland_cairo_context, GDK_TYPE_CAIRO_CONTEXT)
32
33 static void
34 gdk_wayland_cairo_context_surface_add_region (cairo_surface_t *surface,
35 const cairo_region_t *region)
36 {
37 cairo_region_t *surface_region;
38
39 surface_region = cairo_surface_get_user_data (surface, &gdk_wayland_cairo_region_key);
40 if (surface_region == NULL)
41 {
42 surface_region = cairo_region_copy (region);
43 cairo_surface_set_user_data (surface,
44 &gdk_wayland_cairo_region_key,
45 surface_region,
46 (cairo_destroy_func_t) cairo_region_destroy);
47 }
48 else
49 {
50 cairo_region_union (surface_region, region);
51 }
52 }
53
54 static void
gdk_wayland_cairo_context_surface_clear_region(cairo_surface_t * surface)55 gdk_wayland_cairo_context_surface_clear_region (cairo_surface_t *surface)
56 {
57 cairo_surface_set_user_data (surface, &gdk_wayland_cairo_region_key, NULL, NULL);
58 }
59
60 static const cairo_region_t *
gdk_wayland_cairo_context_surface_get_region(cairo_surface_t * surface)61 gdk_wayland_cairo_context_surface_get_region (cairo_surface_t *surface)
62 {
63 return cairo_surface_get_user_data (surface, &gdk_wayland_cairo_region_key);
64 }
65
66 static GdkWaylandCairoContext *
gdk_wayland_cairo_context_get_from_surface(cairo_surface_t * surface)67 gdk_wayland_cairo_context_get_from_surface (cairo_surface_t *surface)
68 {
69 return cairo_surface_get_user_data (surface, &gdk_wayland_cairo_context_key);
70 }
71
72 static void
gdk_wayland_cairo_context_add_surface(GdkWaylandCairoContext * self,cairo_surface_t * surface)73 gdk_wayland_cairo_context_add_surface (GdkWaylandCairoContext *self,
74 cairo_surface_t *surface)
75 {
76 cairo_surface_reference (surface);
77 cairo_surface_set_user_data (surface, &gdk_wayland_cairo_context_key, self, NULL);
78
79 self->surfaces = g_slist_prepend (self->surfaces, surface);
80 }
81
82 static void
gdk_wayland_cairo_context_remove_surface(GdkWaylandCairoContext * self,cairo_surface_t * surface)83 gdk_wayland_cairo_context_remove_surface (GdkWaylandCairoContext *self,
84 cairo_surface_t *surface)
85 {
86 self->surfaces = g_slist_remove (self->surfaces, surface);
87
88 cairo_surface_set_user_data (surface, &gdk_wayland_cairo_context_key, NULL, NULL);
89 cairo_surface_destroy (surface);
90 }
91
92 static void
gdk_wayland_cairo_context_buffer_release(void * _data,struct wl_buffer * wl_buffer)93 gdk_wayland_cairo_context_buffer_release (void *_data,
94 struct wl_buffer *wl_buffer)
95 {
96 cairo_surface_t *cairo_surface = _data;
97 GdkWaylandCairoContext *self = gdk_wayland_cairo_context_get_from_surface (cairo_surface);
98
99 /* context was destroyed before compositor released this buffer */
100 if (self == NULL)
101 return;
102
103 /* Cache one surface for reuse when drawing */
104 if (self->cached_surface == NULL)
105 {
106 self->cached_surface = cairo_surface;
107 return;
108 }
109
110 /* Get rid of all the extra ones */
111 gdk_wayland_cairo_context_remove_surface (self, cairo_surface);
112 /* Release the reference the compositor held to this surface */
113 cairo_surface_destroy (cairo_surface);
114 }
115
116 static const struct wl_buffer_listener buffer_listener = {
117 gdk_wayland_cairo_context_buffer_release
118 };
119
120 static cairo_surface_t *
gdk_wayland_cairo_context_create_surface(GdkWaylandCairoContext * self)121 gdk_wayland_cairo_context_create_surface (GdkWaylandCairoContext *self)
122 {
123 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (self)));
124 GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self));
125 cairo_surface_t *cairo_surface;
126 struct wl_buffer *buffer;
127 cairo_region_t *region;
128 int width, height;
129
130 width = gdk_surface_get_width (surface);
131 height = gdk_surface_get_height (surface);
132 cairo_surface = _gdk_wayland_display_create_shm_surface (display_wayland,
133 width, height,
134 gdk_surface_get_scale_factor (surface));
135 buffer = _gdk_wayland_shm_surface_get_wl_buffer (cairo_surface);
136 wl_buffer_add_listener (buffer, &buffer_listener, cairo_surface);
137 gdk_wayland_cairo_context_add_surface (self, cairo_surface);
138
139 region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { 0, 0, width, height });
140 gdk_wayland_cairo_context_surface_add_region (cairo_surface, region);
141 cairo_region_destroy (region);
142
143 return cairo_surface;
144 }
145
146 static void
gdk_wayland_cairo_context_begin_frame(GdkDrawContext * draw_context,cairo_region_t * region)147 gdk_wayland_cairo_context_begin_frame (GdkDrawContext *draw_context,
148 cairo_region_t *region)
149 {
150 GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (draw_context);
151 const cairo_region_t *surface_region;
152 GSList *l;
153 cairo_t *cr;
154
155 if (self->cached_surface)
156 self->paint_surface = g_steal_pointer (&self->cached_surface);
157 else
158 self->paint_surface = gdk_wayland_cairo_context_create_surface (self);
159
160 surface_region = gdk_wayland_cairo_context_surface_get_region (self->paint_surface);
161 if (surface_region)
162 cairo_region_union (region, surface_region);
163
164 for (l = self->surfaces; l; l = l->next)
165 {
166 gdk_wayland_cairo_context_surface_add_region (l->data, region);
167 }
168
169 /* clear the repaint area */
170 cr = cairo_create (self->paint_surface);
171 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
172 gdk_cairo_region (cr, region);
173 cairo_fill (cr);
174 cairo_destroy (cr);
175 }
176
177 static void
gdk_wayland_cairo_context_end_frame(GdkDrawContext * draw_context,cairo_region_t * painted)178 gdk_wayland_cairo_context_end_frame (GdkDrawContext *draw_context,
179 cairo_region_t *painted)
180 {
181 GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (draw_context);
182 GdkSurface *surface = gdk_draw_context_get_surface (draw_context);
183
184 gdk_wayland_surface_attach_image (surface, self->paint_surface, painted);
185 gdk_wayland_surface_sync (surface);
186 gdk_wayland_surface_request_frame (surface);
187
188 gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "wayland", "surface commit");
189 gdk_wayland_surface_commit (surface);
190 gdk_wayland_surface_notify_committed (surface);
191
192 gdk_wayland_cairo_context_surface_clear_region (self->paint_surface);
193 self->paint_surface = NULL;
194 }
195
196 static void
gdk_wayland_cairo_context_clear_all_cairo_surfaces(GdkWaylandCairoContext * self)197 gdk_wayland_cairo_context_clear_all_cairo_surfaces (GdkWaylandCairoContext *self)
198 {
199 g_clear_pointer (&self->cached_surface, cairo_surface_destroy);
200 while (self->surfaces)
201 gdk_wayland_cairo_context_remove_surface (self, self->surfaces->data);
202 }
203
204 static void
gdk_wayland_cairo_context_surface_resized(GdkDrawContext * draw_context)205 gdk_wayland_cairo_context_surface_resized (GdkDrawContext *draw_context)
206 {
207 GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (draw_context);
208
209 gdk_wayland_cairo_context_clear_all_cairo_surfaces (self);
210 }
211
212 static cairo_t *
gdk_wayland_cairo_context_cairo_create(GdkCairoContext * context)213 gdk_wayland_cairo_context_cairo_create (GdkCairoContext *context)
214 {
215 GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (context);
216
217 return cairo_create (self->paint_surface);
218 }
219
220 static void
gdk_wayland_cairo_context_dispose(GObject * object)221 gdk_wayland_cairo_context_dispose (GObject *object)
222 {
223 GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (object);
224
225 gdk_wayland_cairo_context_clear_all_cairo_surfaces (self);
226 g_assert (self->cached_surface == NULL);
227 g_assert (self->paint_surface == NULL);
228
229 G_OBJECT_CLASS (gdk_wayland_cairo_context_parent_class)->dispose (object);
230 }
231
232 static void
gdk_wayland_cairo_context_class_init(GdkWaylandCairoContextClass * klass)233 gdk_wayland_cairo_context_class_init (GdkWaylandCairoContextClass *klass)
234 {
235 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
236 GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
237 GdkCairoContextClass *cairo_context_class = GDK_CAIRO_CONTEXT_CLASS (klass);
238
239 gobject_class->dispose = gdk_wayland_cairo_context_dispose;
240
241 draw_context_class->begin_frame = gdk_wayland_cairo_context_begin_frame;
242 draw_context_class->end_frame = gdk_wayland_cairo_context_end_frame;
243 draw_context_class->surface_resized = gdk_wayland_cairo_context_surface_resized;
244
245 cairo_context_class->cairo_create = gdk_wayland_cairo_context_cairo_create;
246 }
247
248 static void
gdk_wayland_cairo_context_init(GdkWaylandCairoContext * self)249 gdk_wayland_cairo_context_init (GdkWaylandCairoContext *self)
250 {
251 }
252
253