diff options
author | Søren Sandmann <sandmann@redhat.com> | 2008-06-04 02:14:44 -0400 |
---|---|---|
committer | Søren Sandmann <sandmann@redhat.com> | 2008-06-04 02:14:44 -0400 |
commit | 44ffeb68a9914aba719767ce4c318abceaa87b6a (patch) | |
tree | 38651dee74090d3dae6d38ec3de3d49f43ae6620 /backingstore.c |
Various scroll area fixes
Diffstat (limited to 'backingstore.c')
-rw-r--r-- | backingstore.c | 263 |
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); +} |