[gtk+/wip/ebassi/frame-marker: 13/20] Add frame drawing API to GdkWindow
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/ebassi/frame-marker: 13/20] Add frame drawing API to GdkWindow
- Date: Wed, 8 Jun 2016 15:42:06 +0000 (UTC)
commit f0fa8e780b3c6a9cc145269382aee53220cc17db
Author: Emmanuele Bassi <ebassi gnome org>
Date: Fri May 20 16:55:12 2016 +0100
Add frame drawing API to GdkWindow
Existing code drawing on a GDK window has to handle the direct drawing
and the buffered drawing by itself, by checking the window type and
whether or not the window is backed by a native windowing surface. After
that, the calling code has to create a Cairo context from the window and
keep an association between the context and the window itself.
This is completely unnecessary: GDK can determine whether or not it
should use a backing store to draw on a GdkWindow as well as create a
Cairo context, and keep track of it.
This allows to simplify the calling code, and enforce some of the
drawing behavior we want to guarantee to users.
https://bugzilla.gnome.org/show_bug.cgi?id=766675
docs/reference/gdk/gdk3-sections.txt | 4 +
gdk/gdkcairo.h | 2 +
gdk/gdkwindow.c | 450 ++++++++++++++++++++++------------
gdk/gdkwindow.h | 8 +
4 files changed, 302 insertions(+), 162 deletions(-)
---
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index 0ca8d48..0b57e93 100644
--- a/docs/reference/gdk/gdk3-sections.txt
+++ b/docs/reference/gdk/gdk3-sections.txt
@@ -412,6 +412,9 @@ gdk_window_get_clip_region
gdk_window_begin_paint_rect
gdk_window_begin_paint_region
gdk_window_end_paint
+gdk_window_begin_draw_frame
+gdk_window_end_draw_fram
+gdk_window_should_draw
gdk_window_get_visible_region
GdkWindowInvalidateHandlerFunc
gdk_window_set_invalidate_handler
@@ -622,6 +625,7 @@ gdk_window_create_similar_surface
gdk_window_create_similar_image_surface
gdk_cairo_create
gdk_cairo_get_clip_rectangle
+gdk_cairo_get_window
gdk_cairo_set_source_color
gdk_cairo_set_source_rgba
gdk_cairo_set_source_pixbuf
diff --git a/gdk/gdkcairo.h b/gdk/gdkcairo.h
index 8d62e4f..4d8d223 100644
--- a/gdk/gdkcairo.h
+++ b/gdk/gdkcairo.h
@@ -32,6 +32,8 @@ G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
cairo_t * gdk_cairo_create (GdkWindow *window);
+GDK_AVAILABLE_IN_3_22
+GdkWindow * gdk_cairo_get_window (cairo_t *cr);
GDK_AVAILABLE_IN_ALL
gboolean gdk_cairo_get_clip_rectangle (cairo_t *cr,
GdkRectangle *rect);
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index d82f8ca..b6137f4 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -2841,77 +2841,9 @@ gdk_window_create_gl_context (GdkWindow *window,
error);
}
-/**
- * gdk_window_begin_paint_rect:
- * @window: a #GdkWindow
- * @rectangle: rectangle you intend to draw to
- *
- * A convenience wrapper around gdk_window_begin_paint_region() which
- * creates a rectangular region for you. See
- * gdk_window_begin_paint_region() for details.
- *
- **/
-void
-gdk_window_begin_paint_rect (GdkWindow *window,
- const GdkRectangle *rectangle)
-{
- cairo_region_t *region;
-
- g_return_if_fail (GDK_IS_WINDOW (window));
-
- region = cairo_region_create_rectangle (rectangle);
- gdk_window_begin_paint_region (window, region);
- cairo_region_destroy (region);
-}
-
-/**
- * gdk_window_begin_paint_region:
- * @window: a #GdkWindow
- * @region: region you intend to draw to
- *
- * Indicates that you are beginning the process of redrawing @region.
- * A backing store (offscreen buffer) large enough to contain @region
- * will be created. The backing store will be initialized with the
- * background color or background surface for @window. Then, all
- * drawing operations performed on @window will be diverted to the
- * backing store. When you call gdk_window_end_paint(), the backing
- * store will be copied to @window, making it visible onscreen. Only
- * the part of @window contained in @region will be modified; that is,
- * drawing operations are clipped to @region.
- *
- * The net result of all this is to remove flicker, because the user
- * sees the finished product appear all at once when you call
- * gdk_window_end_paint(). If you draw to @window directly without
- * calling gdk_window_begin_paint_region(), the user may see flicker
- * as individual drawing operations are performed in sequence. The
- * clipping and background-initializing features of
- * gdk_window_begin_paint_region() are conveniences for the
- * programmer, so you can avoid doing that work yourself.
- *
- * When using GTK+, the widget system automatically places calls to
- * gdk_window_begin_paint_region() and gdk_window_end_paint() around
- * emissions of the expose_event signal. That is, if you’re writing an
- * expose event handler, you can assume that the exposed area in
- * #GdkEventExpose has already been cleared to the window background,
- * is already set as the clip region, and already has a backing store.
- * Therefore in most cases, application code need not call
- * gdk_window_begin_paint_region(). (You can disable the automatic
- * calls around expose events on a widget-by-widget basis by calling
- * gtk_widget_set_double_buffered().)
- *
- * If you call this function multiple times before calling the
- * matching gdk_window_end_paint(), the backing stores are pushed onto
- * a stack. gdk_window_end_paint() copies the topmost backing store
- * onscreen, subtracts the topmost region from all other regions in
- * the stack, and pops the stack. All drawing operations affect only
- * the topmost backing store in the stack. One matching call to
- * gdk_window_end_paint() is required for each call to
- * gdk_window_begin_paint_region().
- *
- **/
-void
-gdk_window_begin_paint_region (GdkWindow *window,
- const cairo_region_t *region)
+static void
+gdk_window_begin_paint_internal (GdkWindow *window,
+ const cairo_region_t *region)
{
GdkRectangle clip_box;
GdkWindowImplClass *impl_class;
@@ -2919,16 +2851,14 @@ gdk_window_begin_paint_region (GdkWindow *window,
gboolean needs_surface;
cairo_content_t surface_content;
- g_return_if_fail (GDK_IS_WINDOW (window));
-
if (GDK_WINDOW_DESTROYED (window) ||
!gdk_window_has_impl (window))
return;
if (window->current_paint.surface != NULL)
{
- g_warning ("gdk_window_begin_paint_region called while a paint was "
- "alredy in progress. This is not allowed.");
+ g_warning ("A paint operation on the window is alredy in progress. "
+ "This is not allowed.");
return;
}
@@ -3004,99 +2934,14 @@ gdk_window_begin_paint_region (GdkWindow *window,
gdk_window_clear_backing_region (window);
}
-/**
- * gdk_window_mark_paint_from_clip:
- * @window: a #GdkWindow
- * @cr: a #cairo_t
- *
- * If you call this during a paint (e.g. between gdk_window_begin_paint_region()
- * and gdk_window_end_paint() then GDK will mark the current clip region of the
- * window as being drawn. This is required when mixing GL rendering via
- * gdk_cairo_draw_from_gl() and cairo rendering, as otherwise GDK has no way
- * of knowing when something paints over the GL-drawn regions.
- *
- * This is typically called automatically by GTK+ and you don't need
- * to care about this.
- *
- * Since: 3.16
- **/
-void
-gdk_window_mark_paint_from_clip (GdkWindow *window,
- cairo_t *cr)
-{
- cairo_region_t *clip_region;
- GdkWindow *impl_window = window->impl_window;
-
- if (impl_window->current_paint.surface == NULL ||
- cairo_get_target (cr) != impl_window->current_paint.surface)
- return;
-
- if (cairo_region_is_empty (impl_window->current_paint.flushed_region))
- return;
-
- /* This here seems a bit weird, but basically, we're taking the current
- clip and applying also the flushed region, and the result is that the
- new clip is the intersection of these. This is the area where the newly
- drawn region overlaps a previosly flushed area, which is an area of the
- double buffer surface that need to be blended OVER the back buffer rather
- than SRCed. */
- cairo_save (cr);
- /* We set the identity matrix here so we get and apply regions in native
- window coordinates. */
- cairo_identity_matrix (cr);
- gdk_cairo_region (cr, impl_window->current_paint.flushed_region);
- cairo_clip (cr);
-
- clip_region = gdk_cairo_region_from_clip (cr);
- if (clip_region == NULL)
- {
- /* Failed to represent clip as region, mark all as requiring
- blend */
- cairo_region_union (impl_window->current_paint.need_blend_region,
- impl_window->current_paint.flushed_region);
- cairo_region_destroy (impl_window->current_paint.flushed_region);
- impl_window->current_paint.flushed_region = cairo_region_create ();
- }
- else
- {
- cairo_region_subtract (impl_window->current_paint.flushed_region, clip_region);
- cairo_region_union (impl_window->current_paint.need_blend_region, clip_region);
- }
- cairo_region_destroy (clip_region);
-
- /* Clear the area on the double buffer surface to transparent so we
- can start drawing from scratch the area "above" the flushed
- region */
- cairo_set_source_rgba (cr, 0, 0, 0, 0);
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_paint (cr);
-
- cairo_restore (cr);
-}
-
-/**
- * gdk_window_end_paint:
- * @window: a #GdkWindow
- *
- * Indicates that the backing store created by the most recent call
- * to gdk_window_begin_paint_region() should be copied onscreen and
- * deleted, leaving the next-most-recent backing store or no backing
- * store at all as the active paint region. See
- * gdk_window_begin_paint_region() for full details.
- *
- * It is an error to call this function without a matching
- * gdk_window_begin_paint_region() first.
- **/
-void
-gdk_window_end_paint (GdkWindow *window)
+static void
+gdk_window_end_paint_internal (GdkWindow *window)
{
GdkWindow *composited;
GdkWindowImplClass *impl_class;
GdkRectangle clip_box = { 0, };
cairo_t *cr;
- g_return_if_fail (GDK_IS_WINDOW (window));
-
if (GDK_WINDOW_DESTROYED (window) ||
!gdk_window_has_impl (window))
return;
@@ -3189,6 +3034,285 @@ gdk_window_end_paint (GdkWindow *window)
}
/**
+ * gdk_window_begin_paint_rect:
+ * @window: a #GdkWindow
+ * @rectangle: rectangle you intend to draw to
+ *
+ * A convenience wrapper around gdk_window_begin_paint_region() which
+ * creates a rectangular region for you. See
+ * gdk_window_begin_paint_region() for details.
+ *
+ **/
+void
+gdk_window_begin_paint_rect (GdkWindow *window,
+ const GdkRectangle *rectangle)
+{
+ cairo_region_t *region;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ region = cairo_region_create_rectangle (rectangle);
+ gdk_window_begin_paint_internal (window, region);
+ cairo_region_destroy (region);
+}
+
+/**
+ * gdk_window_begin_paint_region:
+ * @window: a #GdkWindow
+ * @region: region you intend to draw to
+ *
+ * Indicates that you are beginning the process of redrawing @region.
+ * A backing store (offscreen buffer) large enough to contain @region
+ * will be created. The backing store will be initialized with the
+ * background color or background surface for @window. Then, all
+ * drawing operations performed on @window will be diverted to the
+ * backing store. When you call gdk_window_end_paint(), the backing
+ * store will be copied to @window, making it visible onscreen. Only
+ * the part of @window contained in @region will be modified; that is,
+ * drawing operations are clipped to @region.
+ *
+ * The net result of all this is to remove flicker, because the user
+ * sees the finished product appear all at once when you call
+ * gdk_window_end_paint(). If you draw to @window directly without
+ * calling gdk_window_begin_paint_region(), the user may see flicker
+ * as individual drawing operations are performed in sequence. The
+ * clipping and background-initializing features of
+ * gdk_window_begin_paint_region() are conveniences for the
+ * programmer, so you can avoid doing that work yourself.
+ *
+ * When using GTK+, the widget system automatically places calls to
+ * gdk_window_begin_paint_region() and gdk_window_end_paint() around
+ * emissions of the expose_event signal. That is, if you’re writing an
+ * expose event handler, you can assume that the exposed area in
+ * #GdkEventExpose has already been cleared to the window background,
+ * is already set as the clip region, and already has a backing store.
+ * Therefore in most cases, application code need not call
+ * gdk_window_begin_paint_region(). (You can disable the automatic
+ * calls around expose events on a widget-by-widget basis by calling
+ * gtk_widget_set_double_buffered().)
+ *
+ * If you call this function multiple times before calling the
+ * matching gdk_window_end_paint(), the backing stores are pushed onto
+ * a stack. gdk_window_end_paint() copies the topmost backing store
+ * onscreen, subtracts the topmost region from all other regions in
+ * the stack, and pops the stack. All drawing operations affect only
+ * the topmost backing store in the stack. One matching call to
+ * gdk_window_end_paint() is required for each call to
+ * gdk_window_begin_paint_region().
+ *
+ **/
+void
+gdk_window_begin_paint_region (GdkWindow *window,
+ const cairo_region_t *region)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ gdk_window_begin_paint_internal (window, region);
+}
+
+static const cairo_user_data_key_t draw_context_window_key;
+
+static void
+gdk_cairo_set_window (cairo_t *cr,
+ GdkWindow *window)
+{
+ cairo_set_user_data (cr, &draw_context_window_key, window, NULL);
+}
+
+/**
+ * gdk_cairo_get_window:
+ * @cr: a Cairo context created by gdk_window_begin_draw_frame()
+ *
+ * Retrieves the #GdkWindow that created the Cairo context @cr.
+ *
+ * Returns: (nullable) (transfer none): a #GdkWindow
+ *
+ * Since: 3.22
+ */
+GdkWindow *
+gdk_cairo_get_window (cairo_t *cr)
+{
+ g_return_val_if_fail (cr != NULL, NULL);
+
+ return cairo_get_user_data (cr, &draw_context_window_key);
+}
+
+/**
+ * gdk_window_begin_draw_frame:
+ * @window: a #GdkWindow
+ * @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.
+ *
+ * If @window is a top level #GdkWindow, backed by a native window
+ * implementation, a backing store (offscreen buffer) large enough to
+ * contain @region will be created. The backing store will be initialized
+ * with the background color or background surface for @window. Then, all
+ * drawing operations performed on @window will be diverted to the
+ * backing store. When you call gdk_window_end_frame(), the contents of
+ * the backing store will be copied to @window, making it visible
+ * on screen. Only the part of @window contained in @region will be
+ * modified; that is, drawing operations are clipped to @region.
+ *
+ * The net result of all this is to remove flicker, because the user
+ * sees the finished product appear all at once when you call
+ * gdk_window_end_draw_frame(). If you draw to @window directly without
+ * calling gdk_window_begin_draw_frame(), the user may see flicker
+ * as individual drawing operations are performed in sequence.
+ *
+ * When using GTK+, the widget system automatically places calls to
+ * gdk_window_begin_draw_frame() and gdk_window_end_draw_frame() around
+ * emissions of the `GtkWidget::draw` signal. That is, if you’re
+ * drawing the contents of the widget yourself, you can assume that the
+ * widget has a cleared background, is already set as the clip region,
+ * and already has a backing store. Therefore in most cases, application
+ * 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
+ *
+ * Since: 3.22
+ */
+cairo_t *
+gdk_window_begin_draw_frame (GdkWindow *window,
+ const cairo_region_t *region)
+{
+ cairo_t *retval;
+
+ g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+
+ if (gdk_window_has_native (window) && gdk_window_is_toplevel (window))
+ gdk_window_begin_paint_internal (window, region);
+
+ retval = gdk_cairo_create (window);
+
+ gdk_cairo_region (retval, region);
+ cairo_clip (retval);
+
+ return retval;
+}
+
+/**
+ * gdk_window_end_draw_frame:
+ * @window: a #GdkWindow
+ * @cr: the Cairo context 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.
+ *
+ * It is an error to call this function without a matching
+ * gdk_window_begin_frame() first.
+ *
+ * Since: 3.22
+ */
+void
+gdk_window_end_draw_frame (GdkWindow *window,
+ cairo_t *cr)
+{
+ 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);
+}
+
+/**
+ * gdk_window_mark_paint_from_clip:
+ * @window: a #GdkWindow
+ * @cr: a #cairo_t
+ *
+ * If you call this during a paint (e.g. between gdk_window_begin_paint_region()
+ * and gdk_window_end_paint() then GDK will mark the current clip region of the
+ * window as being drawn. This is required when mixing GL rendering via
+ * gdk_cairo_draw_from_gl() and cairo rendering, as otherwise GDK has no way
+ * of knowing when something paints over the GL-drawn regions.
+ *
+ * This is typically called automatically by GTK+ and you don't need
+ * to care about this.
+ *
+ * Since: 3.16
+ **/
+void
+gdk_window_mark_paint_from_clip (GdkWindow *window,
+ cairo_t *cr)
+{
+ cairo_region_t *clip_region;
+ GdkWindow *impl_window = window->impl_window;
+
+ if (impl_window->current_paint.surface == NULL ||
+ cairo_get_target (cr) != impl_window->current_paint.surface)
+ return;
+
+ if (cairo_region_is_empty (impl_window->current_paint.flushed_region))
+ return;
+
+ /* This here seems a bit weird, but basically, we're taking the current
+ clip and applying also the flushed region, and the result is that the
+ new clip is the intersection of these. This is the area where the newly
+ drawn region overlaps a previosly flushed area, which is an area of the
+ double buffer surface that need to be blended OVER the back buffer rather
+ than SRCed. */
+ cairo_save (cr);
+ /* We set the identity matrix here so we get and apply regions in native
+ window coordinates. */
+ cairo_identity_matrix (cr);
+ gdk_cairo_region (cr, impl_window->current_paint.flushed_region);
+ cairo_clip (cr);
+
+ clip_region = gdk_cairo_region_from_clip (cr);
+ if (clip_region == NULL)
+ {
+ /* Failed to represent clip as region, mark all as requiring
+ blend */
+ cairo_region_union (impl_window->current_paint.need_blend_region,
+ impl_window->current_paint.flushed_region);
+ cairo_region_destroy (impl_window->current_paint.flushed_region);
+ impl_window->current_paint.flushed_region = cairo_region_create ();
+ }
+ else
+ {
+ cairo_region_subtract (impl_window->current_paint.flushed_region, clip_region);
+ cairo_region_union (impl_window->current_paint.need_blend_region, clip_region);
+ }
+ cairo_region_destroy (clip_region);
+
+ /* Clear the area on the double buffer surface to transparent so we
+ can start drawing from scratch the area "above" the flushed
+ region */
+ cairo_set_source_rgba (cr, 0, 0, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+
+ cairo_restore (cr);
+}
+
+/**
+ * gdk_window_end_paint:
+ * @window: a #GdkWindow
+ *
+ * Indicates that the backing store created by the most recent call
+ * to gdk_window_begin_paint_region() should be copied onscreen and
+ * deleted, leaving the next-most-recent backing store or no backing
+ * store at all as the active paint region. See
+ * gdk_window_begin_paint_region() for full details.
+ *
+ * It is an error to call this function without a matching
+ * gdk_window_begin_paint_region() first.
+ **/
+void
+gdk_window_end_paint (GdkWindow *window)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ gdk_window_end_paint_internal (window);
+}
+
+/**
* gdk_window_flush:
* @window: a #GdkWindow
*
@@ -3358,6 +3482,8 @@ gdk_cairo_create (GdkWindow *window)
cr = cairo_create (surface);
+ gdk_cairo_set_window (cr, window);
+
if (window->impl_window->current_paint.region != NULL)
{
region = cairo_region_copy (window->impl_window->current_paint.region);
diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
index 2fc934f..8f38cf3 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -704,6 +704,14 @@ void gdk_window_begin_paint_region (GdkWindow *window,
const cairo_region_t *region);
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);
+GDK_AVAILABLE_IN_3_22
+void gdk_window_end_draw_frame (GdkWindow *window,
+ cairo_t *cr);
+
GDK_DEPRECATED_IN_3_14
void gdk_window_flush (GdkWindow *window);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]