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 --- src/compositor-android.c | 3 +- src/compositor-drm.c | 79 +++++++++++++------ src/compositor-wayland.c | 3 +- src/compositor-x11.c | 199 ++++++++++++++++++++++++++++++++++++----------- src/compositor.c | 113 ++++++++++++++++++++++++++- src/compositor.h | 2 +- weston.ini | 3 + 7 files changed, 326 insertions(+), 76 deletions(-) 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 @@ android_compositor_add_output(struct android_compositor *compositor, 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..0911894 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 @@ enum output_config { struct drm_configured_output { char *name; char *mode; + uint32_t transform; int32_t width, height; drmModeModeInfo crtc_mode; enum output_config config; @@ -1528,7 +1530,8 @@ create_output_for_connector(struct drm_compositor *ec, } 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); @@ -2066,14 +2069,6 @@ evdev_input_destroy(struct weston_seat *seat_base) } static void -drm_free_configured_output(struct drm_configured_output *output) -{ - free(output->name); - free(output->mode); - free(output); -} - -static void drm_destroy(struct weston_compositor *ec) { struct drm_compositor *d = (struct drm_compositor *) ec; @@ -2083,7 +2078,7 @@ drm_destroy(struct weston_compositor *ec) wl_list_for_each_safe(seat, next, &ec->seat_list, link) evdev_input_destroy(seat); wl_list_for_each_safe(o, n, &configured_output_list, link) - drm_free_configured_output(o); + free(o); wl_event_source_remove(d->udev_drm_source); wl_event_source_remove(d->drm_source); @@ -2412,23 +2407,52 @@ check_for_modeline(struct drm_configured_output *output) } static void +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; + } +} + +static void output_section_done(void *data) { struct drm_configured_output *output; output = malloc(sizeof *output); - if (!output || !output_name || !output_mode) { - free(output_name); - output_name = NULL; - free(output_mode); - output_mode = NULL; - return; - } + if (!output || !output_name || !output_mode) + goto err_free; + + if (output_name[0] == 'X') + goto err_free; output->config = OUTPUT_CONFIG_INVALID; - output->name = output_name; - output->mode = output_mode; + output->name = strdup(output_name); + output->mode = strdup(output_mode); if (strcmp(output_mode, "off") == 0) output->config = OUTPUT_CONFIG_OFF; @@ -2441,13 +2465,21 @@ output_section_done(void *data) 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 { + if (output->config == OUTPUT_CONFIG_INVALID) { weston_log("Invalid mode \"%s\" for output %s\n", output_mode, output_name); - drm_free_configured_output(output); + goto err_free; } + + output_set_transform(output); + + wl_list_insert(&configured_output_list, &output->link); + +err_free: + free(output_name); + output_name = NULL; + free(output_mode); + output_mode = NULL; } WL_EXPORT struct weston_compositor * @@ -2471,6 +2503,7 @@ backend_init(struct wl_display *display, int argc, char *argv[], 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 @@ wayland_compositor_create_output(struct wayland_compositor *c, 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..9a700f1 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 @@ static struct wl_list configured_output_list; struct x11_configured_output { char *name; int width, height; + uint32_t transform; struct wl_list link; }; @@ -474,9 +476,12 @@ x11_output_set_icon(struct x11_compositor *c, static int 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 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y, 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 | @@ -517,7 +527,8 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y, 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 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y, /* 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); @@ -739,6 +750,98 @@ x11_compositor_deliver_button_event(struct x11_compositor *c, 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->current->width - 1); + wl_fixed_t height = wl_fixed_from_int(output->current->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 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data) 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 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data) 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_handle_event(int fd, uint32_t mask, void *data) 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 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data) 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: @@ -1031,20 +1115,13 @@ x11_restore(struct weston_compositor *ec) } static void -x11_free_configured_output(struct x11_configured_output *output) -{ - free(output->name); - free(output); -} - -static void x11_destroy(struct weston_compositor *ec) { struct x11_compositor *compositor = (struct x11_compositor *)ec; struct x11_configured_output *o, *n; wl_list_for_each_safe(o, n, &configured_output_list, link) - x11_free_configured_output(o); + free(o); wl_event_source_remove(compositor->xcb_source); x11_input_destroy(compositor); @@ -1063,8 +1140,6 @@ x11_compositor_create(struct wl_display *display, 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; xcb_screen_iterator_t s; @@ -1117,14 +1192,13 @@ x11_compositor_create(struct wl_display *display, 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) + o->name, o->transform) < 0) goto err_x11_input; x += option_width ? option_width : o->width; output_count++; @@ -1134,7 +1208,8 @@ x11_compositor_create(struct wl_display *display, for (i = output_count; i < count; i++) { if (x11_compositor_create_output(c, x, 0, width, height, - fullscreen, no_input, name) < 0) + fullscreen, no_input, NULL, + WL_OUTPUT_TRANSFORM_NORMAL) < 0) goto err_x11_input; x += width; } @@ -1160,37 +1235,68 @@ err_free: } static void +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); - output_name = NULL; + if (!output || !output_name || !output_mode) goto err_free; - } - output->name = output_name; + output->name = strdup(output_name); - if (output_name[0] != 'X') { - x11_free_configured_output(output); + if (output_name[0] != 'X') goto err_free; - } 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; } + output_set_transform(output); + wl_list_insert(configured_output_list.prev, &output->link); err_free: + free(output_name); + output_name = NULL; free(output_mode); output_mode = NULL; + free(output_transform); + output_transform = NULL; } WL_EXPORT struct weston_compositor * @@ -1215,6 +1321,7 @@ backend_init(struct wl_display *display, int argc, char *argv[], 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 7370435..98bcac5 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -1136,7 +1136,7 @@ weston_output_repaint(struct weston_output *output, uint32_t msecs) struct weston_frame_callback *cb, *cnext; struct wl_list frame_callback_list; pixman_region32_t opaque, output_damage; - int32_t width, height; + int32_t width, height, temp; weston_compositor_update_drag_surfaces(ec); @@ -1144,6 +1144,20 @@ weston_output_repaint(struct weston_output *output, uint32_t msecs) output->border.left + output->border.right; height = output->current->height + output->border.top + output->border.bottom; + + switch(output->transform) { + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + temp = width; + width = height; + height = temp; + break; + default: + break; + } + glViewport(0, 0, width, height); /* Rebuild the surface list and update surface transforms up front. */ @@ -2962,12 +2976,61 @@ weston_output_destroy(struct weston_output *output) 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) +{ + *flip = 1; + weston_matrix_init(transform); + + switch(output->transform) { + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + transform->d[0] = 0; + transform->d[1] = -1; + transform->d[4] = 1; + transform->d[5] = 0; + break; + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + transform->d[0] = 1; + 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] = 1; + transform->d[4] = -1; + transform->d[5] = 0; + break; + default: + break; + } + + switch(output->transform) { + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + *flip = -1; + break; + default: + break; + } +} + WL_EXPORT void weston_output_update_matrix(struct weston_output *output) { float magnification; struct weston_matrix camera; struct weston_matrix modelview; + struct weston_matrix transform; + int flip; + + weston_output_compute_transform(output, &transform, &flip); weston_matrix_init(&output->matrix); weston_matrix_translate(&output->matrix, @@ -2975,9 +3038,12 @@ weston_output_update_matrix(struct weston_output *output) -(output->y + (output->border.bottom + output->current->height - output->border.top) / 2.0), 0); weston_matrix_scale(&output->matrix, - 2.0 / (output->current->width + output->border.left + output->border.right), + flip * 2.0 / (output->current->width + output->border.left + output->border.right), -2.0 / (output->current->height + output->border.top + output->border.bottom), 1); + if (output->transform != WL_OUTPUT_TRANSFORM_NORMAL) + weston_matrix_multiply(&output->matrix, &transform); + if (output->zoom.active) { magnification = 1 / (1 - output->zoom.spring_z.current); weston_matrix_init(&camera); @@ -2993,6 +3059,39 @@ weston_output_update_matrix(struct weston_output *output) output->dirty = 0; } +static void +weston_output_transform_init(struct weston_output *output, + int *width, int *height, + uint32_t transform) +{ + int temp_w, temp_h; + + output->transform = WL_OUTPUT_TRANSFORM_NORMAL; + + 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 */ + temp_w = output->current->height; + temp_h = output->current->width; + output->current->width = temp_w; + output->current->height = temp_h; + temp_w = *height; + temp_h = *width; + *width = temp_w; + *height = temp_h; + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + output->transform = transform; + break; + default: + break; + } +} + WL_EXPORT void weston_output_move(struct weston_output *output, int x, int y) { @@ -3007,8 +3106,10 @@ weston_output_move(struct weston_output *output, int x, int y) 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) { + weston_output_transform_init(output, &width, &height, transform); + output->compositor = c; output->x = x; output->y = y; @@ -3019,7 +3120,11 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c, 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 = 1; + + output->transform = transform; weston_output_init_zoom(output); diff --git a/src/compositor.h b/src/compositor.h index 7a8058e..f856418 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -682,7 +682,7 @@ void 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/weston.ini b/weston.ini index 3fda31b..99d24dd 100644 --- a/weston.ini +++ b/weston.ini @@ -37,11 +37,14 @@ duration=600 #[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 -- 1.7.11.2 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel