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
49G_DEFINE_TYPE (GdkDrawingContext, gdk_drawing_context, G_TYPE_OBJECT)
50
51enum {
52 PROP_0,
53
54 PROP_WINDOW,
55 PROP_CLIP,
56
57 N_PROPS
58};
59
60static GParamSpec *obj_property[N_PROPS];
61
62static void
63gdk_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
80static void
81gdk_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
103static void
104gdk_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
126static void
127gdk_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
142static void
143gdk_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
182static void
183gdk_drawing_context_init (GdkDrawingContext *self)
184{
185}
186
187static const cairo_user_data_key_t draw_context_key;
188
189void
190gdk_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 */
207GdkDrawingContext *
208gdk_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 */
232cairo_t *
233gdk_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 */
270GdkWindow *
271gdk_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 */
288cairo_region_t *
289gdk_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 */
309gboolean
310gdk_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