diff --git a/src/Makefile.am b/src/Makefile.am index 2969f2ea0c1783f3faa7457424c7455abb165595..ae4b1256126dc986f505a3842714b2d07283f3fa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -95,6 +95,8 @@ mutter_built_sources += \ text-input-unstable-v3-server-protocol.h \ gtk-text-input-protocol.c \ gtk-text-input-server-protocol.h \ + viewporter-protocol.c \ + viewporter-server-protocol.h \ $(NULL) if HAVE_WAYLAND_EGLSTREAM @@ -455,6 +457,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES += \ wayland/meta-wayland-touch.h \ wayland/meta-wayland-surface.c \ wayland/meta-wayland-surface.h \ + wayland/meta-wayland-surface-helper.c \ + wayland/meta-wayland-surface-helper.h \ wayland/meta-wayland-cursor-surface.c \ wayland/meta-wayland-cursor-surface.h \ wayland/meta-wayland-tablet-cursor-surface.c \ @@ -493,6 +497,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES += \ wayland/meta-wayland-inhibit-shortcuts-dialog.h \ wayland/meta-xwayland-grab-keyboard.c \ wayland/meta-xwayland-grab-keyboard.h \ + wayland/meta-wayland-viewporter.c \ + wayland/meta-wayland-viewporter.h \ $(NULL) if HAVE_WAYLAND_EGLSTREAM diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h index 284f43e3645234ca7c0de55f348d13d24015f446..28f3c19056faa0445f26df03fbd0746f338076fb 100644 --- a/src/compositor/meta-shaped-texture-private.h +++ b/src/compositor/meta-shaped-texture-private.h @@ -28,6 +28,7 @@ #define __META_SHAPED_TEXTURE_PRIVATE_H__ #include "meta/meta-shaped-texture.h" +#include "backends/meta-monitor-manager-private.h" ClutterActor *meta_shaped_texture_new (void); void meta_shaped_texture_set_texture (MetaShapedTexture *stex, @@ -42,4 +43,15 @@ void meta_shaped_texture_set_fallback_size (MetaShapedTexture *stex, gboolean meta_shaped_texture_is_obscured (MetaShapedTexture *self); cairo_region_t * meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex); +void meta_shaped_texture_set_transform (MetaShapedTexture *stex, + MetaMonitorTransform transform); +void meta_shaped_texture_set_viewport_src_rect (MetaShapedTexture *stex, + float src_x, + float src_y, + float src_width, + float src_height); +void meta_shaped_texture_set_viewport_dest (MetaShapedTexture *stex, + int dest_width, + int dest_height); + #endif diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index 919e7a9b579e2b036cc26da69b57226939a5f701..11d4f2dd34e5c98996452f13ccd484dea4186f64 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -103,8 +103,17 @@ struct _MetaShapedTexturePrivate cairo_region_t *clip_region; cairo_region_t *unobscured_region; + gboolean size_invalid; + MetaMonitorTransform transform; + gboolean has_viewport_src; + float viewport_src_x, viewport_src_y; + float viewport_src_width, viewport_src_height; + gboolean has_viewport_dest; + int viewport_dest_width, viewport_dest_height; + int tex_width, tex_height; int fallback_width, fallback_height; + int dest_width, dest_height; gint64 prev_invalidation, last_invalidation; guint fast_updates; @@ -139,12 +148,23 @@ meta_shaped_texture_class_init (MetaShapedTextureClass *klass) G_TYPE_NONE, 0); } +static void +invalidate_size (MetaShapedTexture *stex) + +{ + MetaShapedTexturePrivate *priv = stex->priv; + + priv->size_invalid = TRUE; +} + static void meta_shaped_texture_init (MetaShapedTexture *self) { MetaShapedTexturePrivate *priv; + ClutterActor *actor; priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self); + actor = CLUTTER_ACTOR (self); priv->paint_tower = meta_texture_tower_new (); @@ -152,6 +172,77 @@ meta_shaped_texture_init (MetaShapedTexture *self) priv->mask_texture = NULL; priv->create_mipmaps = TRUE; priv->is_y_inverted = TRUE; + priv->transform = META_MONITOR_TRANSFORM_NORMAL; + + g_signal_connect (actor, + "notify::scale-x", + G_CALLBACK(invalidate_size), + self); +} + +static void +update_size (MetaShapedTexture *stex) +{ + MetaShapedTexturePrivate *priv = stex->priv; + int dest_width; + int dest_height; + + if (priv->has_viewport_dest) + { + ClutterActor *actor; + double tex_scale; + + actor = CLUTTER_ACTOR (stex); + clutter_actor_get_scale (actor, &tex_scale, NULL); + + dest_width = priv->viewport_dest_width / tex_scale; + dest_height = priv->viewport_dest_height / tex_scale; + } + else if (priv->has_viewport_src) + { + ClutterActor *actor; + double tex_scale; + + actor = CLUTTER_ACTOR (stex); + clutter_actor_get_scale (actor, &tex_scale, NULL); + + dest_width = priv->viewport_src_width / tex_scale; + dest_height = priv->viewport_src_height / tex_scale; + } + else + { + if (meta_monitor_transform_is_rotated (priv->transform)) + { + dest_width = priv->tex_height; + dest_height = priv->tex_width; + } + else + { + dest_width = priv->tex_width; + dest_height = priv->tex_height; + } + } + + priv->size_invalid = FALSE; + + if (priv->dest_width != dest_width || + priv->dest_height != dest_height) + { + priv->dest_width = dest_width; + priv->dest_height = dest_height; + meta_shaped_texture_set_mask_texture (stex, NULL); + clutter_actor_queue_relayout (CLUTTER_ACTOR (stex)); + g_signal_emit (stex, signals[SIZE_CHANGED], 0); + } +} + +static void +ensure_size_valid (MetaShapedTexture *stex) +{ + MetaShapedTexturePrivate *priv = stex->priv; + + if (priv->size_invalid) + update_size (stex); } static void @@ -167,8 +258,9 @@ set_unobscured_region (MetaShapedTexture *self, if (priv->texture) { - width = priv->tex_width; - height = priv->tex_height; + ensure_size_valid (self); + width = priv->dest_width; + height = priv->dest_height; } else { @@ -262,6 +354,46 @@ get_base_pipeline (MetaShapedTexture *stex, cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); } + if (priv->transform != META_MONITOR_TRANSFORM_NORMAL) + { + CoglMatrix matrix; + CoglEuler euler; + + cogl_matrix_init_translation (&matrix, 0.5, 0.5, 0.0); + switch (priv->transform) + { + default: + cogl_euler_init (&euler, 0.0, 0.0, 0.0); + break; + case META_MONITOR_TRANSFORM_90: + cogl_euler_init (&euler, 0.0, 0.0, 90.0); + break; + case META_MONITOR_TRANSFORM_180: + cogl_euler_init (&euler, 0.0, 0.0, 180.0); + break; + case META_MONITOR_TRANSFORM_270: + cogl_euler_init (&euler, 0.0, 0.0, 270.0); + break; + case META_MONITOR_TRANSFORM_FLIPPED: + cogl_euler_init (&euler, 180.0, 0.0, 0.0); + break; + case META_MONITOR_TRANSFORM_FLIPPED_90: + cogl_euler_init (&euler, 0.0, 180.0, 90.0); + break; + case META_MONITOR_TRANSFORM_FLIPPED_180: + cogl_euler_init (&euler, 180.0, 0.0, 180.0); + break; + case META_MONITOR_TRANSFORM_FLIPPED_270: + cogl_euler_init (&euler, 0.0, 180.0, 270.0); + break; + } + cogl_matrix_rotate_euler (&matrix, &euler); + cogl_matrix_translate (&matrix, -0.5, -0.5, 0.0); + + cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); + cogl_pipeline_set_layer_matrix (pipeline, 1, &matrix); + } + if (priv->snippet) cogl_pipeline_add_layer_snippet (pipeline, 0, priv->snippet); @@ -324,20 +456,64 @@ static void paint_clipped_rectangle (CoglFramebuffer *fb, CoglPipeline *pipeline, cairo_rectangle_int_t *rect, - ClutterActorBox *alloc) + ClutterActorBox *alloc, + MetaShapedTexture *stex) { + MetaShapedTexturePrivate *priv = stex->priv; float coords[8]; float x1, y1, x2, y2; + float alloc_width; + float alloc_height; x1 = rect->x; y1 = rect->y; x2 = rect->x + rect->width; y2 = rect->y + rect->height; + alloc_width = alloc->x2 - alloc->x1; + alloc_height = alloc->y2 - alloc->y1; + + if (priv->has_viewport_src) + { + double tex_scale; + float src_x; + float src_y; + float src_width; + float src_height; - coords[0] = rect->x / (alloc->x2 - alloc->x1); - coords[1] = rect->y / (alloc->y2 - alloc->y1); - coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1); - coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1); + clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL); + + src_x = priv->viewport_src_x / tex_scale; + src_y = priv->viewport_src_y / tex_scale; + src_width = priv->viewport_src_width / tex_scale; + src_height = priv->viewport_src_height / tex_scale; + + coords[0] = rect->x * src_width / alloc_width + src_x; + coords[1] = rect->y * src_height / alloc_height + src_y; + coords[2] = rect->width * src_width / alloc_width + coords[0]; + coords[3] = rect->height * src_height / alloc_height + coords[1]; + + if (meta_monitor_transform_is_rotated (priv->transform)) + { + coords[0] /= priv->tex_height; + coords[1] /= priv->tex_width; + coords[2] /= priv->tex_height; + coords[3] /= priv->tex_width; + } + else + { + coords[0] /= priv->tex_width; + coords[1] /= priv->tex_height; + coords[2] /= priv->tex_width; + coords[3] /= priv->tex_height; + } + } + else + { + coords[0] = rect->x / alloc_width; + coords[1] = rect->y / alloc_height; + coords[2] = (rect->x + rect->width) / alloc_width; + coords[3] = (rect->y + rect->height) / alloc_height; + } coords[4] = coords[0]; coords[5] = coords[1]; @@ -382,9 +558,7 @@ set_cogl_texture (MetaShapedTexture *stex, { priv->tex_width = width; priv->tex_height = height; - meta_shaped_texture_set_mask_texture (stex, NULL); - clutter_actor_queue_relayout (CLUTTER_ACTOR (stex)); - g_signal_emit (stex, signals[SIZE_CHANGED], 0); + update_size (stex); } /* NB: We don't queue a redraw of the actor here because we don't @@ -417,7 +591,7 @@ meta_shaped_texture_paint (ClutterActor *actor) MetaShapedTexture *stex = (MetaShapedTexture *) actor; MetaShapedTexturePrivate *priv = stex->priv; double tex_scale; - int tex_width, tex_height; + int dest_width, dest_height; cairo_rectangle_int_t tex_rect; guchar opacity; gboolean use_opaque_region; @@ -482,13 +656,14 @@ meta_shaped_texture_paint (ClutterActor *actor) } clutter_actor_get_scale (actor, &tex_scale, NULL); - tex_width = priv->tex_width; - tex_height = priv->tex_height; + ensure_size_valid (stex); + dest_width = priv->dest_width; + dest_height = priv->dest_height; - if (tex_width == 0 || tex_height == 0) /* no contents yet */ + if (dest_width == 0 || dest_height == 0) /* no contents yet */ return; - tex_rect = (cairo_rectangle_int_t) { 0, 0, tex_width, tex_height }; + tex_rect = (cairo_rectangle_int_t) { 0, 0, dest_width, dest_height }; /* Use nearest-pixel interpolation if the texture is unscaled. This * improves performance, especially with software rendering. @@ -496,7 +671,7 @@ meta_shaped_texture_paint (ClutterActor *actor) filter = COGL_PIPELINE_FILTER_LINEAR; - if (meta_actor_painting_untransformed (tex_width, tex_height, NULL, NULL)) + if (meta_actor_painting_untransformed (dest_width, dest_height, NULL, NULL)) filter = COGL_PIPELINE_FILTER_NEAREST; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); @@ -593,7 +768,11 @@ meta_shaped_texture_paint (ClutterActor *actor) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); - paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc); + paint_clipped_rectangle (fb, + opaque_pipeline, + &rect, + &alloc, + stex); } } @@ -646,16 +825,21 @@ meta_shaped_texture_paint (ClutterActor *actor) if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect)) continue; - paint_clipped_rectangle (fb, blended_pipeline, &rect, &alloc); + paint_clipped_rectangle (fb, + blended_pipeline, + &rect, + &alloc, + stex); } } else { /* 3) blended_tex_region is NULL. Do a full paint. */ - cogl_framebuffer_draw_rectangle (fb, blended_pipeline, - 0, 0, - alloc.x2 - alloc.x1, - alloc.y2 - alloc.y1); + paint_clipped_rectangle (fb, + blended_pipeline, + &tex_rect, + &alloc, + stex); } } @@ -670,13 +854,19 @@ meta_shaped_texture_get_preferred_width (ClutterActor *self, gfloat *min_width_p, gfloat *natural_width_p) { - MetaShapedTexturePrivate *priv = META_SHAPED_TEXTURE (self)->priv; + MetaShapedTexture *stex = META_SHAPED_TEXTURE (self); + MetaShapedTexturePrivate *priv = stex->priv; int width; if (priv->texture) - width = priv->tex_width; + { + ensure_size_valid (stex); + width = priv->dest_width; + } else - width = priv->fallback_width; + { + width = priv->fallback_width; + } if (min_width_p) *min_width_p = width; @@ -690,13 +880,19 @@ meta_shaped_texture_get_preferred_height (ClutterActor *self, gfloat *min_height_p, gfloat *natural_height_p) { - MetaShapedTexturePrivate *priv = META_SHAPED_TEXTURE (self)->priv; + MetaShapedTexture *stex = META_SHAPED_TEXTURE (self); + MetaShapedTexturePrivate *priv = stex->priv; int height; if (priv->texture) - height = priv->tex_height; + { + ensure_size_valid (stex); + height = priv->dest_height; + } else - height = priv->fallback_height; + { + height = priv->fallback_height; + } if (min_height_p) *min_height_p = height; @@ -958,6 +1154,52 @@ meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex) return priv->opaque_region; } +void +meta_shaped_texture_set_transform (MetaShapedTexture *stex, + MetaMonitorTransform transform) +{ + MetaShapedTexturePrivate *priv = stex->priv; + + if (priv->transform == transform) + return; + + priv->transform = transform; + + meta_shaped_texture_reset_pipelines (stex); + invalidate_size (stex); +} + +void +meta_shaped_texture_set_viewport_src_rect (MetaShapedTexture *stex, + float src_x, + float src_y, + float src_width, + float src_height) + +{ + MetaShapedTexturePrivate *priv = stex->priv; + + priv->viewport_src_x = src_x; + priv->viewport_src_y = src_y; + priv->viewport_src_width = src_width; + priv->viewport_src_height = src_height; + priv->has_viewport_src = src_width > 0; + invalidate_size (stex); +} + +void +meta_shaped_texture_set_viewport_dest (MetaShapedTexture *stex, + int dest_width, + int dest_height) +{ + MetaShapedTexturePrivate *priv = stex->priv; + + priv->viewport_dest_width = dest_width; + priv->viewport_dest_height = dest_height; + priv->has_viewport_dest = dest_width > 0; + invalidate_size (stex); +} + /** * meta_shaped_texture_get_image: * @stex: A #MetaShapedTexture diff --git a/src/meson.build b/src/meson.build index fcad37870f3eac1cb4c3a994875861d43d89e6a6..1573c24a7d51f9c2f6cd220315ebb055a6663679 100644 --- a/src/meson.build +++ b/src/meson.build @@ -478,6 +478,8 @@ if have_wayland 'wayland/meta-wayland-subsurface.h', 'wayland/meta-wayland-surface.c', 'wayland/meta-wayland-surface.h', + 'wayland/meta-wayland-surface-helper.c', + 'wayland/meta-wayland-surface-helper.h', 'wayland/meta-wayland-tablet.c', 'wayland/meta-wayland-tablet-cursor-surface.c', 'wayland/meta-wayland-tablet-cursor-surface.h', @@ -504,6 +506,8 @@ if have_wayland 'wayland/meta-wayland-touch.h', 'wayland/meta-wayland-types.h', 'wayland/meta-wayland-versions.h', + 'wayland/meta-wayland-viewporter.c', + 'wayland/meta-wayland-viewporter.h', 'wayland/meta-wayland-wl-shell.c', 'wayland/meta-wayland-wl-shell.h', 'wayland/meta-wayland-xdg-foreign.c', @@ -644,6 +648,7 @@ if have_wayland ['relative-pointer', 'unstable', 'v1', ], ['tablet', 'unstable', 'v2', ], ['text-input', 'unstable', 'v3', ], + ['viewporter', 'stable', ], ['xdg-foreign', 'unstable', 'v1', ], ['xdg-output', 'unstable', 'v1', ], ['xdg-shell', 'unstable', 'v6', ], diff --git a/src/wayland/meta-wayland-surface-helper.c b/src/wayland/meta-wayland-surface-helper.c new file mode 100644 index 0000000000000000000000000000000000000000..a6c123ab1182a19d74eb6c34fcc5f595dcc58918 --- /dev/null +++ b/src/wayland/meta-wayland-surface-helper.c @@ -0,0 +1,253 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "wayland/meta-wayland-surface-helper.h" + +static void +transformed_coord (int width, + int height, + MetaMonitorTransform transform, + int scale, + float sx, + float sy, + float *bx, + float *by) +{ + switch (transform) + { + default: + case META_MONITOR_TRANSFORM_NORMAL: + *bx = sx; + *by = sy; + break; + case META_MONITOR_TRANSFORM_90: + *bx = width - sy; + *by = sx; + break; + case META_MONITOR_TRANSFORM_180: + *bx = width - sx; + *by = height - sy; + break; + case META_MONITOR_TRANSFORM_270: + *bx = sy; + *by = height - sx; + break; + case META_MONITOR_TRANSFORM_FLIPPED: + *bx = width - sx; + *by = sy; + break; + case META_MONITOR_TRANSFORM_FLIPPED_90: + *bx = width - sy; + *by = height - sx; + break; + case META_MONITOR_TRANSFORM_FLIPPED_180: + *bx = sx; + *by = height - sy; + break; + case META_MONITOR_TRANSFORM_FLIPPED_270: + *bx = sy; + *by = sx; + break; + } + + *bx *= scale; + *by *= scale; +} + +static cairo_rectangle_int_t +transformed_rect (int width, + int height, + MetaMonitorTransform transform, + int scale, + cairo_rectangle_int_t rect) +{ + cairo_rectangle_int_t ret; + float x1; + float x2; + float y1; + float y2; + + transformed_coord(width, + height, + transform, + scale, + rect.x, + rect.y, + &x1, + &y1); + transformed_coord(width, + height, + transform, + scale, + rect.x + rect.width, + rect.y + rect.height, + &x2, + &y2); + + if (x1 <= x2) + { + ret.x = x1; + ret.width = x2 - x1; + } + else + { + ret.x = x2; + ret.width = x1 - x2; + } + + if (y1 <= y2) + { + ret.y = y1; + ret.height = y2; + } + else + { + ret.y = y2; + ret.height = y1 - y2; + } + + return ret; +} + +static void +surface_to_buffer_coordinate (MetaWaylandSurface *surface, + float sx, + float sy, + float *bx, + float *by) +{ + if (!surface->has_viewport_src_rect) + { + *bx = sx; + *by = sy; + } + else + { + float surface_width; + float surface_height; + + if (surface->has_viewport_dest) + { + surface_width = surface->viewport_dest_width; + surface_height = surface->viewport_dest_height; + } + else + { + surface_width = meta_wayland_surface_get_buffer_width (surface) / + surface->scale; + surface_height = meta_wayland_surface_get_buffer_height (surface) / + surface->scale; + } + + *bx = sx * surface->viewport_src_width / surface_width + + surface->viewport_src_x; + *by = sy * surface->viewport_src_height / surface_height + + surface->viewport_src_y; + } +} + +cairo_region_t * +meta_wayland_surface_helper_surface_to_buffer_region (MetaWaylandSurface *surface, + cairo_region_t *region) +{ + int n_rects, i; + cairo_rectangle_int_t *rects; + cairo_rectangle_int_t surface_rect; + cairo_region_t *scaled_region; + float x1; + float x2; + float y1; + float y2; + + if (surface->scale == 1 && + surface->viewport_src_width <= 0 && + surface->viewport_dest_width <= 0 && + surface->buffer_transform == META_MONITOR_TRANSFORM_NORMAL) + { + return cairo_region_copy (region); + } + + n_rects = cairo_region_num_rectangles (region); + + rects = g_malloc (sizeof(cairo_rectangle_int_t) * n_rects); + + for (i = 0; i < n_rects; i++) + { + int width; + int height; + + cairo_region_get_rectangle (region, i, &rects[i]); + + surface_to_buffer_coordinate (surface, rects[i].x, rects[i].y, &x1, &y1); + surface_to_buffer_coordinate (surface, rects[i].x + rects[i].width, + rects[i].y + rects[i].height, &x2, &y2); + rects[i].x = floorf(x1); + rects[i].y = floorf(y1); + rects[i].width = ceilf(x2) - rects[i].x; + rects[i].height = ceilf(y2) - rects[i].y; + + width = meta_wayland_surface_get_buffer_width (surface) / surface->scale; + height = meta_wayland_surface_get_buffer_height (surface) / surface->scale; + + rects[i] = transformed_rect(width, + height, + surface->buffer_transform, + surface->scale, + rects[i]); + } + + scaled_region = cairo_region_create_rectangles (rects, n_rects); + + /* Intersect the scaled region to make sure no rounding errors made + * it to big */ + surface_rect = (cairo_rectangle_int_t) { + .width = meta_wayland_surface_get_buffer_width (surface), + .height = meta_wayland_surface_get_buffer_height (surface), + }; + cairo_region_intersect_rectangle (scaled_region, &surface_rect); + g_free (rects); + + return scaled_region; +} + +MetaMonitorTransform +meta_wayland_surface_helper_transform_from_wl_output_transform (int32_t transform_value) +{ + enum wl_output_transform transform = transform_value; + + switch (transform) + { + case WL_OUTPUT_TRANSFORM_NORMAL: + return META_MONITOR_TRANSFORM_NORMAL; + case WL_OUTPUT_TRANSFORM_90: + return META_MONITOR_TRANSFORM_90; + case WL_OUTPUT_TRANSFORM_180: + return META_MONITOR_TRANSFORM_180; + case WL_OUTPUT_TRANSFORM_270: + return META_MONITOR_TRANSFORM_270; + case WL_OUTPUT_TRANSFORM_FLIPPED: + return META_MONITOR_TRANSFORM_FLIPPED; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + return META_MONITOR_TRANSFORM_FLIPPED_90; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + return META_MONITOR_TRANSFORM_FLIPPED_180; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + return META_MONITOR_TRANSFORM_FLIPPED_270; + default: + return -1; + } +} diff --git a/src/wayland/meta-wayland-surface-helper.h b/src/wayland/meta-wayland-surface-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..fb3f4851609decc85a35e9680295b72e2375ec13 --- /dev/null +++ b/src/wayland/meta-wayland-surface-helper.h @@ -0,0 +1,28 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_WAYLAND_SURFACE_HELPER_H +#define META_WAYLAND_SURFACE_HELPER_H + +#include "wayland/meta-wayland-surface.h" + +cairo_region_t * meta_wayland_surface_helper_surface_to_buffer_region (MetaWaylandSurface *surface, + cairo_region_t *region); + +MetaMonitorTransform meta_wayland_surface_helper_transform_from_wl_output_transform (int32_t transform_value); + +#endif diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index b918e296e264370e9b0ea1574118f0d43e9315b7..af4e155237db837087b3d83a673e0a338b2e0615 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -23,6 +23,7 @@ #include "config.h" #include "wayland/meta-wayland-surface.h" +#include "wayland/meta-wayland-surface-helper.h" #include #include @@ -32,7 +33,6 @@ #include "clutter/wayland/clutter-wayland-compositor.h" #include "clutter/wayland/clutter-wayland-surface.h" #include "cogl/cogl-wayland-server.h" -#include "compositor/meta-shaped-texture-private.h" #include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-surface-actor.h" #include "compositor/meta-window-actor-private.h" @@ -50,6 +50,7 @@ #include "wayland/meta-wayland-region.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-subsurface.h" +#include "wayland/meta-wayland-viewporter.h" #include "wayland/meta-wayland-wl-shell.h" #include "wayland/meta-wayland-xdg-shell.h" #include "wayland/meta-window-wayland.h" @@ -252,7 +253,7 @@ surface_process_damage (MetaWaylandSurface *surface, cairo_region_t *surface_region, cairo_region_t *buffer_region) { - MetaWaylandBuffer *buffer = surface->buffer_ref.buffer; + MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface); cairo_rectangle_int_t surface_rect; cairo_region_t *scaled_region; int i, n_rectangles; @@ -275,7 +276,8 @@ surface_process_damage (MetaWaylandSurface *surface, /* The damage region must be in the same coordinate space as the buffer, * i.e. scaled with surface->scale. */ - scaled_region = meta_region_scale (surface_region, surface->scale); + scaled_region = meta_wayland_surface_helper_surface_to_buffer_region (surface, + surface_region); /* Now add the buffer damage on top of the scaled damage region, as buffer * damage is already in that scale. */ @@ -394,6 +396,12 @@ pending_state_init (MetaWaylandPendingState *state) state->has_new_geometry = FALSE; state->has_new_min_size = FALSE; state->has_new_max_size = FALSE; + + state->has_new_buffer_transform = FALSE; + state->buffer_transform = META_MONITOR_TRANSFORM_NORMAL; + + state->has_new_viewport_src_rect = FALSE; + state->has_new_viewport_dest = FALSE; } static void @@ -501,6 +509,28 @@ merge_pending_state (MetaWaylandPendingState *from, if (from->scale > 0) to->scale = from->scale; + if (from->has_new_buffer_transform) + { + to->buffer_transform = from->buffer_transform; + to->has_new_buffer_transform = TRUE; + } + + if (from->has_new_viewport_src_rect) + { + to->viewport_src_x = from->viewport_src_x; + to->viewport_src_y = from->viewport_src_y; + to->viewport_src_width = from->viewport_src_width; + to->viewport_src_height = from->viewport_src_height; + to->has_new_viewport_src_rect = TRUE; + } + + if (from->has_new_viewport_dest) + { + to->viewport_dest_width = from->viewport_dest_width; + to->viewport_dest_height = from->viewport_dest_height; + to->has_new_viewport_dest = TRUE; + } + if (to->buffer && to->buffer_destroy_handler_id == 0) { to->buffer_destroy_handler_id = @@ -676,6 +706,64 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface *surface, if (pending->scale > 0) surface->scale = pending->scale; + if (pending->has_new_buffer_transform) + { + MetaSurfaceActor *actor = meta_wayland_surface_get_actor (surface); + + if (actor) + { + MetaShapedTexture *stex; + + surface->buffer_transform = pending->buffer_transform; + + stex = meta_surface_actor_get_texture (actor); + meta_shaped_texture_set_transform (stex, + surface->buffer_transform); + } + } + + if (pending->has_new_viewport_src_rect) + { + MetaSurfaceActor *actor = meta_wayland_surface_get_actor (surface); + + if (actor) + { + MetaShapedTexture *stex; + + surface->viewport_src_x = pending->viewport_src_x; + surface->viewport_src_y = pending->viewport_src_y; + surface->viewport_src_width = pending->viewport_src_width; + surface->viewport_src_height = pending->viewport_src_height; + surface->has_viewport_src_rect = surface->viewport_src_width > 0; + + stex = meta_surface_actor_get_texture (actor); + meta_shaped_texture_set_viewport_src_rect (stex, + surface->viewport_src_x, + surface->viewport_src_y, + surface->viewport_src_width, + surface->viewport_src_height); + } + } + + if (pending->has_new_viewport_dest) + { + MetaSurfaceActor *actor = meta_wayland_surface_get_actor (surface); + + if (actor) + { + MetaShapedTexture *stex; + + surface->viewport_dest_width = pending->viewport_dest_width; + surface->viewport_dest_height = pending->viewport_dest_height; + surface->has_viewport_dest = surface->viewport_dest_height > 0; + + stex = meta_surface_actor_get_texture (actor); + meta_shaped_texture_set_viewport_dest (stex, + surface->viewport_dest_width, + surface->viewport_dest_height); + } + } + if (meta_wayland_surface_get_actor (surface) && (!cairo_region_is_empty (pending->surface_damage) || !cairo_region_is_empty (pending->buffer_damage))) @@ -921,11 +1009,30 @@ wl_surface_commit (struct wl_client *client, } static void -wl_surface_set_buffer_transform (struct wl_client *client, +wl_surface_set_buffer_transform (struct wl_client *client, struct wl_resource *resource, - int32_t transform) + int32_t transform) { - g_warning ("TODO: support set_buffer_transform request"); + MetaWaylandSurface *surface = wl_resource_get_user_data (resource); + MetaWaylandPendingState *pending = surface->pending; + MetaMonitorTransform buffer_transform; + + buffer_transform = meta_wayland_surface_helper_transform_from_wl_output_transform (transform); + + if (buffer_transform == -1) + { + wl_resource_post_error (resource, + WL_SURFACE_ERROR_INVALID_TRANSFORM, + "Trying to set invalid buffer_transform of %d\n", + transform); + return; + } + + if (surface->buffer_transform != buffer_transform) + { + pending->buffer_transform = buffer_transform; + pending->has_new_buffer_transform = TRUE; + } } static void @@ -1285,6 +1392,7 @@ meta_wayland_shell_init (MetaWaylandCompositor *compositor) meta_wayland_legacy_xdg_shell_init (compositor); meta_wayland_wl_shell_init (compositor); meta_wayland_gtk_shell_init (compositor); + meta_wayland_viewporter_init (compositor); } void @@ -1744,13 +1852,60 @@ meta_wayland_surface_notify_geometry_changed (MetaWaylandSurface *surface) int meta_wayland_surface_get_width (MetaWaylandSurface *surface) { - MetaWaylandBuffer *buffer; + if (surface->has_viewport_dest) + { + return surface->viewport_dest_width; + } + else if (surface->has_viewport_src_rect) + { + return surface->viewport_src_width; + } + else + { + int width; + + if (meta_monitor_transform_is_rotated (surface->buffer_transform)) + width = meta_wayland_surface_get_buffer_height (surface); + else + width = meta_wayland_surface_get_buffer_width (surface); + + return width / surface->scale; + } +} + +int +meta_wayland_surface_get_height (MetaWaylandSurface *surface) +{ + if (surface->has_viewport_dest) + { + return surface->viewport_dest_height; + } + else if (surface->has_viewport_src_rect) + { + return surface->viewport_src_height; + } + else + { + int height; + + if (meta_monitor_transform_is_rotated (surface->buffer_transform)) + height = meta_wayland_surface_get_buffer_width (surface); + else + height = meta_wayland_surface_get_buffer_height (surface); + + return height / surface->scale; + } +} + +int +meta_wayland_surface_get_buffer_width (MetaWaylandSurface *surface) +{ + MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface); - buffer = surface->buffer_ref.buffer; if (buffer) { CoglTexture *texture = meta_wayland_buffer_get_texture (buffer); - return cogl_texture_get_width (texture) / surface->scale; + return cogl_texture_get_width (texture); } else { @@ -1759,15 +1914,14 @@ meta_wayland_surface_get_width (MetaWaylandSurface *surface) } int -meta_wayland_surface_get_height (MetaWaylandSurface *surface) +meta_wayland_surface_get_buffer_height (MetaWaylandSurface *surface) { - MetaWaylandBuffer *buffer; + MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface); - buffer = surface->buffer_ref.buffer; if (buffer) { CoglTexture *texture = meta_wayland_buffer_get_texture (buffer); - return cogl_texture_get_height (texture) / surface->scale; + return cogl_texture_get_height (texture); } else { diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h index f84d88061cf81dcfe66b7dfabab51e8ca1d2ff3b..a7213afb1f676c940b849be37e3c1b351082f719 100644 --- a/src/wayland/meta-wayland-surface.h +++ b/src/wayland/meta-wayland-surface.h @@ -27,6 +27,7 @@ #include "backends/meta-monitor-manager-private.h" #include "clutter/clutter.h" +#include "compositor/meta-shaped-texture-private.h" #include "compositor/meta-surface-actor.h" #include "meta/meta-cursor-tracker.h" #include "wayland/meta-wayland-pointer-constraints.h" @@ -85,7 +86,6 @@ struct _MetaWaylandPendingState gulong buffer_destroy_handler_id; int32_t dx; int32_t dy; - int scale; /* wl_surface.damage */ @@ -111,6 +111,17 @@ struct _MetaWaylandPendingState gboolean has_new_max_size; int new_max_width; int new_max_height; + + gboolean has_new_buffer_transform; + MetaMonitorTransform buffer_transform; + gboolean has_new_viewport_src_rect; + float viewport_src_x; + float viewport_src_y; + float viewport_src_width; + float viewport_src_height; + gboolean has_new_viewport_dest; + int viewport_dest_width; + int viewport_dest_height; }; struct _MetaWaylandDragDestFuncs @@ -144,6 +155,17 @@ struct _MetaWaylandSurface int32_t offset_x, offset_y; GList *subsurfaces; GHashTable *outputs_to_destroy_notify_id; + MetaMonitorTransform buffer_transform; + + struct wl_resource *viewport_resource; + gboolean has_viewport_src_rect; + float viewport_src_x; + float viewport_src_y; + float viewport_src_width; + float viewport_src_height; + gboolean has_viewport_dest; + int viewport_dest_width; + int viewport_dest_height; /* Buffer reference state. */ struct { @@ -307,4 +329,7 @@ void meta_wayland_surface_notify_geometry_changed (MetaWaylandSur int meta_wayland_surface_get_width (MetaWaylandSurface *surface); int meta_wayland_surface_get_height (MetaWaylandSurface *surface); +int meta_wayland_surface_get_buffer_width (MetaWaylandSurface *surface); +int meta_wayland_surface_get_buffer_height (MetaWaylandSurface *surface); + #endif diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h index 9e1139a9188f323b9b6bdcafbdcc99c935215cb0..857cec7dd3fe88fac0dc64338d9888057bd887ad 100644 --- a/src/wayland/meta-wayland-versions.h +++ b/src/wayland/meta-wayland-versions.h @@ -54,5 +54,6 @@ #define META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION 1 #define META_GTK_TEXT_INPUT_VERSION 1 #define META_ZWP_TEXT_INPUT_V3_VERSION 1 +#define META_WP_VIEWPORTER_VERSION 1 #endif diff --git a/src/wayland/meta-wayland-viewporter.c b/src/wayland/meta-wayland-viewporter.c new file mode 100644 index 0000000000000000000000000000000000000000..9de3a1d78a43916bd72a6ea3bf1e907ca4615d15 --- /dev/null +++ b/src/wayland/meta-wayland-viewporter.c @@ -0,0 +1,224 @@ +/* + * Wayland Support + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include +#include "meta-wayland-viewporter.h" +#include "meta-wayland-versions.h" +#include "meta-wayland-surface.h" +#include "meta-wayland-subsurface.h" +#include "meta-wayland-private.h" +#include "viewporter-server-protocol.h" + +static void +destroy_wl_viewport (struct wl_resource *resource) +{ + MetaWaylandSurface *surface = wl_resource_get_user_data (resource); + + if (!surface) + return; + + g_clear_object (&surface->viewport_resource); + + if (!surface->pending) + return; + + surface->pending->viewport_src_width = -1; + surface->pending->viewport_dest_width = -1; + surface->pending->has_new_viewport_src_rect = TRUE; + surface->pending->has_new_viewport_dest = TRUE; +} + +static void +viewport_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +viewport_set_source (struct wl_client *client, + struct wl_resource *resource, + wl_fixed_t src_x, + wl_fixed_t src_y, + wl_fixed_t src_width, + wl_fixed_t src_height) +{ + MetaWaylandSurface *surface; + float new_x; + float new_y; + float new_width; + float new_height; + + surface = wl_resource_get_user_data (resource); + if (!surface) + { + wl_resource_post_error (resource, + WP_VIEWPORT_ERROR_NO_SURFACE, + "wl_surface for this viewport is no longer exists"); + return; + } + + new_x = wl_fixed_to_double (src_x); + new_y = wl_fixed_to_double (src_y); + new_width = wl_fixed_to_double (src_width); + new_height = wl_fixed_to_double (src_height); + + if ((new_x >= 0 && new_y >= 0 && + new_width > 0 && new_height > 0) || + (new_x == -1 && new_y == -1 && + new_width == -1 && new_height == -1)) + { + if (new_x != surface->viewport_src_x || + new_y != surface->viewport_src_y || + new_width != surface->viewport_src_width || + new_height != surface->viewport_src_height) + { + surface->pending->viewport_src_x = new_x; + surface->pending->viewport_src_y = new_y; + surface->pending->viewport_src_width = new_width; + surface->pending->viewport_src_height = new_height; + surface->pending->has_new_viewport_src_rect = TRUE; + } + else + { + surface->pending->has_new_viewport_src_rect = FALSE; + } + } + else + { + wl_resource_post_error (resource, + WP_VIEWPORT_ERROR_BAD_VALUE, + "all values must be either positive or -1"); + } +} + +static void +viewport_set_destination (struct wl_client *client, + struct wl_resource *resource, + int dst_width, + int dst_height) +{ + MetaWaylandSurface *surface; + + surface = wl_resource_get_user_data (resource); + if (!surface) + { + wl_resource_post_error (resource, + WP_VIEWPORT_ERROR_NO_SURFACE, + "wl_surface for this viewport is no longer exists"); + return; + } + + if ((dst_width > 0 && dst_height > 0) || (dst_width == -1 && dst_height == -1)) + { + if (surface->viewport_dest_width != dst_width || + surface->viewport_dest_height != dst_height) + { + surface->pending->viewport_dest_width = dst_width; + surface->pending->viewport_dest_height = dst_height; + surface->pending->has_new_viewport_dest = TRUE; + } + else + { + surface->pending->has_new_viewport_dest = FALSE; + } + } + else + { + wl_resource_post_error (resource, + WP_VIEWPORT_ERROR_BAD_VALUE, + "all values must be either positive or -1"); + } +} + +static const struct wp_viewport_interface meta_wayland_viewport_interface = { + viewport_destroy, + viewport_set_source, + viewport_set_destination, +}; + +static void +viewporter_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +viewporter_get_viewport (struct wl_client *client, + struct wl_resource *master_resource, + uint32_t viewport_id, + struct wl_resource *surface_resource) +{ + struct wl_resource *resource; + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + + if (surface->viewport_resource) + { + wl_resource_post_error (master_resource, + WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS, + "viewport already exists on surface"); + return; + } + + resource = wl_resource_create (client, + &wp_viewport_interface, + wl_resource_get_version (master_resource), + viewport_id); + wl_resource_set_implementation (resource, + &meta_wayland_viewport_interface, + surface, + destroy_wl_viewport); + + surface->viewport_resource = resource; +} + +static const struct wp_viewporter_interface meta_wayland_viewporter_interface = { + viewporter_destroy, + viewporter_get_viewport, +}; + +static void +bind_viewporter (struct wl_client *client, + void *data, + guint32 version, + guint32 id) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, + &wp_viewporter_interface, + version, + id); + wl_resource_set_implementation (resource, + &meta_wayland_viewporter_interface, + data, + NULL); +} + +void +meta_wayland_viewporter_init (MetaWaylandCompositor *compositor) +{ + if (wl_global_create (compositor->wayland_display, + &wp_viewporter_interface, + META_WP_VIEWPORTER_VERSION, + compositor, bind_viewporter) == NULL) + g_error ("Failed to register a global wl-subcompositor object"); +} diff --git a/src/wayland/meta-wayland-viewporter.h b/src/wayland/meta-wayland-viewporter.h new file mode 100644 index 0000000000000000000000000000000000000000..6927f0937474863394e52cd23388702467323d8c --- /dev/null +++ b/src/wayland/meta-wayland-viewporter.h @@ -0,0 +1,28 @@ +/* + * Wayland Support + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef META_WAYLAND_VIEWPORTER_H +#define META_WAYLAND_VIEWPORTER_H + +#include "wayland/meta-wayland-types.h" + +void meta_wayland_viewporter_init (MetaWaylandCompositor *compositor); + +#endif /* META_WAYLAND_VIEWPORTER_H */