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