summaryrefslogtreecommitdiff
path: root/backingstore.c
diff options
context:
space:
mode:
authorSøren Sandmann <sandmann@redhat.com>2008-06-04 02:14:44 -0400
committerSøren Sandmann <sandmann@redhat.com>2008-06-04 02:14:44 -0400
commit44ffeb68a9914aba719767ce4c318abceaa87b6a (patch)
tree38651dee74090d3dae6d38ec3de3d49f43ae6620 /backingstore.c
Various scroll area fixes
Diffstat (limited to 'backingstore.c')
-rw-r--r--backingstore.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/backingstore.c b/backingstore.c
new file mode 100644
index 0000000..5e040c4
--- /dev/null
+++ b/backingstore.c
@@ -0,0 +1,263 @@
+#include "backingstore.h"
+
+struct BackingStore
+{
+ GdkPixmap *pixmap;
+ GdkRegion *update_region;
+ int width;
+ int height;
+};
+
+BackingStore *
+backing_store_new (GdkWindow *window,
+ int width,
+ int height)
+{
+ BackingStore *store = g_new0 (BackingStore, 1);
+ GdkRectangle rect = { 0, 0, width, height };
+
+ store->pixmap = gdk_pixmap_new (window, width, height, -1);
+ store->update_region = gdk_region_rectangle (&rect);
+ store->width = width;
+ store->height = height;
+
+ return store;
+}
+
+void
+backing_store_free (BackingStore *store)
+{
+ g_object_unref (store->pixmap);
+ gdk_region_destroy (store->update_region);
+ g_free (store);
+}
+
+void
+backing_store_scroll (BackingStore *store,
+ int dx,
+ int dy)
+{
+ GdkGC *gc = gdk_gc_new (store->pixmap);
+ GdkRectangle rect;
+
+ gdk_draw_drawable (store->pixmap, gc, store->pixmap,
+ 0, 0, dx, dy,
+ store->width, store->height);
+
+ gdk_region_offset (store->update_region, dx, dy);
+
+ /* Invalidate vertically */
+ rect.x = 0;
+ rect.width = store->width;
+
+ if (dy > 0)
+ {
+ rect.y = 0;
+ rect.height = dy;
+ }
+ else
+ {
+ rect.y = store->height + dy;
+ rect.height = -dy;
+ }
+
+ gdk_region_union_with_rect (store->update_region, &rect);
+
+ /* Invalidate horizontally */
+ rect.y = 0;
+ rect.height = store->height;
+
+ if (dx > 0)
+ {
+ rect.x = 0;
+ rect.width = dx;
+ }
+ else
+ {
+ rect.x = store->width + dx;
+ rect.width = -dx;
+ }
+
+ gdk_region_union_with_rect (store->update_region, &rect);
+}
+
+static void
+print_region (const char *header, GdkRegion *region)
+{
+ GdkRectangle *rects;
+ int n_rects;
+ int i;
+
+ g_print ("%s\n", header);
+
+ gdk_region_get_rectangles (region, &rects, &n_rects);
+ for (i = 0; i < n_rects; ++i)
+ {
+ GdkRectangle *rect = &(rects[i]);
+ g_print (" %d %d %d %d\n",
+ rect->x, rect->y, rect->width, rect->height);
+ }
+
+ g_free (rects);
+}
+
+void
+backing_store_invalidate_rect (BackingStore *store,
+ GdkRectangle *rect)
+{
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (rect != NULL);
+
+ gdk_region_union_with_rect (store->update_region, rect);
+}
+
+void
+backing_store_invalidate_region (BackingStore *store,
+ GdkRegion *region)
+{
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (region != NULL);
+
+ gdk_region_union (store->update_region, region);
+}
+
+void
+backing_store_invalidate_all (BackingStore *store)
+{
+ GdkRectangle rect;
+
+ g_return_if_fail (store != NULL);
+
+ gdk_region_destroy (store->update_region);
+
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = store->width;
+ rect.height = store->height;
+
+ store->update_region = gdk_region_rectangle (&rect);
+}
+
+static void
+simple_draw_drawable (GdkDrawable *dst,
+ GdkDrawable *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ int width,
+ int height)
+{
+ GdkGC *gc = gdk_gc_new (dst);
+
+ gdk_draw_drawable (dst, gc, src, src_x, src_y, dst_x, dst_y, width, height);
+
+ g_object_unref (gc);
+}
+
+void
+backing_store_resize (BackingStore *store,
+ int width,
+ int height)
+{
+ GdkPixmap *pixmap;
+ GdkRegion *old, *invalid;
+ GdkRectangle r;
+
+ width = MAX (width, 1);
+ height = MAX (height, 1);
+
+ pixmap = gdk_pixmap_new (store->pixmap, width, height, -1);
+
+ /* Unfortunately we don't know in which direction we were resized,
+ * so we just assume we were dragged from the south-east corner.
+ *
+ * Although, maybe we could get the root coordinates of the input-window?
+ * That might just work, actually. We need to make sure metacity uses
+ * static gravity for the window before this will be useful.
+ */
+ simple_draw_drawable (pixmap, store->pixmap, 0, 0, 0, 0, -1, -1);
+
+ g_object_unref (store->pixmap);
+
+ store->pixmap = pixmap;
+
+ r.x = r.y = 0;
+ r.height = store->height;
+ r.width = store->width;
+
+ old = gdk_region_rectangle (&r);
+
+ store->width = width;
+ store->height = height;
+
+ r.height = store->height;
+ r.width = store->width;
+
+ invalid = gdk_region_rectangle (&r);
+
+ gdk_region_subtract (invalid, old);
+
+ backing_store_invalidate_region (store, invalid);
+
+ gdk_region_destroy (old);
+ gdk_region_destroy (invalid);
+}
+
+
+static void
+cclip_to_region (cairo_t *cr, GdkRegion *region)
+{
+ int n_rects;
+ GdkRectangle *rects;
+
+ gdk_region_get_rectangles (region, &rects, &n_rects);
+
+ cairo_new_path (cr);
+ while (n_rects--)
+ {
+ GdkRectangle *rect = &(rects[n_rects]);
+
+ cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
+ }
+ cairo_clip (cr);
+
+ g_free (rects);
+}
+
+void
+backing_store_draw (BackingStore *store,
+ GdkDrawable *dest,
+ GdkRegion *clip,
+ int dest_x,
+ int dest_y)
+{
+ GdkGC *gc = gdk_gc_new (dest);
+
+ gdk_gc_set_clip_region (gc, clip);
+
+ gdk_draw_drawable (dest, gc, store->pixmap,
+ 0, 0, dest_x, dest_y, store->width, store->height);
+ g_object_unref (gc);
+}
+
+void
+backing_store_process_updates (BackingStore *store,
+ BackingPaintFunc func,
+ gpointer data)
+{
+ cairo_t *cr = gdk_cairo_create (store->pixmap);
+ GdkRegion *region = store->update_region;
+ store->update_region = gdk_region_new ();
+
+ cclip_to_region (cr, region);
+
+ cairo_set_source_rgb (cr, g_random_double(), 0, 0);
+
+ cairo_paint (cr);
+
+ func (cr, region, data);
+
+ gdk_region_destroy (region);
+ cairo_destroy (cr);
+}