Use pixman regions to reduce repainting

For now, we just use glScissor and clip to the extent of the damage region,
but we can do even better by clipping the repaint to the region rectangles.
diff --git a/compositor/compositor.c b/compositor/compositor.c
index 535b3f1..ec0d727 100644
--- a/compositor/compositor.c
+++ b/compositor/compositor.c
@@ -163,6 +163,27 @@
 	return surface;
 }
 
+void
+wlsc_surface_damage_rectangle(struct wlsc_surface *surface,
+			      int32_t x, int32_t y,
+			      int32_t width, int32_t height)
+{
+	struct wlsc_compositor *compositor = surface->compositor;
+
+	pixman_region32_union_rect(&compositor->damage_region,
+				   &compositor->damage_region,
+				   surface->x + x, surface->y + y,
+				   width, height);
+	wlsc_compositor_schedule_repaint(compositor);
+}
+
+void
+wlsc_surface_damage(struct wlsc_surface *surface)
+{
+	wlsc_surface_damage_rectangle(surface, 0, 0,
+				      surface->width, surface->height);
+}
+
 uint32_t
 get_time(void)
 {
@@ -178,10 +199,11 @@
 {
 	struct wlsc_surface *surface =
 		container_of(resource, struct wlsc_surface, surface.resource);
-	struct wlsc_compositor *compositor = surface->compositor;
 	struct wl_listener *l, *next;
 	uint32_t time;
 
+	wlsc_surface_damage(surface);
+
 	wl_list_remove(&surface->link);
 	glDeleteTextures(1, &surface->texture);
 
@@ -191,8 +213,6 @@
 		l->func(l, &surface->surface, time);
 
 	free(surface);
-
-	wlsc_compositor_schedule_repaint(compositor);
 }
 
 uint32_t *
@@ -407,9 +427,28 @@
 	struct wlsc_compositor *ec = output->compositor;
 	struct wlsc_surface *es;
 	struct wlsc_input_device *eid;
+	pixman_region32_t new_damage, total_damage;
+	pixman_box32_t *extents;
 
 	glViewport(0, 0, output->width, output->height);
 
+	pixman_region32_init(&new_damage);
+	pixman_region32_init(&total_damage);
+	pixman_region32_intersect_rect(&new_damage,
+				       &ec->damage_region,
+				       output->x, output->y,
+				       output->width, output->height);
+	pixman_region32_subtract(&ec->damage_region,
+				 &ec->damage_region, &new_damage);
+	pixman_region32_union(&total_damage, &new_damage,
+			      &output->previous_damage_region);
+	pixman_region32_copy(&output->previous_damage_region, &new_damage);
+
+	extents = pixman_region32_extents(&total_damage);
+	glEnable(GL_SCISSOR_TEST);
+	glScissor(extents->x1, extents->y1,
+		  extents->x2 - extents->x1, extents->y2 - extents->y1);
+
 	es = container_of(ec->surface_list.next, struct wlsc_surface, link);
 	if (es->map_type == WLSC_SURFACE_MAP_FULLSCREEN &&
 	    es->fullscreen_output == output) {
@@ -483,6 +522,12 @@
 {
 	struct wlsc_surface *es = (struct wlsc_surface *) surface;
 
+	/* FIXME: This damages the entire old surface, but we should
+	 * really just damage the part that's no longer covered by the
+	 * surface.  Anything covered by the new surface will be
+	 * damaged by the client. */
+	wlsc_surface_damage(es);
+
 	buffer->attach(buffer, surface);
 	es->buffer = buffer;
 	es->x += x;
@@ -517,7 +562,7 @@
 		break;
 	}
 
-	wlsc_compositor_schedule_repaint(es->compositor);
+	wlsc_surface_damage(es);
 	es->map_type = WLSC_SURFACE_MAP_TOPLEVEL;
 }
 
@@ -544,7 +589,7 @@
 	es->y = pes->y + y;
 
 	wlsc_surface_update_matrix(es);
-	wlsc_compositor_schedule_repaint(es->compositor);
+	wlsc_surface_damage(es);
 	es->map_type = WLSC_SURFACE_MAP_TRANSIENT;
 }
 
@@ -577,7 +622,7 @@
 	es->y = (output->height - es->height) / 2;
 	es->fullscreen_output = output;
 	wlsc_surface_update_matrix(es);
-	wlsc_compositor_schedule_repaint(es->compositor);
+	wlsc_surface_damage(es);
 	es->map_type = WLSC_SURFACE_MAP_FULLSCREEN;
 }
 
@@ -589,7 +634,8 @@
 	struct wlsc_surface *es = (struct wlsc_surface *) surface;
 
 	es->buffer->damage(es->buffer, surface, x, y, width, height);
-	wlsc_compositor_schedule_repaint(es->compositor);
+
+	wlsc_surface_damage_rectangle(es, x, y, width, height);
 }
 
 const static struct wl_surface_interface surface_interface = {
@@ -605,8 +651,7 @@
 wlsc_input_device_attach(struct wlsc_input_device *device,
 			 struct wl_buffer *buffer, int x, int y)
 {
-	struct wlsc_compositor *ec =
-		(struct wlsc_compositor *) device->input_device.compositor;
+	wlsc_surface_damage(device->sprite);
 
 	buffer->attach(buffer, &device->sprite->surface);
 	device->hotspot_x = x;
@@ -618,7 +663,7 @@
 	device->sprite->height = buffer->height;
 	wlsc_surface_update_matrix(device->sprite);
 
-	wlsc_compositor_schedule_repaint(ec);
+	wlsc_surface_damage(device->sprite);
 }
 
 
@@ -770,11 +815,13 @@
 					     time, x, y, sx, sy);
 	}
 
+	wlsc_surface_damage(wd->sprite);
+
 	wd->sprite->x = device->x - wd->hotspot_x;
 	wd->sprite->y = device->y - wd->hotspot_y;
 	wlsc_surface_update_matrix(wd->sprite);
 
-	wlsc_compositor_schedule_repaint(ec);
+	wlsc_surface_damage(wd->sprite);
 }
 
 static void
@@ -854,6 +901,7 @@
 {
 	struct wl_list *l;
 
+	wlsc_surface_damage(switcher->current);
 	l = switcher->current->link.next;
 	if (l == &switcher->compositor->surface_list)
 		l = switcher->compositor->surface_list.next;
@@ -861,7 +909,7 @@
 	wl_list_remove(&switcher->listener.link);
 	wl_list_insert(switcher->current->surface.destroy_listener_list.prev,
 		       &switcher->listener.link);
-	wlsc_compositor_schedule_repaint(switcher->compositor);
+	wlsc_surface_damage(switcher->current);
 }
 
 static void
@@ -1006,7 +1054,7 @@
 		compositor->focus = 0;
 	}
 
-	wlsc_compositor_schedule_repaint(compositor);
+	wlsc_surface_damage(wd->sprite);
 }
 
 void
@@ -1213,6 +1261,8 @@
 	output->width = width;
 	output->height = height;
 
+	pixman_region32_init(&output->previous_damage_region);
+
 	output->background =
 		background_create(output, option_background);
 
@@ -1227,6 +1277,10 @@
 	wl_display_add_object(c->wl_display, &output->object);
 	wl_display_add_global(c->wl_display, &output->object,
 			      wlsc_output_post_geometry);
+
+	pixman_region32_union_rect(&c->damage_region,
+				   &c->damage_region,
+				   x, y, width, height);
 }
 
 int
@@ -1266,6 +1320,7 @@
 
 	loop = wl_display_get_event_loop(ec->wl_display);
 	ec->timer_source = wl_event_loop_add_timer(loop, repaint, ec);
+	pixman_region32_init(&ec->damage_region);
 	wlsc_compositor_schedule_repaint(ec);
 
 	return 0;
diff --git a/compositor/compositor.h b/compositor/compositor.h
index dfe9951..67d9f2a 100644
--- a/compositor/compositor.h
+++ b/compositor/compositor.h
@@ -22,6 +22,7 @@
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 #include <libudev.h>
+#include <pixman.h>
 #include "wayland-server.h"
 #include "wayland-util.h"
 
@@ -47,6 +48,7 @@
 	struct wlsc_surface *background;
 	struct wlsc_matrix matrix;
 	int32_t x, y, width, height;
+	pixman_region32_t previous_damage_region;
 };
 
 enum wlsc_pointer_type {
@@ -109,6 +111,7 @@
 	int repaint_needed;
 	int repaint_on_timeout;
 	struct timespec previous_swap;
+	pixman_region32_t damage_region;
 
 	struct wlsc_switcher *switcher;
 	uint32_t focus;
@@ -182,6 +185,14 @@
 wlsc_compositor_schedule_repaint(struct wlsc_compositor *compositor);
 
 void
+wlsc_surface_damage(struct wlsc_surface *surface);
+
+void
+wlsc_surface_damage_rectangle(struct wlsc_surface *surface,
+			      int32_t x, int32_t y,
+			      int32_t width, int32_t height);
+
+void
 wlsc_input_device_set_pointer_image(struct wlsc_input_device *device,
 				    enum wlsc_pointer_type type);
 struct wlsc_surface *
diff --git a/compositor/shell.c b/compositor/shell.c
index 1ff32e9..a19d3db 100644
--- a/compositor/shell.c
+++ b/compositor/shell.c
@@ -40,9 +40,11 @@
 	struct wlsc_move_grab *move = (struct wlsc_move_grab *) grab;
 	struct wlsc_surface *es = move->surface;
 
+	wlsc_surface_damage(es);
 	es->x = x + move->dx;
 	es->y = y + move->dy;
 	wlsc_surface_update_matrix(es);
+	wlsc_surface_damage(es);
 }
 
 static void
diff --git a/configure.ac b/configure.ac
index b671722..48b9169 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,7 +23,7 @@
 PKG_CHECK_MODULES(FFI, [libffi])
 
 PKG_CHECK_MODULES(COMPOSITOR,
-		  [egl >= 7.10 glesv2 gdk-pixbuf-2.0 libudev >= 136 libdrm >= 2.4.23] xcb-dri2 xcb-xfixes)
+		  [egl >= 7.10 glesv2 gdk-pixbuf-2.0 libudev >= 136 libdrm >= 2.4.23] pixman-1 xcb-dri2 xcb-xfixes)
 PKG_CHECK_MODULES(GLES2, [egl >= 7.10 glesv2])
 PKG_CHECK_MODULES(CLIENT, [egl >= 7.10 gl cairo >= 1.10.0 gdk-pixbuf-2.0 glib-2.0 gobject-2.0 xkbcommon])
 PKG_CHECK_MODULES(POPPLER, [poppler-glib gdk-2.0 gio-2.0],