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-x11.c b/src/compositor-x11.c
index ea0d4b9..58ccc16 100644
--- a/src/compositor-x11.c
+++ b/src/compositor-x11.c
@@ -59,6 +59,7 @@
 static char *output_name;
 static char *output_mode;
 static char *output_transform;
+static char *output_scale;
 static int option_width;
 static int option_height;
 static int option_count;
@@ -68,6 +69,7 @@
 	char *name;
 	int width, height;
 	uint32_t transform;
+	unsigned int scale;
 	struct wl_list link;
 };
 
@@ -125,6 +127,7 @@
 	int			shm_id;
 	void		       *buf;
 	uint8_t			depth;
+	uint32_t                scale;
 };
 
 static struct xkb_keymap *
@@ -534,8 +537,12 @@
 			configure_notify =
 				(xcb_configure_notify_event_t *) event;
 
-			output->mode.width = configure_notify->width;
-			output->mode.height = configure_notify->height;
+
+			if (configure_notify->width % output->scale != 0 ||
+			    configure_notify->height % output->scale != 0)
+				weston_log("Resolution is not a multiple of screen size, rounding\n");
+			output->mode.width = configure_notify->width / output->scale;
+			output->mode.height = configure_notify->height / output->scale;
 			configured = 1;
 			break;
 		}
@@ -677,7 +684,7 @@
 x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 			     int width, int height, int fullscreen,
 			     int no_input, char *configured_name,
-			     uint32_t transform)
+			     uint32_t transform, uint32_t scale)
 {
 	static const char name[] = "Weston Compositor";
 	static const char class[] = "weston-1\0Weston Compositor";
@@ -686,6 +693,7 @@
 	xcb_screen_iterator_t iter;
 	struct wm_normal_hints normal_hints;
 	struct wl_event_loop *loop;
+	int output_width, output_height;
 	uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR;
 	xcb_atom_t atom_list[1];
 	uint32_t values[2] = {
@@ -694,6 +702,9 @@
 		0
 	};
 
+	output_width = width * scale;
+	output_height = height * scale;
+
 	if (configured_name)
 		sprintf(title, "%s - %s", name, configured_name);
 	else
@@ -719,9 +730,13 @@
 
 	output->mode.flags =
 		WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+	if (output->scale != 1)
+		output->mode.flags |= WL_OUTPUT_MODE_SCALED;
+
 	output->mode.width = width;
 	output->mode.height = height;
 	output->mode.refresh = 60000;
+	output->scale = scale;
 	wl_list_init(&output->base.mode_list);
 	wl_list_insert(&output->base.mode_list, &output->mode.link);
 
@@ -733,7 +748,7 @@
 			  output->window,
 			  iter.data->root,
 			  0, 0,
-			  width, height,
+			  output_width, output_height,
 			  0,
 			  XCB_WINDOW_CLASS_INPUT_OUTPUT,
 			  iter.data->root_visual,
@@ -751,10 +766,10 @@
 		memset(&normal_hints, 0, sizeof normal_hints);
 		normal_hints.flags =
 			WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE;
-		normal_hints.min_width = width;
-		normal_hints.min_height = height;
-		normal_hints.max_width = width;
-		normal_hints.max_height = height;
+		normal_hints.min_width = output_width;
+		normal_hints.min_height = output_height;
+		normal_hints.max_width = output_width;
+		normal_hints.max_height = output_height;
 		xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
 				    c->atom.wm_normal_hints,
 				    c->atom.wm_size_hints, 32,
@@ -794,10 +809,10 @@
 	output->base.make = "xwayland";
 	output->base.model = "none";
 	weston_output_init(&output->base, &c->base,
-			   x, y, width, height, transform);
+			   x, y, width, height, transform, scale);
 
 	if (c->use_pixman) {
-		if (x11_output_init_shm(c, output, width, height) < 0)
+		if (x11_output_init_shm(c, output, output_width, output_height) < 0)
 			return NULL;
 		if (pixman_renderer_output_create(&output->base) < 0) {
 			x11_output_deinit_shm(c, output);
@@ -960,8 +975,8 @@
 {
 	struct weston_output *output = &x11_output->base;
 	wl_fixed_t tx, ty;
-	wl_fixed_t width = wl_fixed_from_int(output->width - 1);
-	wl_fixed_t height = wl_fixed_from_int(output->height - 1);
+	wl_fixed_t width = wl_fixed_from_int(output->width * output->scale - 1);
+	wl_fixed_t height = wl_fixed_from_int(output->height * output->scale - 1);
 
 	switch(output->transform) {
 	case WL_OUTPUT_TRANSFORM_NORMAL:
@@ -999,6 +1014,9 @@
 		break;
 	}
 
+	tx /= output->scale;
+	ty /= output->scale;
+
 	tx += wl_fixed_from_int(output->x);
 	ty += wl_fixed_from_int(output->y);
 
@@ -1457,7 +1475,7 @@
 						      option_height ? height :
 						      o->height,
 						      fullscreen, no_input,
-						      o->name, o->transform);
+						      o->name, o->transform, o->scale);
 		if (output == NULL)
 			goto err_x11_input;
 
@@ -1471,7 +1489,7 @@
 	for (i = output_count; i < count; i++) {
 		output = x11_compositor_create_output(c, x, 0, width, height,
 						      fullscreen, no_input, NULL,
-						      WL_OUTPUT_TRANSFORM_NORMAL);
+						      WL_OUTPUT_TRANSFORM_NORMAL, wl_fixed_from_int(1));
 		if (output == NULL)
 			goto err_x11_input;
 		x = pixman_region32_extents(&output->base.region)->x2;
@@ -1536,7 +1554,7 @@
 	output = malloc(sizeof *output);
 
 	if (!output || !output_name || (output_name[0] != 'X') ||
-				(!output_mode && !output_transform)) {
+				(!output_mode && !output_transform && !output_scale)) {
 		if (output_name)
 			free(output_name);
 		output_name = NULL;
@@ -1559,6 +1577,16 @@
 		output->height = 640;
 	}
 
+	output->scale = 1;
+	if (output_scale) {
+		if (sscanf(output_scale, "%d", &output->scale) != 1) {
+			weston_log("Invalid scale \"%s\" for output %s\n",
+				   output_scale, output_name);
+			x11_free_configured_output(output);
+			goto err_free;
+		}
+	}
+
 	x11_output_set_transform(output);
 
 	wl_list_insert(configured_output_list.prev, &output->link);
@@ -1570,6 +1598,7 @@
 		free(output_transform);
 	output_mode = NULL;
 	output_transform = NULL;
+	output_scale = NULL;
 }
 
 WL_EXPORT struct weston_compositor *
@@ -1597,6 +1626,7 @@
 		{ "name", CONFIG_KEY_STRING, &output_name },
 		{ "mode", CONFIG_KEY_STRING, &output_mode },
 		{ "transform", CONFIG_KEY_STRING, &output_transform },
+		{ "scale", CONFIG_KEY_STRING, &output_scale },
 	};
 
 	const struct config_section config_section[] = {