compositor: Support output/buffer scaling

If you specify e.g. scale=2 in weston.ini an output section for the
X11 backend we automatically upscale all normal surfaces by this
amount. Additionally we respect a buffer_scale set on the buffer to
mean that the buffer is already in a scaled form.

This works with both the gl and the pixman renderer. The non-X
backends compile and work, but don't support changing the output
scale (they do downscale as needed due to buffer_scale though).

This also sends the new "scale" and "done" events on wl_output,
making clients aware of the scale.
diff --git a/src/compositor.c b/src/compositor.c
index f67028e..99fff6d 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -93,6 +93,8 @@
 
 static void
 weston_output_transform_init(struct weston_output *output, uint32_t transform);
+static void
+weston_output_scale_init(struct weston_output *output, uint32_t scale);
 
 WL_EXPORT int
 weston_output_switch_mode(struct weston_output *output, struct weston_mode *mode)
@@ -113,6 +115,7 @@
 
 	/* Update output region and transformation matrix */
 	weston_output_transform_init(output, output->transform);
+	weston_output_scale_init(output, output->scale);
 
 	pixman_region32_init(&output->previous_damage);
 	pixman_region32_init_rect(&output->region, output->x, output->y,
@@ -291,7 +294,9 @@
 	}
 
 	surface->buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL;
+	surface->buffer_scale = 1;
 	surface->pending.buffer_transform = surface->buffer_transform;
+	surface->pending.buffer_scale = surface->buffer_scale;
 	surface->output = NULL;
 	surface->plane = &compositor->primary_plane;
 	surface->pending.newly_attached = 0;
@@ -360,6 +365,7 @@
 WL_EXPORT void
 weston_transformed_coord(int width, int height,
 			 enum wl_output_transform transform,
+			 uint32_t scale,
 			 float sx, float sy, float *bx, float *by)
 {
 	switch (transform) {
@@ -397,20 +403,24 @@
 		*by = sx;
 		break;
 	}
+
+	*bx *= scale;
+	*by *= scale;
 }
 
 WL_EXPORT pixman_box32_t
 weston_transformed_rect(int width, int height,
 			enum wl_output_transform transform,
+			uint32_t scale,
 			pixman_box32_t rect)
 {
 	float x1, x2, y1, y2;
 
 	pixman_box32_t ret;
 
-	weston_transformed_coord(width, height, transform,
+	weston_transformed_coord(width, height, transform, scale,
 				 rect.x1, rect.y1, &x1, &y1);
-	weston_transformed_coord(width, height, transform,
+	weston_transformed_coord(width, height, transform, scale,
 				 rect.x2, rect.y2, &x2, &y2);
 
 	if (x1 <= x2) {
@@ -439,16 +449,34 @@
 	weston_transformed_coord(surface->geometry.width,
 				 surface->geometry.height,
 				 surface->buffer_transform,
+				 surface->buffer_scale,
 				 sx, sy, bx, by);
 }
 
+WL_EXPORT void
+weston_surface_to_buffer(struct weston_surface *surface,
+			 int sx, int sy, int *bx, int *by)
+{
+	float bxf, byf;
+
+	weston_transformed_coord(surface->geometry.width,
+				 surface->geometry.height,
+				 surface->buffer_transform,
+				 surface->buffer_scale,
+				 sx, sy, &bxf, &byf);
+	*bx = floorf(bxf);
+	*by = floorf(byf);
+}
+
 WL_EXPORT pixman_box32_t
 weston_surface_to_buffer_rect(struct weston_surface *surface,
 			      pixman_box32_t rect)
 {
 	return weston_transformed_rect(surface->geometry.width,
 				       surface->geometry.height,
-				       surface->buffer_transform, rect);
+				       surface->buffer_transform,
+				       surface->buffer_scale,
+				       rect);
 }
 
 WL_EXPORT void
@@ -877,29 +905,37 @@
 WL_EXPORT int32_t
 weston_surface_buffer_width(struct weston_surface *surface)
 {
+	int32_t width;
 	switch (surface->buffer_transform) {
 	case WL_OUTPUT_TRANSFORM_90:
 	case WL_OUTPUT_TRANSFORM_270:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
-		return surface->buffer_ref.buffer->height;
+		width = surface->buffer_ref.buffer->height;
+                break;
 	default:
-		return surface->buffer_ref.buffer->width;
+		width = surface->buffer_ref.buffer->width;
+                break;
 	}
+	return width / surface->buffer_scale;
 }
 
 WL_EXPORT int32_t
 weston_surface_buffer_height(struct weston_surface *surface)
 {
+	int32_t height;
 	switch (surface->buffer_transform) {
 	case WL_OUTPUT_TRANSFORM_90:
 	case WL_OUTPUT_TRANSFORM_270:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
 	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
-		return surface->buffer_ref.buffer->width;
+		height = surface->buffer_ref.buffer->width;
+                break;
 	default:
-		return surface->buffer_ref.buffer->height;
+		height = surface->buffer_ref.buffer->height;
+                break;
 	}
+	return height / surface->buffer_scale;
 }
 
 WL_EXPORT uint32_t
@@ -1485,25 +1521,28 @@
 weston_surface_commit(struct weston_surface *surface)
 {
 	pixman_region32_t opaque;
-	int buffer_width = 0;
-	int buffer_height = 0;
+	int surface_width = 0;
+	int surface_height = 0;
 
-	/* wl_surface.set_buffer_rotation */
+	/* wl_surface.set_buffer_transform */
 	surface->buffer_transform = surface->pending.buffer_transform;
 
+	/* wl_surface.set_buffer_scale */
+	surface->buffer_scale = surface->pending.buffer_scale;
+
 	/* wl_surface.attach */
 	if (surface->pending.buffer || surface->pending.newly_attached)
 		weston_surface_attach(surface, surface->pending.buffer);
 
 	if (surface->buffer_ref.buffer) {
-		buffer_width = weston_surface_buffer_width(surface);
-		buffer_height = weston_surface_buffer_height(surface);
+		surface_width = weston_surface_buffer_width(surface);
+		surface_height = weston_surface_buffer_height(surface);
 	}
 
 	if (surface->configure && surface->pending.newly_attached)
 		surface->configure(surface,
 				   surface->pending.sx, surface->pending.sy,
-				   buffer_width, buffer_height);
+				   surface_width, surface_height);
 
 	if (surface->pending.buffer)
 		wl_list_remove(&surface->pending.buffer_destroy_listener.link);
@@ -1588,6 +1627,16 @@
 	surface->pending.buffer_transform = transform;
 }
 
+static void
+surface_set_buffer_scale(struct wl_client *client,
+			 struct wl_resource *resource,
+			 uint32_t scale)
+{
+	struct weston_surface *surface = resource->data;
+
+	surface->pending.buffer_scale = scale;
+}
+
 static const struct wl_surface_interface surface_interface = {
 	surface_destroy,
 	surface_attach,
@@ -1596,7 +1645,8 @@
 	surface_set_opaque_region,
 	surface_set_input_region,
 	surface_commit,
-	surface_set_buffer_transform
+	surface_set_buffer_transform,
+	surface_set_buffer_scale
 };
 
 static void
@@ -1702,25 +1752,28 @@
 {
 	struct weston_surface *surface = sub->surface;
 	pixman_region32_t opaque;
-	int buffer_width = 0;
-	int buffer_height = 0;
+	int surface_width = 0;
+	int surface_height = 0;
 
-	/* wl_surface.set_buffer_rotation */
+	/* wl_surface.set_buffer_transform */
 	surface->buffer_transform = sub->cached.buffer_transform;
 
+	/* wl_surface.set_buffer_scale */
+	surface->buffer_scale = sub->cached.buffer_scale;
+
 	/* wl_surface.attach */
 	if (sub->cached.buffer_ref.buffer || sub->cached.newly_attached)
 		weston_surface_attach(surface, sub->cached.buffer_ref.buffer);
 	weston_buffer_reference(&sub->cached.buffer_ref, NULL);
 
 	if (surface->buffer_ref.buffer) {
-		buffer_width = weston_surface_buffer_width(surface);
-		buffer_height = weston_surface_buffer_height(surface);
+		surface_width = weston_surface_buffer_width(surface);
+		surface_height = weston_surface_buffer_height(surface);
 	}
 
 	if (surface->configure && sub->cached.newly_attached)
 		surface->configure(surface, sub->cached.sx, sub->cached.sy,
-				   buffer_width, buffer_height);
+				   surface_width, surface_height);
 	sub->cached.sx = 0;
 	sub->cached.sy = 0;
 	sub->cached.newly_attached = 0;
@@ -1797,6 +1850,7 @@
 	surface->pending.newly_attached = 0;
 
 	sub->cached.buffer_transform = surface->pending.buffer_transform;
+	sub->cached.buffer_scale = surface->pending.buffer_scale;
 
 	pixman_region32_copy(&sub->cached.opaque, &surface->pending.opaque);
 
@@ -2466,6 +2520,9 @@
 				output->subpixel,
 				output->make, output->model,
 				output->transform);
+	if (version >= 2)
+		wl_output_send_scale(resource,
+				     output->scale);
 
 	wl_list_for_each (mode, &output->mode_list, link) {
 		wl_output_send_mode(resource,
@@ -2474,6 +2531,9 @@
 				    mode->height,
 				    mode->refresh);
 	}
+
+	if (version >= 2)
+		wl_output_send_done(resource);
 }
 
 WL_EXPORT void
@@ -2608,6 +2668,12 @@
 	}
 }
 
+static void
+weston_output_scale_init(struct weston_output *output, uint32_t scale)
+{
+        output->scale = scale;
+}
+
 WL_EXPORT void
 weston_output_move(struct weston_output *output, int x, int y)
 {
@@ -2622,7 +2688,8 @@
 
 WL_EXPORT void
 weston_output_init(struct weston_output *output, struct weston_compositor *c,
-		   int x, int y, int width, int height, uint32_t transform)
+		   int x, int y, int width, int height, uint32_t transform,
+		   uint32_t scale)
 {
 	output->compositor = c;
 	output->x = x;
@@ -2636,6 +2703,7 @@
 	output->dirty = 1;
 
 	weston_output_transform_init(output, transform);
+	weston_output_scale_init(output, scale);
 	weston_output_init_zoom(output);
 
 	weston_output_move(output, x, y);