summaryrefslogtreecommitdiff
path: root/gtk/gtkviewport.c
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2013-04-21 01:54:05 +0200
committerAlexander Larsson <alexl@redhat.com>2013-05-07 16:34:05 +0200
commit2df27ce7f801da92e64ed966d96c47e3f5d190e7 (patch)
tree410d8fd53e8aaaa7a97d7e3caed47abb2eaf6fb4 /gtk/gtkviewport.c
parentdde714386de730058981c87c807c83c05b5dc7be (diff)
downloadgtk+-2df27ce7f801da92e64ed966d96c47e3f5d190e7.tar.gz
Make GtkViewport use GtkPixelCache
Since gdk_window_move() no longer uses XCopyArea all scrolling now re-renders everything in the window. To get performance back we use a GtkPixelCache to store already drawn children, and we when we expose the viewport we just blit the offscreen to the right place.
Diffstat (limited to 'gtk/gtkviewport.c')
-rw-r--r--gtk/gtkviewport.c104
1 files changed, 82 insertions, 22 deletions
diff --git a/gtk/gtkviewport.c b/gtk/gtkviewport.c
index 145e7603ab..20120f6b2d 100644
--- a/gtk/gtkviewport.c
+++ b/gtk/gtkviewport.c
@@ -32,6 +32,7 @@
#include "gtkprivate.h"
#include "gtkscrollable.h"
#include "gtktypebuiltins.h"
+#include "gtkpixelcacheprivate.h"
/**
@@ -66,6 +67,8 @@ struct _GtkViewportPrivate
GdkWindow *bin_window;
GdkWindow *view_window;
+ GtkPixelCache *pixel_cache;
+
/* GtkScrollablePolicy needs to be checked when
* driving the scrollable adjustment values */
guint hscroll_policy : 1;
@@ -113,6 +116,8 @@ static void gtk_viewport_get_preferred_height (GtkWidget *widget,
static void viewport_set_adjustment (GtkViewport *viewport,
GtkOrientation orientation,
GtkAdjustment *adjustment);
+static void gtk_viewport_queue_draw_region (GtkWidget *widget,
+ const cairo_region_t *region);
G_DEFINE_TYPE_WITH_CODE (GtkViewport, gtk_viewport, GTK_TYPE_BIN,
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
@@ -139,6 +144,7 @@ gtk_viewport_class_init (GtkViewportClass *class)
widget_class->style_updated = gtk_viewport_style_updated;
widget_class->get_preferred_width = gtk_viewport_get_preferred_width;
widget_class->get_preferred_height = gtk_viewport_get_preferred_height;
+ widget_class->queue_draw_region = gtk_viewport_queue_draw_region;
gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_VIEWPORT);
@@ -249,6 +255,8 @@ gtk_viewport_init (GtkViewport *viewport)
priv->hadjustment = NULL;
priv->vadjustment = NULL;
+ priv->pixel_cache = _gtk_pixel_cache_new ();
+
viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, NULL);
viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, NULL);
}
@@ -301,10 +309,15 @@ static void
gtk_viewport_destroy (GtkWidget *widget)
{
GtkViewport *viewport = GTK_VIEWPORT (widget);
+ GtkViewportPrivate *priv = viewport->priv;
viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
+ if (priv->pixel_cache)
+ _gtk_pixel_cache_free (priv->pixel_cache);
+ priv->pixel_cache = NULL;
+
GTK_WIDGET_CLASS (gtk_viewport_parent_class)->destroy (widget);
}
@@ -650,6 +663,40 @@ gtk_viewport_get_view_window (GtkViewport *viewport)
}
static void
+gtk_viewport_bin_window_invalidate_handler (GdkWindow *window,
+ cairo_region_t *region)
+{
+ gpointer widget;
+ GtkViewport *viewport;
+ GtkViewportPrivate *priv;
+
+ gdk_window_get_user_data (window, &widget);
+ viewport = GTK_VIEWPORT (widget);
+ priv = viewport->priv;
+
+ _gtk_pixel_cache_invalidate (priv->pixel_cache, region);
+}
+
+static void
+gtk_viewport_queue_draw_region (GtkWidget *widget,
+ const cairo_region_t *region)
+{
+ GtkViewport *viewport = GTK_VIEWPORT (widget);
+ GtkViewportPrivate *priv = viewport->priv;
+
+ /* There is no way we can know if a region targets the
+ not-currently-visible but in pixel cache region, so we
+ always just invalidate the whole thing whenever the
+ tree view gets a queue draw. This doesn't normally happen
+ in normal scrolling cases anyway. */
+ _gtk_pixel_cache_invalidate (priv->pixel_cache, NULL);
+
+ GTK_WIDGET_CLASS (gtk_viewport_parent_class)->queue_draw_region (widget,
+ region);
+}
+
+
+static void
gtk_viewport_realize (GtkWidget *widget)
{
GtkViewport *viewport = GTK_VIEWPORT (widget);
@@ -713,6 +760,8 @@ gtk_viewport_realize (GtkWidget *widget)
priv->bin_window = gdk_window_new (priv->view_window, &attributes, attributes_mask);
gtk_widget_register_window (widget, priv->bin_window);
+ gdk_window_set_invalidate_handler (priv->bin_window,
+ gtk_viewport_bin_window_invalidate_handler);
child = gtk_bin_get_child (bin);
if (child)
@@ -743,6 +792,25 @@ gtk_viewport_unrealize (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unrealize (widget);
}
+static void
+draw_bin (cairo_t *cr,
+ gpointer user_data)
+{
+ GtkWidget *widget = GTK_WIDGET (user_data);
+ GtkViewport *viewport = GTK_VIEWPORT (widget);
+ GtkViewportPrivate *priv = viewport->priv;
+ GtkStyleContext *context;
+ int x, y;
+
+ context = gtk_widget_get_style_context (widget);
+
+ gdk_window_get_position (priv->bin_window, &x, &y);
+ gtk_render_background (context, cr, x, y,
+ gdk_window_get_width (priv->bin_window),
+ gdk_window_get_height (priv->bin_window));
+ GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
+}
+
static gint
gtk_viewport_draw (GtkWidget *widget,
cairo_t *cr)
@@ -750,7 +818,6 @@ gtk_viewport_draw (GtkWidget *widget,
GtkViewport *viewport = GTK_VIEWPORT (widget);
GtkViewportPrivate *priv = viewport->priv;
GtkStyleContext *context;
- int x, y;
context = gtk_widget_get_style_context (widget);
@@ -766,30 +833,23 @@ gtk_viewport_draw (GtkWidget *widget,
gtk_style_context_restore (context);
}
-
- if (gtk_cairo_should_draw_window (cr, priv->view_window))
- {
- /* This is a cute hack to ensure the contents of bin_window are
- * restricted to where they are visible. We only need to do this
- * clipping when called via gtk_widget_draw() and not in expose
- * events. And when that happens every window (including this one)
- * should be drawn.
- */
- gdk_window_get_position (priv->view_window, &x, &y);
- cairo_rectangle (cr, x, y,
- gdk_window_get_width (priv->view_window),
- gdk_window_get_height (priv->view_window));
- cairo_clip (cr);
- }
if (gtk_cairo_should_draw_window (cr, priv->bin_window))
{
- gdk_window_get_position (priv->bin_window, &x, &y);
- gtk_render_background (context, cr, x, y,
- gdk_window_get_width (priv->bin_window),
- gdk_window_get_height (priv->bin_window));
+ cairo_rectangle_int_t view_rect;
+ cairo_rectangle_int_t canvas_rect;
+
+ gdk_window_get_position (priv->view_window, &view_rect.x, &view_rect.y);
+ view_rect.width = gdk_window_get_width (priv->view_window);
+ view_rect.height = gdk_window_get_height (priv->view_window);
+
+ gdk_window_get_position (priv->bin_window, &canvas_rect.x, &canvas_rect.y);
+ canvas_rect.width = gdk_window_get_width (priv->bin_window);
+ canvas_rect.height = gdk_window_get_height (priv->bin_window);
- GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
+ _gtk_pixel_cache_draw (priv->pixel_cache, cr, priv->bin_window,
+ &view_rect, &canvas_rect,
+ draw_bin, widget);
}
return FALSE;
@@ -843,7 +903,7 @@ gtk_viewport_size_allocate (GtkWidget *widget,
viewport_set_hadjustment_values (viewport);
viewport_set_vadjustment_values (viewport);
-
+
child_allocation.x = 0;
child_allocation.y = 0;
child_allocation.width = gtk_adjustment_get_upper (hadjustment);