diff options
author | Emmanuele Bassi <ebassi@gnome.org> | 2016-05-30 09:44:30 +0100 |
---|---|---|
committer | Emmanuele Bassi <ebassi@gnome.org> | 2016-06-08 14:40:08 +0100 |
commit | 7510615893aa5167f3130d89a4d4423b7f218c27 (patch) | |
tree | 3f375bddfce35d41ab4be0ef7d27457e226b501c | |
parent | 45d33f2e3ab0e5cf27f3ad7c858ed0a7e28a7461 (diff) | |
download | gtk+-7510615893aa5167f3130d89a4d4423b7f218c27.tar.gz |
Add GdkDrawingContext
Instead of giving out Cairo contexts, GdkWindow should provide a
"drawing context", which can then create Cairo contexts on demand; this
allows us to future proof the API for when we're going to use a
different rendering pipeline, like OpenGL.
https://bugzilla.gnome.org/show_bug.cgi?id=766675
-rw-r--r-- | gdk/Makefile.am | 3 | ||||
-rw-r--r-- | gdk/gdk.h | 1 | ||||
-rw-r--r-- | gdk/gdkdrawingcontext.c | 275 | ||||
-rw-r--r-- | gdk/gdkdrawingcontext.h | 53 | ||||
-rw-r--r-- | gdk/gdkdrawingcontextprivate.h | 29 | ||||
-rw-r--r-- | gdk/gdkinternals.h | 4 | ||||
-rw-r--r-- | gdk/gdkwindow.c | 91 | ||||
-rw-r--r-- | gdk/gdkwindow.h | 7 | ||||
-rw-r--r-- | gdk/gdkwindowimpl.h | 5 | ||||
-rw-r--r-- | gtk/gtkwidget.c | 6 |
10 files changed, 453 insertions, 21 deletions
diff --git a/gdk/Makefile.am b/gdk/Makefile.am index 16230cb911..91da7bdb3c 100644 --- a/gdk/Makefile.am +++ b/gdk/Makefile.am @@ -75,6 +75,7 @@ gdk_public_h_sources = \ gdkdisplay.h \ gdkdisplaymanager.h \ gdkdnd.h \ + gdkdrawingcontext.h \ gdkevents.h \ gdkframetimings.h \ gdkglcontext.h \ @@ -116,6 +117,7 @@ gdk_private_headers = \ gdkdisplaymanagerprivate.h \ gdkdisplayprivate.h \ gdkdndprivate.h \ + gdkdrawingcontextprivate.h \ gdkframeclockidle.h \ gdkframeclockprivate.h \ gdkglcontextprivate.h \ @@ -146,6 +148,7 @@ gdk_c_sources = \ gdkdisplay.c \ gdkdisplaymanager.c \ gdkdnd.c \ + gdkdrawingcontext.c \ gdkevents.c \ gdkframetimings.c \ gdkgl.c \ @@ -38,6 +38,7 @@ #include <gdk/gdkdisplay.h> #include <gdk/gdkdisplaymanager.h> #include <gdk/gdkdnd.h> +#include <gdk/gdkdrawingcontext.h> #include <gdk/gdkenumtypes.h> #include <gdk/gdkevents.h> #include <gdk/gdkframeclock.h> diff --git a/gdk/gdkdrawingcontext.c b/gdk/gdkdrawingcontext.c new file mode 100644 index 0000000000..caffa0cf48 --- /dev/null +++ b/gdk/gdkdrawingcontext.c @@ -0,0 +1,275 @@ +/* GDK - The GIMP Drawing Kit + * Copyright 2016 Endless + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * SECTION:gdkdrawingcontext + * @Title: GdkDrawingContext + * @Short_description: Drawing context for GDK windows + * + * #GdkDrawingContext is an object that represents the current drawing + * state of a #GdkWindow. + * + * It's possible to use a #GdkDrawingContext to draw on a #GdkWindow + * via rendering API like Cairo or OpenGL. + * + * A #GdkDrawingContext can only be created by calling gdk_window_begin_draw_frame() + * and will be valid until a call to gdk_window_end_draw_frame(). + * + * #GdkDrawingContext is available since GDK 3.22 + */ + +#include "config.h" + +#include <cairo-gobject.h> + +#include "gdkdrawingcontextprivate.h" + +#include "gdkrectangle.h" +#include "gdkinternals.h" +#include "gdkintl.h" +#include "gdkframeclockidle.h" +#include "gdkwindowimpl.h" +#include "gdkglcontextprivate.h" +#include "gdk-private.h" + +G_DEFINE_TYPE (GdkDrawingContext, gdk_drawing_context, G_TYPE_OBJECT) + +enum { + PROP_0, + + PROP_WINDOW, + PROP_CLIP, + + N_PROPS +}; + +static GParamSpec *obj_property[N_PROPS]; + +static void +gdk_drawing_context_dispose (GObject *gobject) +{ + GdkDrawingContext *self = GDK_DRAWING_CONTEXT (gobject); + + g_clear_object (&self->window); + g_clear_pointer (&self->clip, cairo_region_destroy); + g_clear_pointer (&self->cr, cairo_destroy); + + G_OBJECT_CLASS (gdk_drawing_context_parent_class)->dispose (gobject); +} + +static void +gdk_drawing_context_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdkDrawingContext *self = GDK_DRAWING_CONTEXT (gobject); + + switch (prop_id) + { + case PROP_WINDOW: + self->window = g_value_dup_object (value); + break; + + case PROP_CLIP: + self->clip = g_value_dup_boxed (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +gdk_drawing_context_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdkDrawingContext *self = GDK_DRAWING_CONTEXT (gobject); + + switch (prop_id) + { + case PROP_WINDOW: + g_value_set_object (value, self->window); + break; + + case PROP_CLIP: + g_value_set_boxed (value, self->clip); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +gdk_drawing_context_constructed (GObject *gobject) +{ + GdkDrawingContext *self = GDK_DRAWING_CONTEXT (gobject); + + if (self->window == NULL) + { + g_critical ("The drawing context of type %s does not have a window " + "associated to it. Drawing contexts can only be created " + "using gdk_window_begin_draw_frame().", + G_OBJECT_TYPE_NAME (gobject)); + } + + G_OBJECT_CLASS (gdk_drawing_context_parent_class)->constructed (gobject); +} + +static void +gdk_drawing_context_class_init (GdkDrawingContextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->constructed = gdk_drawing_context_constructed; + gobject_class->set_property = gdk_drawing_context_set_property; + gobject_class->get_property = gdk_drawing_context_get_property; + gobject_class->dispose = gdk_drawing_context_dispose; + + /** + * GdkDrawingContext:window: + * + * The #GdkWindow that created the drawing context. + * + * Since: 3.22 + */ + obj_property[PROP_WINDOW] = + g_param_spec_object ("window", "Window", "The window that created the context", + GDK_TYPE_WINDOW, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + /** + * GdkDrawingContext:clip: + * + * The clip region applied to the drawing context. + * + * Since: 3.22 + */ + obj_property[PROP_CLIP] = + g_param_spec_boxed ("clip", "Clip", "The clip region of the context", + CAIRO_GOBJECT_TYPE_REGION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, N_PROPS, obj_property); +} + +static void +gdk_drawing_context_init (GdkDrawingContext *self) +{ +} + +/** + * gdk_drawing_context_get_cairo_context: + * @context: + * + * Retrieves a Cairo context to be used to draw on the #GdkWindow + * that created the #GdkDrawingContext. + * + * The returned context is guaranteed to be valid as long as the + * #GdkDrawingContext is valid, that is between a call to + * gdk_window_begin_draw_frame() and gdk_window_end_draw_frame(). + * + * Returns: (transfer none): a Cairo context to be used to draw + * the contents of the #GdkWindow. The context is owned by the + * #GdkDrawingContext and should not be destroyed + * + * Since: 3.22 + */ +cairo_t * +gdk_drawing_context_get_cairo_context (GdkDrawingContext *context) +{ + g_return_val_if_fail (GDK_IS_DRAWING_CONTEXT (context), NULL); + g_return_val_if_fail (GDK_IS_WINDOW (context->window), NULL); + + if (context->cr == NULL) + { + context->cr = gdk_cairo_create (context->window); + gdk_cairo_region (context->cr, context->clip); + cairo_clip (context->cr); + } + + return context->cr; +} + +/** + * gdk_drawing_context_get_window: + * @context: a #GdkDrawingContext + * + * Retrieves the window that created the drawing @context. + * + * Returns: (transfer none): a #GdkWindow + * + * Since: 3.22 + */ +GdkWindow * +gdk_drawing_context_get_window (GdkDrawingContext *context) +{ + g_return_val_if_fail (GDK_IS_DRAWING_CONTEXT (context), NULL); + + return context->window; +} + +/** + * gdk_drawing_context_get_clip: + * @context: a #GdkDrawingContext + * + * Retrieves a copy of the clip region used when creating the @context. + * + * Returns: (transfer full) (nullable): a Cairo region + * + * Since: 3.22 + */ +cairo_region_t * +gdk_drawing_context_get_clip (GdkDrawingContext *context) +{ + g_return_val_if_fail (GDK_IS_DRAWING_CONTEXT (context), NULL); + + if (context->clip == NULL) + return NULL; + + return cairo_region_copy (context->clip); +} + +/** + * gdk_drawing_context_is_valid: + * @context: a #GdkDrawingContext + * + * Checks whether the given #GdkDrawingContext is valid. + * + * Returns: %TRUE if the context is valid + * + * Since: 3.22 + */ +gboolean +gdk_drawing_context_is_valid (GdkDrawingContext *context) +{ + g_return_val_if_fail (GDK_IS_DRAWING_CONTEXT (context), FALSE); + + if (context->window == NULL) + return FALSE; + + if (gdk_window_get_drawing_context (context->window) != context) + return FALSE; + + return TRUE; +} diff --git a/gdk/gdkdrawingcontext.h b/gdk/gdkdrawingcontext.h new file mode 100644 index 0000000000..6202648492 --- /dev/null +++ b/gdk/gdkdrawingcontext.h @@ -0,0 +1,53 @@ +/* GDK - The GIMP Drawing Kit + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GDK_DRAWING_CONTEXT_H__ +#define __GDK_DRAWING_CONTEXT_H__ + +#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only <gdk/gdk.h> can be included directly." +#endif + +#include <gdk/gdkversionmacros.h> +#include <gdk/gdktypes.h> + +G_BEGIN_DECLS + +#define GDK_TYPE_DRAWING_CONTEXT (gdk_drawing_context_get_type ()) + +#define GDK_DRAWING_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_DRAWING_CONTEXT, GdkDrawingContext)) +#define GDK_IS_DRAWING_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_DRAWING_CONTEXT)) + +typedef struct _GdkDrawingContext GdkDrawingContext; +typedef struct _GdkDrawingContextClass GdkDrawingContextClass; + +GDK_AVAILABLE_IN_3_22 +GType gdk_drawing_context_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_3_22 +GdkWindow * gdk_drawing_context_get_window (GdkDrawingContext *context); +GDK_AVAILABLE_IN_3_22 +cairo_region_t *gdk_drawing_context_get_clip (GdkDrawingContext *context); + +GDK_AVAILABLE_IN_3_22 +gboolean gdk_drawing_context_is_valid (GdkDrawingContext *context); + +GDK_AVAILABLE_IN_3_22 +cairo_t * gdk_drawing_context_get_cairo_context (GdkDrawingContext *context); + +G_END_DECLS + +#endif /* __GDK_DRAWING_CONTEXT_H__ */ diff --git a/gdk/gdkdrawingcontextprivate.h b/gdk/gdkdrawingcontextprivate.h new file mode 100644 index 0000000000..a41cf02a04 --- /dev/null +++ b/gdk/gdkdrawingcontextprivate.h @@ -0,0 +1,29 @@ +#ifndef __GDK_DRAWING_CONTEXT_PRIVATE_H__ +#define __GDK_DRAWING_CONTEXT_PRIVATE_H__ + +#include "gdkdrawingcontext.h" + +G_BEGIN_DECLS + +#define GDK_DRAWING_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DRAWING_CONTEXT, GdkDrawingContextClass)) +#define GDK_IS_DRAWING_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DRAWING_CONTEXT)) +#define GDK_DRAWING_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DRAWING_CONTEXT, GdkDrawingContextClass)) + +struct _GdkDrawingContext +{ + GObject parent_instance; + + GdkWindow *window; + + cairo_region_t *clip; + cairo_t *cr; +}; + +struct _GdkDrawingContextClass +{ + GObjectClass parent_instance; +}; + +G_END_DECLS + +#endif /* __GDK_DRAWING_CONTEXT_PRIVATE_H__ */ diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 03555d1fd2..1e7bcb0ad5 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -379,6 +379,8 @@ struct _GdkWindow GdkFrameClock *frame_clock; /* NULL to use from parent or default */ GdkWindowInvalidateHandlerFunc invalidate_handler; + + GdkDrawingContext *drawing_context; }; #define GDK_WINDOW_TYPE(d) ((((GdkWindow *)(d)))->window_type) @@ -468,6 +470,8 @@ void gdk_window_get_unscaled_size (GdkWindow *window, int *unscaled_width, int *unscaled_height); +GdkDrawingContext *gdk_window_get_drawing_context (GdkWindow *window); + void _gdk_window_process_updates_recurse (GdkWindow *window, cairo_region_t *expose_region); diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index b6137f4037..4724f3fa9f 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -40,6 +40,7 @@ #include "gdkframeclockidle.h" #include "gdkwindowimpl.h" #include "gdkglcontextprivate.h" +#include "gdkdrawingcontextprivate.h" #include "gdk-private.h" #include <math.h> @@ -3143,7 +3144,7 @@ gdk_cairo_get_window (cairo_t *cr) * @region: a Cairo region * * Indicates that you are beginning the process of redrawing @region - * on @window, and provides you with a Cairo context for drawing. + * on @window, and provides you with a #GdkDrawingContext. * * If @window is a top level #GdkWindow, backed by a native window * implementation, a backing store (offscreen buffer) large enough to @@ -3170,40 +3171,60 @@ gdk_cairo_get_window (cairo_t *cr) * code in GTK does not need to call gdk_window_begin_draw_frame() * explicitly. * - * Returns: (transfer none): a Cairo context that should be used to - * draw the contents of the window; the returned context is owned - * by GDK and should not be destroyed directly + * Returns: (transfer none): a #GdkDrawingContext context that should be + * used to draw the contents of the window; the returned context is owned + * by GDK. * * Since: 3.22 */ -cairo_t * +GdkDrawingContext * gdk_window_begin_draw_frame (GdkWindow *window, const cairo_region_t *region) { - cairo_t *retval; + GdkDrawingContext *context; + GdkWindowImplClass *impl_class; g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); + if (window->drawing_context != NULL) + { + g_critical ("The window %p already has a drawing context. You cannot " + "call gdk_window_begin_draw_frame() without calling " + "gdk_window_end_draw_frame() first.", window); + return NULL; + } + if (gdk_window_has_native (window) && gdk_window_is_toplevel (window)) gdk_window_begin_paint_internal (window, region); - retval = gdk_cairo_create (window); + impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl); + if (impl_class->create_draw_context != NULL) + { + context = impl_class->create_draw_context (window, region); + } + else + { + context = g_object_new (GDK_TYPE_DRAWING_CONTEXT, + "window", window, + "clip", region, + NULL); + } - gdk_cairo_region (retval, region); - cairo_clip (retval); + /* Do not take a reference, to avoid creating cycles */ + window->drawing_context = context; - return retval; + return context; } /** * gdk_window_end_draw_frame: * @window: a #GdkWindow - * @cr: the Cairo context created by gdk_window_begin_draw_frame() + * @context: the #GdkDrawingContext created by gdk_window_begin_draw_frame() * * Indicates that the drawing of the contents of @window started with * gdk_window_begin_frame() has been completed. * - * This function will take care of destroying the Cairo context. + * This function will take care of destroying the #GdkDrawingContext. * * It is an error to call this function without a matching * gdk_window_begin_frame() first. @@ -3211,14 +3232,52 @@ gdk_window_begin_draw_frame (GdkWindow *window, * Since: 3.22 */ void -gdk_window_end_draw_frame (GdkWindow *window, - cairo_t *cr) +gdk_window_end_draw_frame (GdkWindow *window, + GdkDrawingContext *context) { + GdkWindowImplClass *impl_class; + + g_return_if_fail (GDK_IS_WINDOW (window)); + g_return_if_fail (GDK_IS_DRAWING_CONTEXT (context)); + + if (window->drawing_context == NULL) + { + g_critical ("The window %p has no drawing context. You must call " + "gdk_window_begin_draw_frame() before calling " + "gdk_window_end_draw_frame().", window); + return; + } + if (gdk_window_has_native (window) && gdk_window_is_toplevel (window)) gdk_window_end_paint_internal (window); - gdk_cairo_set_window (cr, NULL); - cairo_destroy (cr); + impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl); + if (impl_class->destroy_draw_context != NULL) + impl_class->destroy_draw_context (window, context); + else + g_object_unref (context); + + window->drawing_context = NULL; +} + +/*< private > + * gdk_window_get_drawing_context: + * @window: a #GdkWindow + * + * Retrieves the #GdkDrawingContext associated to @window by + * gdk_window_begin_draw_frame(). + * + * Returns: (transfer none) (nullable): a #GdkDrawingContext, if any is set + */ +GdkDrawingContext * +gdk_window_get_drawing_context (GdkWindow *window) +{ + g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); + + if (GDK_WINDOW_DESTROYED (window)) + return NULL; + + return window->drawing_context; } /** diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index 8f38cf36d7..463cd4fffd 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -31,6 +31,7 @@ #include <gdk/gdkversionmacros.h> #include <gdk/gdktypes.h> +#include <gdk/gdkdrawingcontext.h> #include <gdk/gdkevents.h> #include <gdk/gdkframeclock.h> @@ -706,11 +707,11 @@ GDK_AVAILABLE_IN_ALL void gdk_window_end_paint (GdkWindow *window); GDK_AVAILABLE_IN_3_22 -cairo_t * gdk_window_begin_draw_frame (GdkWindow *window, - const cairo_region_t *region); +GdkDrawingContext *gdk_window_begin_draw_frame (GdkWindow *window, + const cairo_region_t *region); GDK_AVAILABLE_IN_3_22 void gdk_window_end_draw_frame (GdkWindow *window, - cairo_t *cr); + GdkDrawingContext *context); GDK_DEPRECATED_IN_3_14 void gdk_window_flush (GdkWindow *window); diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h index f0743434fc..4ccdc67fea 100644 --- a/gdk/gdkwindowimpl.h +++ b/gdk/gdkwindowimpl.h @@ -303,6 +303,11 @@ struct _GdkWindowImplClass GError **error); void (*invalidate_for_new_frame)(GdkWindow *window, cairo_region_t *update_area); + + GdkDrawingContext *(* create_draw_context) (GdkWindow *window, + const cairo_region_t *region); + void (* destroy_draw_context) (GdkWindow *window, + GdkDrawingContext *context); }; /* Interface Functions */ diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 2751a4ddb3..158e656852 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -17441,6 +17441,7 @@ gtk_widget_render (GtkWidget *widget, const cairo_region_t *region) { GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); + GdkDrawingContext *context; gboolean do_clip; cairo_t *cr; int x, y; @@ -17452,12 +17453,13 @@ gtk_widget_render (GtkWidget *widget, return; } - cr = gdk_window_begin_draw_frame (window, region); + context = gdk_window_begin_draw_frame (window, region); + cr = gdk_drawing_context_get_cairo_context (context); do_clip = _gtk_widget_get_translation_to_window (widget, window, &x, &y); cairo_translate (cr, -x, -y); gtk_widget_draw_internal (widget, cr, do_clip); - gdk_window_end_draw_frame (window, cr); + gdk_window_end_draw_frame (window, context); } |