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 | |
27 | static const cairo_user_data_key_t gdk_wayland_cairo_context_key; |
28 | static const cairo_user_data_key_t gdk_wayland_cairo_region_key; |
29 | |
30 | G_DEFINE_TYPE (GdkWaylandCairoContext, gdk_wayland_cairo_context, GDK_TYPE_CAIRO_CONTEXT) |
31 | |
32 | static void |
33 | gdk_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 | |
53 | static void |
54 | gdk_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 | |
59 | static const cairo_region_t * |
60 | gdk_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 | |
65 | static GdkWaylandCairoContext * |
66 | gdk_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 | |
71 | static void |
72 | gdk_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 | |
81 | static void |
82 | gdk_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 | |
91 | static void |
92 | gdk_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 | |
115 | static const struct wl_buffer_listener buffer_listener = { |
116 | gdk_wayland_cairo_context_buffer_release |
117 | }; |
118 | |
119 | static cairo_surface_t * |
120 | gdk_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 | |
145 | static void |
146 | gdk_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 | |
177 | static void |
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 (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 | |
196 | static void |
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, surface: self->surfaces->data); |
202 | } |
203 | |
204 | static void |
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 * |
213 | gdk_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 | |
220 | static void |
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 |
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 |
249 | gdk_wayland_cairo_context_init (GdkWaylandCairoContext *self) |
250 | { |
251 | } |
252 | |
253 | |