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