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
gdk_drawing_context_dispose(GObject * gobject)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
gdk_drawing_context_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)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
gdk_drawing_context_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)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
gdk_drawing_context_constructed(GObject * gobject)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
gdk_drawing_context_class_init(GdkDrawingContextClass * klass)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
gdk_drawing_context_init(GdkDrawingContext * self)183 gdk_drawing_context_init (GdkDrawingContext *self)
184 {
185 }
186
187 static const cairo_user_data_key_t draw_context_key;
188
189 void
gdk_cairo_set_drawing_context(cairo_t * cr,GdkDrawingContext * context)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 *
gdk_cairo_get_drawing_context(cairo_t * cr)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 *
gdk_drawing_context_get_cairo_context(GdkDrawingContext * context)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 *
gdk_drawing_context_get_window(GdkDrawingContext * context)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 *
gdk_drawing_context_get_clip(GdkDrawingContext * context)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
gdk_drawing_context_is_valid(GdkDrawingContext * context)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