Implement output transformations.

This patch allows rotation and mirroring outputs for x11 and drm backends.
A new 'transform' key can be set in the [output] section. From the protocol:

"The flipped values correspond to an initial flip around a vertical axis
followed by rotation."

The transform key can be one of the following 8 strings:

normal
90
180
270
flipped
flipped-90
flipped-180
flipped-270
diff --git a/src/compositor-android.c b/src/compositor-android.c
index a9c45d2..b095262 100644
--- a/src/compositor-android.c
+++ b/src/compositor-android.c
@@ -238,7 +238,8 @@
 	mm_width  = output->fb->width / output->fb->xdpi * 25.4f;
 	mm_height = output->fb->height / output->fb->ydpi * 25.4f;
 	weston_output_init(&output->base, &compositor->base,
-			   0, 0, round(mm_width), round(mm_height));
+			   0, 0, round(mm_width), round(mm_height),
+			   WL_OUTPUT_TRANSFORM_NORMAL);
 	wl_list_insert(compositor->base.output_list.prev, &output->base.link);
 }
 
diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index 8c8c8c0..bcc9a0d 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -46,6 +46,7 @@
 static int option_current_mode = 0;
 static char *output_name;
 static char *output_mode;
+static char *output_transform;
 static struct wl_list configured_output_list;
 
 enum output_config {
@@ -60,6 +61,7 @@
 struct drm_configured_output {
 	char *name;
 	char *mode;
+	uint32_t transform;
 	int32_t width, height;
 	drmModeModeInfo crtc_mode;
 	enum output_config config;
@@ -1434,7 +1436,8 @@
 
 	wl_list_for_each(temp, &configured_output_list, link) {
 		if (strcmp(temp->name, output->name) == 0) {
-			weston_log("%s mode \"%s\" in config\n",
+			if (temp->mode)
+				weston_log("%s mode \"%s\" in config\n",
 							temp->name, temp->mode);
 			o = temp;
 			break;
@@ -1450,9 +1453,9 @@
 	}
 
 	wl_list_for_each(drm_mode, &output->base.mode_list, base.link) {
-		if (o && o->width == drm_mode->base.width &&
-			o->height == drm_mode->base.height &&
-			o->config == OUTPUT_CONFIG_MODE)
+		if (o && o->config == OUTPUT_CONFIG_MODE &&
+			o->width == drm_mode->base.width &&
+			o->height == drm_mode->base.height)
 			configured = drm_mode;
 		if (!memcmp(&crtc_mode, &drm_mode->mode_info, sizeof crtc_mode))
 			current = drm_mode;
@@ -1528,7 +1531,8 @@
 	}
 
 	weston_output_init(&output->base, &ec->base, x, y,
-			   connector->mmWidth, connector->mmHeight);
+			   connector->mmWidth, connector->mmHeight,
+			   o ? o->transform : WL_OUTPUT_TRANSFORM_NORMAL);
 
 	wl_list_insert(ec->base.output_list.prev, &output->base.link);
 
@@ -1695,7 +1699,7 @@
 
 			x += container_of(ec->base.output_list.prev,
 					  struct weston_output,
-					  link)->current->width;
+					  link)->width;
 		}
 
 		drmModeFreeConnector(connector);
@@ -1751,7 +1755,7 @@
 
 			/* XXX: not yet needed, we die with 0 outputs */
 			if (!wl_list_empty(&ec->base.output_list))
-				x = last->x + last->current->width;
+				x = last->x + last->width;
 			else
 				x = 0;
 			y = 0;
@@ -1779,7 +1783,7 @@
 				disconnects &= ~(1 << output->connector_id);
 				weston_log("connector %d disconnected\n",
 				       output->connector_id);
-				x_offset += output->base.current->width;
+				x_offset += output->base.width;
 				drm_output_destroy(&output->base);
 			}
 		}
@@ -2412,17 +2416,54 @@
 }
 
 static void
+drm_output_set_transform(struct drm_configured_output *output)
+{
+	if (!output_transform) {
+		output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+		return;
+	}
+
+	if (!strcmp(output_transform, "normal"))
+		output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+	else if (!strcmp(output_transform, "90"))
+		output->transform = WL_OUTPUT_TRANSFORM_90;
+	else if (!strcmp(output_transform, "180"))
+		output->transform = WL_OUTPUT_TRANSFORM_180;
+	else if (!strcmp(output_transform, "270"))
+		output->transform = WL_OUTPUT_TRANSFORM_270;
+	else if (!strcmp(output_transform, "flipped"))
+		output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
+	else if (!strcmp(output_transform, "flipped-90"))
+		output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
+	else if (!strcmp(output_transform, "flipped-180"))
+		output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
+	else if (!strcmp(output_transform, "flipped-270"))
+		output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
+	else {
+		weston_log("Invalid transform \"%s\" for output %s\n",
+						output_transform, output_name);
+		output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+	}
+
+	free(output_transform);
+	output_transform = NULL;
+}
+
+static void
 output_section_done(void *data)
 {
 	struct drm_configured_output *output;
 
 	output = malloc(sizeof *output);
 
-	if (!output || !output_name || !output_mode) {
+	if (!output || !output_name || (output_name[0] == 'X') ||
+					(!output_mode && !output_transform)) {
 		free(output_name);
-		output_name = NULL;
 		free(output_mode);
+		free(output_transform);
+		output_name = NULL;
 		output_mode = NULL;
+		output_transform = NULL;
 		return;
 	}
 
@@ -2430,24 +2471,32 @@
 	output->name = output_name;
 	output->mode = output_mode;
 
-	if (strcmp(output_mode, "off") == 0)
-		output->config = OUTPUT_CONFIG_OFF;
-	else if (strcmp(output_mode, "preferred") == 0)
-		output->config = OUTPUT_CONFIG_PREFERRED;
-	else if (strcmp(output_mode, "current") == 0)
-		output->config = OUTPUT_CONFIG_CURRENT;
-	else if (sscanf(output_mode, "%dx%d", &output->width, &output->height) == 2)
-		output->config = OUTPUT_CONFIG_MODE;
-	else if (check_for_modeline(output) == 0)
-		output->config = OUTPUT_CONFIG_MODELINE;
+	if (output_mode) {
+		if (strcmp(output_mode, "off") == 0)
+			output->config = OUTPUT_CONFIG_OFF;
+		else if (strcmp(output_mode, "preferred") == 0)
+			output->config = OUTPUT_CONFIG_PREFERRED;
+		else if (strcmp(output_mode, "current") == 0)
+			output->config = OUTPUT_CONFIG_CURRENT;
+		else if (sscanf(output_mode, "%dx%d",
+					&output->width, &output->height) == 2)
+			output->config = OUTPUT_CONFIG_MODE;
+		else if (check_for_modeline(output) == 0)
+			output->config = OUTPUT_CONFIG_MODELINE;
 
-	if (output->config != OUTPUT_CONFIG_INVALID)
-		wl_list_insert(&configured_output_list, &output->link);
-	else {
-		weston_log("Invalid mode \"%s\" for output %s\n",
-						output_mode, output_name);
-		drm_free_configured_output(output);
+		if (output->config == OUTPUT_CONFIG_INVALID)
+			weston_log("Invalid mode \"%s\" for output %s\n",
+							output_mode, output_name);
+		output_mode = NULL;
 	}
+
+	drm_output_set_transform(output);
+
+	wl_list_insert(&configured_output_list, &output->link);
+
+	if (output_transform)
+		free(output_transform);
+	output_transform = NULL;
 }
 
 WL_EXPORT struct weston_compositor *
@@ -2471,6 +2520,7 @@
 	const struct config_key drm_config_keys[] = {
 		{ "name", CONFIG_KEY_STRING, &output_name },
 		{ "mode", CONFIG_KEY_STRING, &output_mode },
+		{ "transform", CONFIG_KEY_STRING, &output_transform },
 	};
 
 	const struct config_section config_section[] = {
diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
index 4fc77f1..84eaf6c 100644
--- a/src/compositor-wayland.c
+++ b/src/compositor-wayland.c
@@ -395,7 +395,8 @@
 	wl_list_insert(&output->base.mode_list, &output->mode.link);
 
 	output->base.current = &output->mode;
-	weston_output_init(&output->base, &c->base, 0, 0, width, height);
+	weston_output_init(&output->base, &c->base, 0, 0, width, height,
+						WL_OUTPUT_TRANSFORM_NORMAL);
 
 	output->base.border.top = c->border.top;
 	output->base.border.bottom = c->border.bottom;
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
index c02911d..5b1cdd9 100644
--- a/src/compositor-x11.c
+++ b/src/compositor-x11.c
@@ -52,6 +52,7 @@
 
 static char *output_name;
 static char *output_mode;
+static char *output_transform;
 static int option_width;
 static int option_height;
 static int option_count;
@@ -60,6 +61,7 @@
 struct x11_configured_output {
 	char *name;
 	int width, height;
+	uint32_t transform;
 	struct wl_list link;
 };
 
@@ -471,12 +473,15 @@
 	pixman_image_unref(image);
 }
 
-static int
+static struct x11_output *
 x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 			     int width, int height, int fullscreen,
-			     int no_input, const char *name)
+			     int no_input, char *configured_name,
+			     uint32_t transform)
 {
+	static const char name[] = "Weston Compositor";
 	static const char class[] = "weston-1\0Weston Compositor";
+	char title[32];
 	struct x11_output *output;
 	xcb_screen_iterator_t iter;
 	struct wm_normal_hints normal_hints;
@@ -488,6 +493,11 @@
 		0
 	};
 
+	if (configured_name)
+		sprintf(title, "%s - %s", name, configured_name);
+	else
+		strcpy(title, name);
+
 	if (!no_input)
 		values[0] |=
 			XCB_EVENT_MASK_KEY_PRESS |
@@ -502,7 +512,7 @@
 
 	output = malloc(sizeof *output);
 	if (output == NULL)
-		return -1;
+		return NULL;
 
 	memset(output, 0, sizeof *output);
 
@@ -517,7 +527,8 @@
 	output->base.current = &output->mode;
 	output->base.make = "xwayland";
 	output->base.model = "none";
-	weston_output_init(&output->base, &c->base, x, y, width, height);
+	weston_output_init(&output->base, &c->base,
+			   x, y, width, height, transform);
 
 	values[1] = c->null_cursor;
 	output->window = xcb_generate_id(c->conn);
@@ -550,7 +561,7 @@
 	/* Set window name.  Don't bother with non-EWMH WMs. */
 	xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
 			    c->atom.net_wm_name, c->atom.utf8_string, 8,
-			    strlen(name), name);
+			    strlen(title), title);
 	xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
 			    c->atom.wm_class, c->atom.string, 8,
 			    sizeof class, class);
@@ -570,12 +581,12 @@
 				       output->window, NULL);
 	if (!output->egl_surface) {
 		weston_log("failed to create window surface\n");
-		return -1;
+		return NULL;
 	}
 	if (!eglMakeCurrent(c->base.egl_display, output->egl_surface,
 			    output->egl_surface, c->base.egl_context)) {
 		weston_log("failed to make surface current\n");
-		return -1;
+		return NULL;
 	}
 
 	loop = wl_display_get_event_loop(c->base.wl_display);
@@ -595,7 +606,7 @@
 	weston_log("x11 output %dx%d, window id %d\n",
 		   width, height, output->window);
 
-	return 0;
+	return output;
 }
 
 static struct x11_output *
@@ -739,6 +750,98 @@
 			      WL_POINTER_BUTTON_STATE_RELEASED);
 }
 
+static void
+x11_output_transform_coordinate(struct x11_output *x11_output,
+						wl_fixed_t *x, wl_fixed_t *y)
+{
+	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);
+
+	switch(output->transform) {
+	case WL_OUTPUT_TRANSFORM_NORMAL:
+	default:
+		tx = *x;
+		ty = *y;
+		break;
+	case WL_OUTPUT_TRANSFORM_90:
+		tx = *y;
+		ty = height - *x;
+		break;
+	case WL_OUTPUT_TRANSFORM_180:
+		tx = width - *x;
+		ty = height - *y;
+		break;
+	case WL_OUTPUT_TRANSFORM_270:
+		tx = width - *y;
+		ty = *x;
+		break;
+	case WL_OUTPUT_TRANSFORM_FLIPPED:
+		tx = width - *x;
+		ty = *y;
+		break;
+	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+		tx = width - *y;
+		ty = height - *x;
+		break;
+	case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+		tx = *x;
+		ty = height - *y;
+		break;
+	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+		tx = *y;
+		ty = *x;
+		break;
+	}
+
+	tx += wl_fixed_from_int(output->x);
+	ty += wl_fixed_from_int(output->y);
+
+	*x = tx;
+	*y = ty;
+}
+
+static void
+x11_compositor_deliver_motion_event(struct x11_compositor *c,
+					xcb_generic_event_t *event)
+{
+	struct x11_output *output;
+	wl_fixed_t x, y;
+	xcb_motion_notify_event_t *motion_notify =
+			(xcb_motion_notify_event_t *) event;
+
+	if (!c->has_xkb)
+		update_xkb_state_from_core(c, motion_notify->state);
+	output = x11_compositor_find_output(c, motion_notify->event);
+	x = wl_fixed_from_int(motion_notify->event_x);
+	y = wl_fixed_from_int(motion_notify->event_y);
+	x11_output_transform_coordinate(output, &x, &y);
+
+	notify_motion(&c->core_seat, weston_compositor_get_time(), x, y);
+}
+
+static void
+x11_compositor_deliver_enter_event(struct x11_compositor *c,
+					xcb_generic_event_t *event)
+{
+	struct x11_output *output;
+	wl_fixed_t x, y;
+
+	xcb_enter_notify_event_t *enter_notify =
+			(xcb_enter_notify_event_t *) event;
+	if (enter_notify->state >= Button1Mask)
+		return;
+	if (!c->has_xkb)
+		update_xkb_state_from_core(c, enter_notify->state);
+	output = x11_compositor_find_output(c, enter_notify->event);
+	x = wl_fixed_from_int(enter_notify->event_x);
+	y = wl_fixed_from_int(enter_notify->event_y);
+	x11_output_transform_coordinate(output, &x, &y);
+
+	notify_pointer_focus(&c->core_seat, &output->base, x, y);
+}
+
 static int
 x11_compositor_next_event(struct x11_compositor *c,
 			  xcb_generic_event_t **event, uint32_t mask)
@@ -763,7 +866,6 @@
 	struct x11_output *output;
 	xcb_generic_event_t *event, *prev;
 	xcb_client_message_event_t *client_message;
-	xcb_motion_notify_event_t *motion_notify;
 	xcb_enter_notify_event_t *enter_notify;
 	xcb_key_press_event_t *key_press, *key_release;
 	xcb_keymap_notify_event_t *keymap_notify;
@@ -772,7 +874,6 @@
 	xcb_atom_t atom;
 	uint32_t *k;
 	uint32_t i, set;
-	wl_fixed_t x, y;
 	int count;
 
 	prev = NULL;
@@ -872,14 +973,7 @@
 			x11_compositor_deliver_button_event(c, event, 0);
 			break;
 		case XCB_MOTION_NOTIFY:
-			motion_notify = (xcb_motion_notify_event_t *) event;
-			if (!c->has_xkb)
-				update_xkb_state_from_core(c, motion_notify->state);
-			output = x11_compositor_find_output(c, motion_notify->event);
-			x = wl_fixed_from_int(output->base.x + motion_notify->event_x);
-			y = wl_fixed_from_int(output->base.y + motion_notify->event_y);
-			notify_motion(&c->core_seat,
-				      weston_compositor_get_time(), x, y);
+			x11_compositor_deliver_motion_event(c, event);
 			break;
 
 		case XCB_EXPOSE:
@@ -889,17 +983,7 @@
 			break;
 
 		case XCB_ENTER_NOTIFY:
-			enter_notify = (xcb_enter_notify_event_t *) event;
-			if (enter_notify->state >= Button1Mask)
-				break;
-			if (!c->has_xkb)
-				update_xkb_state_from_core(c, enter_notify->state);
-			output = x11_compositor_find_output(c, enter_notify->event);
-			x = wl_fixed_from_int(output->base.x + enter_notify->event_x);
-			y = wl_fixed_from_int(output->base.y + enter_notify->event_y);
-
-			notify_pointer_focus(&c->core_seat,
-					     &output->base, x, y);
+			x11_compositor_deliver_enter_event(c, event);
 			break;
 
 		case XCB_LEAVE_NOTIFY:
@@ -1063,10 +1147,9 @@
 		      int no_input,
 		      int argc, char *argv[], const char *config_file)
 {
-	static const char name[] = "Weston Compositor";
-	char configured_name[32];
 	struct x11_compositor *c;
 	struct x11_configured_output *o;
+	struct x11_output *output;
 	xcb_screen_iterator_t s;
 	int i, x = 0, output_count = 0;
 	int width, height, count;
@@ -1117,26 +1200,30 @@
 	count = option_count ? option_count : 1;
 
 	wl_list_for_each(o, &configured_output_list, link) {
-		sprintf(configured_name, "%s - %s", name, o->name);
-		if (x11_compositor_create_output(c, x, 0,
-						option_width ? option_width :
-						o->width,
-						option_height ? option_height :
-						o->height,
-						fullscreen, no_input,
-						configured_name) < 0)
+		output = x11_compositor_create_output(c, x, 0,
+						      option_width ? width :
+						      o->width,
+						      option_height ? height :
+						      o->height,
+						      fullscreen, no_input,
+						      o->name, o->transform);
+		if (output == NULL)
 			goto err_x11_input;
-		x += option_width ? option_width : o->width;
+
+		x = pixman_region32_extents(&output->base.region)->x2;
+
 		output_count++;
 		if (option_count && output_count >= option_count)
 			break;
 	}
 
 	for (i = output_count; i < count; i++) {
-		if (x11_compositor_create_output(c, x, 0, width, height,
-						 fullscreen, no_input, name) < 0)
+		output = x11_compositor_create_output(c, x, 0, width, height,
+						      fullscreen, no_input, NULL,
+						      WL_OUTPUT_TRANSFORM_NORMAL);
+		if (output == NULL)
 			goto err_x11_input;
-		x += width;
+		x = pixman_region32_extents(&output->base.region)->x2;
 	}
 
 	c->xcb_source =
@@ -1160,37 +1247,77 @@
 }
 
 static void
+x11_output_set_transform(struct x11_configured_output *output)
+{
+	if (!output_transform) {
+		output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+		return;
+	}
+
+	if (!strcmp(output_transform, "normal"))
+		output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+	else if (!strcmp(output_transform, "90"))
+		output->transform = WL_OUTPUT_TRANSFORM_90;
+	else if (!strcmp(output_transform, "180"))
+		output->transform = WL_OUTPUT_TRANSFORM_180;
+	else if (!strcmp(output_transform, "270"))
+		output->transform = WL_OUTPUT_TRANSFORM_270;
+	else if (!strcmp(output_transform, "flipped"))
+		output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
+	else if (!strcmp(output_transform, "flipped-90"))
+		output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
+	else if (!strcmp(output_transform, "flipped-180"))
+		output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
+	else if (!strcmp(output_transform, "flipped-270"))
+		output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
+	else {
+		weston_log("Invalid transform \"%s\" for output %s\n",
+						output_transform, output_name);
+		output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+	}
+}
+
+static void
 output_section_done(void *data)
 {
 	struct x11_configured_output *output;
 
 	output = malloc(sizeof *output);
 
-	if (!output || !output_name || !output_mode) {
-		free(output_name);
+	if (!output || !output_name || (output_name[0] != 'X') ||
+				(!output_mode && !output_transform)) {
+		if (output_name)
+			free(output_name);
 		output_name = NULL;
 		goto err_free;
 	}
 
 	output->name = output_name;
 
-	if (output_name[0] != 'X') {
-		x11_free_configured_output(output);
-		goto err_free;
+	if (output_mode) {
+		if (sscanf(output_mode, "%dx%d", &output->width,
+						&output->height) != 2) {
+			weston_log("Invalid mode \"%s\" for output %s\n",
+							output_mode, output_name);
+			x11_free_configured_output(output);
+			goto err_free;
+		}
+	} else {
+		output->width = 1024;
+		output->height = 640;
 	}
 
-	if (sscanf(output_mode, "%dx%d", &output->width, &output->height) != 2) {
-		weston_log("Invalid mode \"%s\" for output %s\n",
-						output_mode, output_name);
-		x11_free_configured_output(output);
-		goto err_free;
-	}
+	x11_output_set_transform(output);
 
 	wl_list_insert(configured_output_list.prev, &output->link);
 
 err_free:
-	free(output_mode);
+	if (output_mode)
+		free(output_mode);
+	if (output_transform)
+		free(output_transform);
 	output_mode = NULL;
+	output_transform = NULL;
 }
 
 WL_EXPORT struct weston_compositor *
@@ -1215,6 +1342,7 @@
 	const struct config_key x11_config_keys[] = {
 		{ "name", CONFIG_KEY_STRING, &output_name },
 		{ "mode", CONFIG_KEY_STRING, &output_mode },
+		{ "transform", CONFIG_KEY_STRING, &output_transform },
 	};
 
 	const struct config_section config_section[] = {
diff --git a/src/compositor.c b/src/compositor.c
index 1bf7c96..df28cb7 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -1148,6 +1148,7 @@
 		output->border.left + output->border.right;
 	height = output->current->height +
 		output->border.top + output->border.bottom;
+
 	glViewport(0, 0, width, height);
 
 	/* Rebuild the surface list and update surface transforms up front. */
@@ -1731,14 +1732,14 @@
 	if (!valid) {
 		if (x < prev->x)
 			*fx = wl_fixed_from_int(prev->x);
-		else if (x >= prev->x + prev->current->width)
+		else if (x >= prev->x + prev->width)
 			*fx = wl_fixed_from_int(prev->x +
-						prev->current->width - 1);
+						prev->width - 1);
 		if (y < prev->y)
 			*fy = wl_fixed_from_int(prev->y);
 		else if (y >= prev->y + prev->current->height)
 			*fy = wl_fixed_from_int(prev->y +
-						prev->current->height - 1);
+						prev->height - 1);
 	}
 }
 
@@ -2972,6 +2973,62 @@
 	wl_display_remove_global(c->wl_display, output->global);
 }
 
+static void
+weston_output_compute_transform(struct weston_output *output)
+{
+	struct weston_matrix transform;
+	int flip;
+
+	weston_matrix_init(&transform);
+
+	switch(output->transform) {
+	case WL_OUTPUT_TRANSFORM_FLIPPED:
+	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+	case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+		flip = -1;
+		break;
+	default:
+		flip = 1;
+		break;
+	}
+
+        switch(output->transform) {
+        case WL_OUTPUT_TRANSFORM_NORMAL:
+        case WL_OUTPUT_TRANSFORM_FLIPPED:
+                transform.d[0] = flip;
+                transform.d[1] = 0;
+                transform.d[4] = 0;
+                transform.d[5] = 1;
+                break;
+        case WL_OUTPUT_TRANSFORM_90:
+        case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+                transform.d[0] = 0;
+                transform.d[1] = -flip;
+                transform.d[4] = 1;
+                transform.d[5] = 0;
+                break;
+        case WL_OUTPUT_TRANSFORM_180:
+        case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+                transform.d[0] = -flip;
+                transform.d[1] = 0;
+                transform.d[4] = 0;
+                transform.d[5] = -1;
+                break;
+        case WL_OUTPUT_TRANSFORM_270:
+        case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+                transform.d[0] = 0;
+                transform.d[1] = flip;
+                transform.d[4] = -1;
+                transform.d[5] = 0;
+                break;
+        default:
+                break;
+        }
+
+	weston_matrix_multiply(&output->matrix, &transform);
+}
+
 WL_EXPORT void
 weston_output_update_matrix(struct weston_output *output)
 {
@@ -2981,12 +3038,14 @@
 
 	weston_matrix_init(&output->matrix);
 	weston_matrix_translate(&output->matrix,
-				-(output->x + (output->border.right + output->current->width - output->border.left) / 2.0),
-				-(output->y + (output->border.bottom + output->current->height - output->border.top) / 2.0), 0);
+				-(output->x + (output->border.right + output->width - output->border.left) / 2.0),
+				-(output->y + (output->border.bottom + output->height - output->border.top) / 2.0), 0);
 
 	weston_matrix_scale(&output->matrix,
-			    2.0 / (output->current->width + output->border.left + output->border.right),
-			    -2.0 / (output->current->height + output->border.top + output->border.bottom), 1);
+			    2.0 / (output->width + output->border.left + output->border.right),
+			    -2.0 / (output->height + output->border.top + output->border.bottom), 1);
+
+	weston_output_compute_transform(output);
 
 	if (output->zoom.active) {
 		magnification = 1 / (1 - output->zoom.spring_z.current);
@@ -3003,6 +3062,32 @@
 	output->dirty = 0;
 }
 
+static void
+weston_output_transform_init(struct weston_output *output, uint32_t transform)
+{
+	output->transform = transform;
+
+	switch (transform) {
+	case WL_OUTPUT_TRANSFORM_90:
+	case WL_OUTPUT_TRANSFORM_270:
+	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+		/* Swap width and height */
+		output->width = output->current->height;
+		output->height = output->current->width;
+		break;
+	case WL_OUTPUT_TRANSFORM_NORMAL:
+	case WL_OUTPUT_TRANSFORM_180:
+	case WL_OUTPUT_TRANSFORM_FLIPPED:
+	case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+		output->width = output->current->width;
+		output->height = output->current->height;
+		break;
+	default:
+		break;
+	}
+}
+
 WL_EXPORT void
 weston_output_move(struct weston_output *output, int x, int y)
 {
@@ -3011,13 +3096,13 @@
 
 	pixman_region32_init(&output->previous_damage);
 	pixman_region32_init_rect(&output->region, x, y,
-				  output->current->width,
-				  output->current->height);
+				  output->width,
+				  output->height);
 }
 
 WL_EXPORT void
 weston_output_init(struct weston_output *output, struct weston_compositor *c,
-		   int x, int y, int width, int height)
+		   int x, int y, int width, int height, uint32_t transform)
 {
 	output->compositor = c;
 	output->x = x;
@@ -3029,8 +3114,11 @@
 	output->mm_width = width;
 	output->mm_height = height;
 	output->dirty = 1;
-	output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
 
+	if (transform != WL_OUTPUT_TRANSFORM_NORMAL)
+		output->disable_planes++;
+
+	weston_output_transform_init(output, transform);
 	weston_output_init_zoom(output);
 
 	weston_output_move(output, x, y);
diff --git a/src/compositor.h b/src/compositor.h
index b8e767d..ac887ee 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -155,7 +155,8 @@
 	struct weston_compositor *compositor;
 	struct weston_matrix matrix;
 	struct wl_list animation_list;
-	int32_t x, y, mm_width, mm_height;
+	int32_t x, y, width, height;
+	int32_t mm_width, mm_height;
 	struct weston_border border;
 	pixman_region32_t region;
 	pixman_region32_t previous_damage;
@@ -678,7 +679,7 @@
 weston_output_move(struct weston_output *output, int x, int y);
 void
 weston_output_init(struct weston_output *output, struct weston_compositor *c,
-		   int x, int y, int width, int height);
+		   int x, int y, int width, int height, uint32_t transform);
 void
 weston_output_destroy(struct weston_output *output);
 
diff --git a/src/shell.c b/src/shell.c
index 4d6bc4f..2bd23fc 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1373,8 +1373,8 @@
 	edges = WL_SHELL_SURFACE_RESIZE_TOP|WL_SHELL_SURFACE_RESIZE_LEFT;
 
 	shsurf->client->send_configure(shsurf->surface, edges,
-				       shsurf->output->current->width,
-				       shsurf->output->current->height - panel_height);
+				       shsurf->output->width,
+				       shsurf->output->height - panel_height);
 
 	shsurf->next_type = SHELL_SURFACE_MAXIMIZED;
 }
@@ -1419,8 +1419,8 @@
 			create_black_surface(surface->compositor,
 					     surface,
 					     output->x, output->y,
-					     output->current->width,
-					     output->current->height);
+					     output->width,
+					     output->height);
 
 	wl_list_remove(&shsurf->fullscreen.black_surface->layer_link);
 	wl_list_insert(&surface->layer_link,
@@ -1436,23 +1436,23 @@
 		matrix = &shsurf->fullscreen.transform.matrix;
 		weston_matrix_init(matrix);
 
-		output_aspect = (float) output->current->width /
-			(float) output->current->height;
+		output_aspect = (float) output->width /
+			(float) output->height;
 		surface_aspect = (float) surface->geometry.width /
 			(float) surface->geometry.height;
 		if (output_aspect < surface_aspect)
-			scale = (float) output->current->width /
+			scale = (float) output->width /
 				(float) surface->geometry.width;
 		else
-			scale = (float) output->current->height /
+			scale = (float) output->height /
 				(float) surface->geometry.height;
 
 		weston_matrix_scale(matrix, scale, scale, 1);
 		wl_list_remove(&shsurf->fullscreen.transform.link);
 		wl_list_insert(&surface->geometry.transformation_list,
 			       &shsurf->fullscreen.transform.link);
-		x = output->x + (output->current->width - surface->geometry.width * scale) / 2;
-		y = output->y + (output->current->height - surface->geometry.height * scale) / 2;
+		x = output->x + (output->width - surface->geometry.width * scale) / 2;
+		y = output->y + (output->height - surface->geometry.height * scale) / 2;
 		weston_surface_set_position(surface, x, y);
 
 		break;
@@ -1466,8 +1466,8 @@
 			if (weston_output_switch_mode(output, &mode) == 0) {
 				weston_surface_configure(shsurf->fullscreen.black_surface, 
 					                 output->x, output->y,
-							 output->current->width,
-							 output->current->height);
+							 output->width,
+							 output->height);
 				weston_surface_set_position(surface, output->x, output->y);
 				break;
 			}
@@ -1498,8 +1498,8 @@
 			create_black_surface(surface->compositor,
 					     surface,
 					     output->x, output->y,
-					     output->current->width,
-					     output->current->height);
+					     output->width,
+					     output->height);
 
 	wl_list_remove(&shsurf->fullscreen.black_surface->layer_link);
 	wl_list_insert(&surface->layer_link,
@@ -1537,8 +1537,8 @@
 	shsurf->next_type = SHELL_SURFACE_FULLSCREEN;
 
 	shsurf->client->send_configure(shsurf->surface, 0,
-				       shsurf->output->current->width,
-				       shsurf->output->current->height);
+				       shsurf->output->width,
+				       shsurf->output->height);
 }
 
 static void
@@ -1915,8 +1915,8 @@
 	surface->output = output_resource->data;
 	desktop_shell_send_configure(resource, 0,
 				     surface_resource,
-				     surface->output->current->width,
-				     surface->output->current->height);
+				     surface->output->width,
+				     surface->output->height);
 }
 
 static void
@@ -1948,8 +1948,8 @@
 	surface->output = output_resource->data;
 	desktop_shell_send_configure(resource, 0,
 				     surface_resource,
-				     surface->output->current->width,
-				     surface->output->current->height);
+				     surface->output->width,
+				     surface->output->height);
 }
 
 static void
@@ -2567,9 +2567,8 @@
 static void
 center_on_output(struct weston_surface *surface, struct weston_output *output)
 {
-	struct weston_mode *mode = output->current;
-	GLfloat x = (mode->width - surface->buffer->width) / 2;
-	GLfloat y = (mode->height - surface->buffer->height) / 2;
+	GLfloat x = (output->width - surface->buffer->width) / 2;
+	GLfloat y = (output->height - surface->buffer->height) / 2;
 
 	weston_surface_configure(surface, output->x + x, output->y + y,
 				 surface->buffer->width,
@@ -2618,8 +2617,8 @@
 	 * output.
 	 */
 	panel_height = get_output_panel_height(shell, target_output);
-	range_x = target_output->current->width - surface->geometry.width;
-	range_y = (target_output->current->height - panel_height) -
+	range_x = target_output->width - surface->geometry.width;
+	range_y = (target_output->height - panel_height) -
 		  surface->geometry.height;
 
 	if (range_x > 0)
diff --git a/src/zoom.c b/src/zoom.c
index 56a9ead..674a80e 100644
--- a/src/zoom.c
+++ b/src/zoom.c
@@ -187,14 +187,58 @@
 	float level = output->zoom.spring_z.current;
 	wl_fixed_t offset_x = wl_fixed_from_int(output->x);
 	wl_fixed_t offset_y = wl_fixed_from_int(output->y);
-	wl_fixed_t w = wl_fixed_from_int(output->current->width);
-	wl_fixed_t h = wl_fixed_from_int(output->current->height);
+	wl_fixed_t w = wl_fixed_from_int(output->width);
+	wl_fixed_t h = wl_fixed_from_int(output->height);
 
 	*x -= ((((*x - offset_x) / (float) w) - 0.5) * (w * (1.0 - level)));
 	*y -= ((((*y - offset_y) / (float) h) - 0.5) * (h * (1.0 - level)));
 }
 
 static void
+weston_zoom_apply_output_transform(struct weston_output *output,
+						float *x, float *y)
+{
+	float tx, ty;
+
+	switch(output->transform) {
+	case WL_OUTPUT_TRANSFORM_NORMAL:
+	default:
+		return;
+	case WL_OUTPUT_TRANSFORM_90:
+		tx = -*y;
+		ty = *x;
+		break;
+	case WL_OUTPUT_TRANSFORM_180:
+		tx = -*x;
+		ty = -*y;
+		break;
+	case WL_OUTPUT_TRANSFORM_270:
+		tx = *y;
+		ty = -*x;
+		break;
+	case WL_OUTPUT_TRANSFORM_FLIPPED:
+		tx = -*x;
+		ty = *y;
+		break;
+	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+		tx = -*y;
+		ty = -*x;
+		break;
+	case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+		tx = *x;
+		ty = -*y;
+		break;
+	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+		tx = *y;
+		ty = *x;
+		break;
+	}
+
+	*x = tx;
+	*y = ty;
+}
+
+static void
 weston_output_update_zoom_transform(struct weston_output *output)
 {
 	uint32_t type = output->zoom.type;
@@ -218,12 +262,15 @@
 	global_y = wl_fixed_to_double(y);
 
 	output->zoom.trans_x =
-		((((global_x - output->x) / output->current->width) *
+		((((global_x - output->x) / output->width) *
 		(level * 2)) - level) * ratio;
 	output->zoom.trans_y =
-		((((global_y - output->y) / output->current->height) *
+		((((global_y - output->y) / output->height) *
 		(level * 2)) - level) * ratio;
 
+	weston_zoom_apply_output_transform(output, &output->zoom.trans_x,
+						   &output->zoom.trans_y);
+
 	trans_max = level * 2 - level;
 	trans_min = -trans_max;
 
diff --git a/weston.ini b/weston.ini
index 3fda31b..99d24dd 100644
--- a/weston.ini
+++ b/weston.ini
@@ -37,11 +37,14 @@
 #[output]
 #name=LVDS1
 #mode=1680x1050
+#transform=90
 
 #[output]
 #name=VGA1
 #mode=173.00  1920 2048 2248 2576  1080 1083 1088 1120 -hsync +vsync
+#transform=flipped
 
 #[output]
 #name=X1
 #mode=1024x768
+#transform=flipped-270