1 | /* GDK - The GIMP Drawing Kit |
2 | * Copyright 2016 Endless |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | /** |
19 | * SECTION:gdkdrawingcontext |
20 | * @Title: GdkDrawingContext |
21 | * @Short_description: Drawing context for GDK windows |
22 | * |
23 | * #GdkDrawingContext is an object that represents the current drawing |
24 | * state of a #GdkWindow. |
25 | * |
26 | * It's possible to use a #GdkDrawingContext to draw on a #GdkWindow |
27 | * via rendering API like Cairo or OpenGL. |
28 | * |
29 | * A #GdkDrawingContext can only be created by calling gdk_window_begin_draw_frame() |
30 | * and will be valid until a call to gdk_window_end_draw_frame(). |
31 | * |
32 | * #GdkDrawingContext is available since GDK 3.22 |
33 | */ |
34 | |
35 | #include "config.h" |
36 | |
37 | #include <cairo-gobject.h> |
38 | |
39 | #include "gdkdrawingcontextprivate.h" |
40 | |
41 | #include "gdkrectangle.h" |
42 | #include "gdkinternals.h" |
43 | #include "gdkintl.h" |
44 | #include "gdkframeclockidle.h" |
45 | #include "gdkwindowimpl.h" |
46 | #include "gdkglcontextprivate.h" |
47 | #include "gdk-private.h" |
48 | |
49 | G_DEFINE_TYPE (GdkDrawingContext, gdk_drawing_context, G_TYPE_OBJECT) |
50 | |
51 | enum { |
52 | PROP_0, |
53 | |
54 | PROP_WINDOW, |
55 | PROP_CLIP, |
56 | |
57 | N_PROPS |
58 | }; |
59 | |
60 | static GParamSpec *obj_property[N_PROPS]; |
61 | |
62 | static void |
63 | gdk_drawing_context_dispose (GObject *gobject) |
64 | { |
65 | GdkDrawingContext *self = GDK_DRAWING_CONTEXT (gobject); |
66 | |
67 | /* Unset the drawing context, in case somebody is holding |
68 | * onto the Cairo context |
69 | */ |
70 | if (self->cr != NULL) |
71 | gdk_cairo_set_drawing_context (self->cr, NULL); |
72 | |
73 | g_clear_object (&self->window); |
74 | g_clear_pointer (&self->clip, cairo_region_destroy); |
75 | g_clear_pointer (&self->cr, cairo_destroy); |
76 | |
77 | G_OBJECT_CLASS (gdk_drawing_context_parent_class)->dispose (gobject); |
78 | } |
79 | |
80 | static void |
81 | gdk_drawing_context_set_property (GObject *gobject, |
82 | guint prop_id, |
83 | const GValue *value, |
84 | GParamSpec *pspec) |
85 | { |
86 | GdkDrawingContext *self = GDK_DRAWING_CONTEXT (gobject); |
87 | |
88 | switch (prop_id) |
89 | { |
90 | case PROP_WINDOW: |
91 | self->window = g_value_dup_object (value); |
92 | break; |
93 | |
94 | case PROP_CLIP: |
95 | self->clip = g_value_dup_boxed (value); |
96 | break; |
97 | |
98 | default: |
99 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
100 | } |
101 | } |
102 | |
103 | static void |
104 | gdk_drawing_context_get_property (GObject *gobject, |
105 | guint prop_id, |
106 | GValue *value, |
107 | GParamSpec *pspec) |
108 | { |
109 | GdkDrawingContext *self = GDK_DRAWING_CONTEXT (gobject); |
110 | |
111 | switch (prop_id) |
112 | { |
113 | case PROP_WINDOW: |
114 | g_value_set_object (value, self->window); |
115 | break; |
116 | |
117 | case PROP_CLIP: |
118 | g_value_set_boxed (value, self->clip); |
119 | break; |
120 | |
121 | default: |
122 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
123 | } |
124 | } |
125 | |
126 | static void |
127 | gdk_drawing_context_constructed (GObject *gobject) |
128 | { |
129 | GdkDrawingContext *self = GDK_DRAWING_CONTEXT (gobject); |
130 | |
131 | if (self->window == NULL) |
132 | { |
133 | g_critical ("The drawing context of type %s does not have a window " |
134 | "associated to it. Drawing contexts can only be created " |
135 | "using gdk_window_begin_draw_frame()." , |
136 | G_OBJECT_TYPE_NAME (gobject)); |
137 | } |
138 | |
139 | G_OBJECT_CLASS (gdk_drawing_context_parent_class)->constructed (gobject); |
140 | } |
141 | |
142 | static void |
143 | gdk_drawing_context_class_init (GdkDrawingContextClass *klass) |
144 | { |
145 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
146 | |
147 | gobject_class->constructed = gdk_drawing_context_constructed; |
148 | gobject_class->set_property = gdk_drawing_context_set_property; |
149 | gobject_class->get_property = gdk_drawing_context_get_property; |
150 | gobject_class->dispose = gdk_drawing_context_dispose; |
151 | |
152 | /** |
153 | * GdkDrawingContext:window: |
154 | * |
155 | * The #GdkWindow that created the drawing context. |
156 | * |
157 | * Since: 3.22 |
158 | */ |
159 | obj_property[PROP_WINDOW] = |
160 | g_param_spec_object ("window" , "Window" , "The window that created the context" , |
161 | GDK_TYPE_WINDOW, |
162 | G_PARAM_CONSTRUCT_ONLY | |
163 | G_PARAM_READWRITE | |
164 | G_PARAM_STATIC_STRINGS); |
165 | /** |
166 | * GdkDrawingContext:clip: |
167 | * |
168 | * The clip region applied to the drawing context. |
169 | * |
170 | * Since: 3.22 |
171 | */ |
172 | obj_property[PROP_CLIP] = |
173 | g_param_spec_boxed ("clip" , "Clip" , "The clip region of the context" , |
174 | CAIRO_GOBJECT_TYPE_REGION, |
175 | G_PARAM_CONSTRUCT_ONLY | |
176 | G_PARAM_READWRITE | |
177 | G_PARAM_STATIC_STRINGS); |
178 | |
179 | g_object_class_install_properties (gobject_class, N_PROPS, obj_property); |
180 | } |
181 | |
182 | static void |
183 | gdk_drawing_context_init (GdkDrawingContext *self) |
184 | { |
185 | } |
186 | |
187 | static const cairo_user_data_key_t draw_context_key; |
188 | |
189 | void |
190 | gdk_cairo_set_drawing_context (cairo_t *cr, |
191 | GdkDrawingContext *context) |
192 | { |
193 | cairo_set_user_data (cr, &draw_context_key, context, NULL); |
194 | } |
195 | |
196 | /** |
197 | * gdk_cairo_get_drawing_context: |
198 | * @cr: a Cairo context |
199 | * |
200 | * Retrieves the #GdkDrawingContext that created the Cairo |
201 | * context @cr. |
202 | * |
203 | * Returns: (transfer none) (nullable): a #GdkDrawingContext, if any is set |
204 | * |
205 | * Since: 3.22 |
206 | */ |
207 | GdkDrawingContext * |
208 | gdk_cairo_get_drawing_context (cairo_t *cr) |
209 | { |
210 | g_return_val_if_fail (cr != NULL, NULL); |
211 | |
212 | return cairo_get_user_data (cr, &draw_context_key); |
213 | } |
214 | |
215 | /** |
216 | * gdk_drawing_context_get_cairo_context: |
217 | * @context: |
218 | * |
219 | * Retrieves a Cairo context to be used to draw on the #GdkWindow |
220 | * that created the #GdkDrawingContext. |
221 | * |
222 | * The returned context is guaranteed to be valid as long as the |
223 | * #GdkDrawingContext is valid, that is between a call to |
224 | * gdk_window_begin_draw_frame() and gdk_window_end_draw_frame(). |
225 | * |
226 | * Returns: (transfer none): a Cairo context to be used to draw |
227 | * the contents of the #GdkWindow. The context is owned by the |
228 | * #GdkDrawingContext and should not be destroyed |
229 | * |
230 | * Since: 3.22 |
231 | */ |
232 | cairo_t * |
233 | gdk_drawing_context_get_cairo_context (GdkDrawingContext *context) |
234 | { |
235 | g_return_val_if_fail (GDK_IS_DRAWING_CONTEXT (context), NULL); |
236 | g_return_val_if_fail (GDK_IS_WINDOW (context->window), NULL); |
237 | |
238 | if (context->cr == NULL) |
239 | { |
240 | cairo_region_t *region; |
241 | cairo_surface_t *surface; |
242 | |
243 | surface = _gdk_window_ref_cairo_surface (context->window); |
244 | context->cr = cairo_create (surface); |
245 | |
246 | gdk_cairo_set_drawing_context (context->cr, context); |
247 | |
248 | region = gdk_window_get_current_paint_region (context->window); |
249 | cairo_region_union (region, context->clip); |
250 | gdk_cairo_region (context->cr, region); |
251 | cairo_clip (context->cr); |
252 | |
253 | cairo_region_destroy (region); |
254 | cairo_surface_destroy (surface); |
255 | } |
256 | |
257 | return context->cr; |
258 | } |
259 | |
260 | /** |
261 | * gdk_drawing_context_get_window: |
262 | * @context: a #GdkDrawingContext |
263 | * |
264 | * Retrieves the window that created the drawing @context. |
265 | * |
266 | * Returns: (transfer none): a #GdkWindow |
267 | * |
268 | * Since: 3.22 |
269 | */ |
270 | GdkWindow * |
271 | gdk_drawing_context_get_window (GdkDrawingContext *context) |
272 | { |
273 | g_return_val_if_fail (GDK_IS_DRAWING_CONTEXT (context), NULL); |
274 | |
275 | return context->window; |
276 | } |
277 | |
278 | /** |
279 | * gdk_drawing_context_get_clip: |
280 | * @context: a #GdkDrawingContext |
281 | * |
282 | * Retrieves a copy of the clip region used when creating the @context. |
283 | * |
284 | * Returns: (transfer full) (nullable): a Cairo region |
285 | * |
286 | * Since: 3.22 |
287 | */ |
288 | cairo_region_t * |
289 | gdk_drawing_context_get_clip (GdkDrawingContext *context) |
290 | { |
291 | g_return_val_if_fail (GDK_IS_DRAWING_CONTEXT (context), NULL); |
292 | |
293 | if (context->clip == NULL) |
294 | return NULL; |
295 | |
296 | return cairo_region_copy (context->clip); |
297 | } |
298 | |
299 | /** |
300 | * gdk_drawing_context_is_valid: |
301 | * @context: a #GdkDrawingContext |
302 | * |
303 | * Checks whether the given #GdkDrawingContext is valid. |
304 | * |
305 | * Returns: %TRUE if the context is valid |
306 | * |
307 | * Since: 3.22 |
308 | */ |
309 | gboolean |
310 | gdk_drawing_context_is_valid (GdkDrawingContext *context) |
311 | { |
312 | g_return_val_if_fail (GDK_IS_DRAWING_CONTEXT (context), FALSE); |
313 | |
314 | if (context->window == NULL) |
315 | return FALSE; |
316 | |
317 | if (gdk_window_get_drawing_context (context->window) != context) |
318 | return FALSE; |
319 | |
320 | return TRUE; |
321 | } |
322 | |