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 "gdkprofilerprivate.h"
26
27static const cairo_user_data_key_t gdk_wayland_cairo_context_key;
28static const cairo_user_data_key_t gdk_wayland_cairo_region_key;
29
30G_DEFINE_TYPE (GdkWaylandCairoContext, gdk_wayland_cairo_context, GDK_TYPE_CAIRO_CONTEXT)
31
32static void
33gdk_wayland_cairo_context_surface_add_region (cairo_surface_t *surface,
34 const cairo_region_t *region)
35{
36 cairo_region_t *surface_region;
37
38 surface_region = cairo_surface_get_user_data (surface, key: &gdk_wayland_cairo_region_key);
39 if (surface_region == NULL)
40 {
41 surface_region = cairo_region_copy (original: region);
42 cairo_surface_set_user_data (surface,
43 key: &gdk_wayland_cairo_region_key,
44 user_data: surface_region,
45 destroy: (cairo_destroy_func_t) cairo_region_destroy);
46 }
47 else
48 {
49 cairo_region_union (dst: surface_region, other: region);
50 }
51}
52
53static void
54gdk_wayland_cairo_context_surface_clear_region (cairo_surface_t *surface)
55{
56 cairo_surface_set_user_data (surface, key: &gdk_wayland_cairo_region_key, NULL, NULL);
57}
58
59static const cairo_region_t *
60gdk_wayland_cairo_context_surface_get_region (cairo_surface_t *surface)
61{
62 return cairo_surface_get_user_data (surface, key: &gdk_wayland_cairo_region_key);
63}
64
65static GdkWaylandCairoContext *
66gdk_wayland_cairo_context_get_from_surface (cairo_surface_t *surface)
67{
68 return cairo_surface_get_user_data (surface, key: &gdk_wayland_cairo_context_key);
69}
70
71static void
72gdk_wayland_cairo_context_add_surface (GdkWaylandCairoContext *self,
73 cairo_surface_t *surface)
74{
75 cairo_surface_reference (surface);
76 cairo_surface_set_user_data (surface, key: &gdk_wayland_cairo_context_key, user_data: self, NULL);
77
78 self->surfaces = g_slist_prepend (list: self->surfaces, data: surface);
79}
80
81static void
82gdk_wayland_cairo_context_remove_surface (GdkWaylandCairoContext *self,
83 cairo_surface_t *surface)
84{
85 self->surfaces = g_slist_remove (list: self->surfaces, data: surface);
86
87 cairo_surface_set_user_data (surface, key: &gdk_wayland_cairo_context_key, NULL, NULL);
88 cairo_surface_destroy (surface);
89}
90
91static void
92gdk_wayland_cairo_context_buffer_release (void *_data,
93 struct wl_buffer *wl_buffer)
94{
95 cairo_surface_t *cairo_surface = _data;
96 GdkWaylandCairoContext *self = gdk_wayland_cairo_context_get_from_surface (surface: cairo_surface);
97
98 /* context was destroyed before compositor released this buffer */
99 if (self == NULL)
100 return;
101
102 /* Cache one surface for reuse when drawing */
103 if (self->cached_surface == NULL)
104 {
105 self->cached_surface = cairo_surface;
106 return;
107 }
108
109 /* Get rid of all the extra ones */
110 gdk_wayland_cairo_context_remove_surface (self, surface: cairo_surface);
111 /* Release the reference the compositor held to this surface */
112 cairo_surface_destroy (surface: cairo_surface);
113}
114
115static const struct wl_buffer_listener buffer_listener = {
116 gdk_wayland_cairo_context_buffer_release
117};
118
119static cairo_surface_t *
120gdk_wayland_cairo_context_create_surface (GdkWaylandCairoContext *self)
121{
122 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (self)));
123 GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self));
124 cairo_surface_t *cairo_surface;
125 struct wl_buffer *buffer;
126 cairo_region_t *region;
127 int width, height;
128
129 width = gdk_surface_get_width (surface);
130 height = gdk_surface_get_height (surface);
131 cairo_surface = _gdk_wayland_display_create_shm_surface (display: display_wayland,
132 width, height,
133 scale: gdk_surface_get_scale_factor (surface));
134 buffer = _gdk_wayland_shm_surface_get_wl_buffer (surface: cairo_surface);
135 wl_buffer_add_listener (wl_buffer: buffer, listener: &buffer_listener, data: cairo_surface);
136 gdk_wayland_cairo_context_add_surface (self, surface: cairo_surface);
137
138 region = cairo_region_create_rectangle (rectangle: &(cairo_rectangle_int_t) { 0, 0, width, height });
139 gdk_wayland_cairo_context_surface_add_region (surface: cairo_surface, region);
140 cairo_region_destroy (region);
141
142 return cairo_surface;
143}
144
145static void
146gdk_wayland_cairo_context_begin_frame (GdkDrawContext *draw_context,
147 gboolean prefers_high_depth,
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 (surface: self->paint_surface);
161 if (surface_region)
162 cairo_region_union (dst: region, other: surface_region);
163
164 for (l = self->surfaces; l; l = l->next)
165 {
166 gdk_wayland_cairo_context_surface_add_region (surface: l->data, region);
167 }
168
169 /* clear the repaint area */
170 cr = cairo_create (target: self->paint_surface);
171 cairo_set_operator (cr, op: CAIRO_OPERATOR_CLEAR);
172 gdk_cairo_region (cr, region);
173 cairo_fill (cr);
174 cairo_destroy (cr);
175}
176
177static void
178gdk_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 (context: draw_context);
183
184 gdk_wayland_surface_sync (surface);
185 gdk_wayland_surface_attach_image (surface, cairo_surface: self->paint_surface, damage: painted);
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 (surface: self->paint_surface);
193 self->paint_surface = NULL;
194}
195
196static void
197gdk_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, surface: self->surfaces->data);
202}
203
204static void
205gdk_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
212static cairo_t *
213gdk_wayland_cairo_context_cairo_create (GdkCairoContext *context)
214{
215 GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (context);
216
217 return cairo_create (target: self->paint_surface);
218}
219
220static void
221gdk_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
232static void
233gdk_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
248static void
249gdk_wayland_cairo_context_init (GdkWaylandCairoContext *self)
250{
251}
252
253

source code of gtk/gdk/wayland/gdkcairocontext-wayland.c