Commit 75106158 authored by Emmanuele Bassi's avatar Emmanuele Bassi

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
parent 45d33f2e
......@@ -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>
......
/* 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;
}
/* 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__ */
#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__ */
......@@ -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);
......
......@@ -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;
}
/**
......
......@@ -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);
......
......@@ -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 */
......
......@@ -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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment