diff --git a/src/meson.build b/src/meson.build index a3e579d9b..8779c956e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -523,6 +523,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', @@ -663,6 +665,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-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c index 56ea66e1e..2471de0a9 100644 --- a/src/wayland/meta-wayland-actor-surface.c +++ b/src/wayland/meta-wayland-actor-surface.c @@ -207,6 +207,27 @@ meta_wayland_actor_surface_real_sync_actor_state (MetaWaylandActorSurface *actor meta_surface_actor_set_transform (surface_actor, surface->buffer_transform); + if (surface->viewport.has_src_rect) + { + meta_surface_actor_set_viewport_src_rect (surface_actor, + &surface->viewport.src_rect); + } + else + { + meta_surface_actor_reset_viewport_src_rect (surface_actor); + } + + if (surface->viewport.has_dst_size) + { + meta_surface_actor_set_viewport_dst_size (surface_actor, + surface->viewport.dst_width, + surface->viewport.dst_height); + } + else + { + meta_surface_actor_reset_viewport_dst_size (surface_actor); + } + for (l = surface->subsurfaces; l; l = l->next) { MetaWaylandSurface *subsurface_surface = l->data; diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index f75fefd87..2351fedab 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -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" @@ -280,6 +281,8 @@ surface_process_damage (MetaWaylandSurface *surface, cairo_rectangle_int_t buffer_rect; cairo_region_t *scaled_region; cairo_region_t *transformed_region; + cairo_region_t *viewport_region; + ClutterRect src_rect; int i, n_rectangles; /* If the client destroyed the buffer it attached before committing, but @@ -293,7 +296,6 @@ surface_process_damage (MetaWaylandSurface *surface, .width = get_buffer_width (surface), .height = get_buffer_height (surface), }; - cairo_region_intersect_rectangle (buffer_region, &buffer_rect); /* Intersect the damage region with the surface region before scaling in * order to avoid integer overflow when scaling a damage region is too large @@ -307,32 +309,57 @@ 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); - transformed_region = meta_region_transform (scaled_region, + if (surface->viewport.has_src_rect) + { + src_rect = (ClutterRect) { + .origin.x = surface->viewport.src_rect.origin.x * surface->scale, + .origin.y = surface->viewport.src_rect.origin.y * surface->scale, + .size.width = surface->viewport.src_rect.size.width * surface->scale, + .size.height = surface->viewport.src_rect.size.height * surface->scale + }; + } + else + { + src_rect = (ClutterRect) { + .size.width = surface_rect.width * surface->scale, + .size.height = surface_rect.height * surface->scale, + }; + } + viewport_region = meta_region_crop_and_scale (scaled_region, + &src_rect, + surface_rect.width * + surface->scale, + surface_rect.height * + surface->scale); + transformed_region = meta_region_transform (viewport_region, surface->buffer_transform, buffer_rect.width, buffer_rect.height); - /* Now add the buffer damage on top of the scaled damage region, as buffer - * damage is already in that scale. */ - cairo_region_union (transformed_region, buffer_region); + /* Now add the scaled, cropped and transformed damage region to the + * buffer damage. Buffer damage is already in the correct coordinate space. */ + cairo_region_union (buffer_region, transformed_region); + + cairo_region_intersect_rectangle (buffer_region, &buffer_rect); /* First update the buffer. */ - meta_wayland_buffer_process_damage (buffer, transformed_region); + meta_wayland_buffer_process_damage (buffer, buffer_region); /* Now damage the actor. The actor expects damage in the unscaled texture * coordinate space, i.e. same as the buffer. */ /* XXX: Should this be a signal / callback on MetaWaylandBuffer instead? */ - n_rectangles = cairo_region_num_rectangles (transformed_region); + n_rectangles = cairo_region_num_rectangles (buffer_region); for (i = 0; i < n_rectangles; i++) { cairo_rectangle_int_t rect; - cairo_region_get_rectangle (transformed_region, i, &rect); + cairo_region_get_rectangle (buffer_region, i, &rect); meta_surface_actor_process_damage (meta_wayland_surface_get_actor (surface), rect.x, rect.y, rect.width, rect.height); } + cairo_region_destroy (viewport_region); cairo_region_destroy (scaled_region); cairo_region_destroy (transformed_region); } @@ -422,6 +449,8 @@ pending_state_init (MetaWaylandPendingState *state) state->has_new_max_size = FALSE; state->has_new_buffer_transform = FALSE; + state->has_new_viewport_src_rect = FALSE; + state->has_new_viewport_dst_size = FALSE; } static void @@ -535,6 +564,22 @@ merge_pending_state (MetaWaylandPendingState *from, to->has_new_buffer_transform = TRUE; } + if (from->has_new_viewport_src_rect) + { + to->viewport_src_rect.origin.x = from->viewport_src_rect.origin.x; + to->viewport_src_rect.origin.y = from->viewport_src_rect.origin.y; + to->viewport_src_rect.size.width = from->viewport_src_rect.size.width; + to->viewport_src_rect.size.height = from->viewport_src_rect.size.height; + to->has_new_viewport_src_rect = TRUE; + } + + if (from->has_new_viewport_dst_size) + { + to->viewport_dst_width = from->viewport_dst_width; + to->viewport_dst_height = from->viewport_dst_height; + to->has_new_viewport_dst_size = TRUE; + } + if (to->buffer && to->buffer_destroy_handler_id == 0) { to->buffer_destroy_handler_id = @@ -713,6 +758,22 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface *surface, if (pending->has_new_buffer_transform) surface->buffer_transform = pending->buffer_transform; + if (pending->has_new_viewport_src_rect) + { + surface->viewport.src_rect.origin.x = pending->viewport_src_rect.origin.x; + surface->viewport.src_rect.origin.y = pending->viewport_src_rect.origin.y; + surface->viewport.src_rect.size.width = pending->viewport_src_rect.size.width; + surface->viewport.src_rect.size.height = pending->viewport_src_rect.size.height; + surface->viewport.has_src_rect = surface->viewport.src_rect.size.width > 0; + } + + if (pending->has_new_viewport_dst_size) + { + surface->viewport.dst_width = pending->viewport_dst_width; + surface->viewport.dst_height = pending->viewport_dst_height; + surface->viewport.has_dst_size = surface->viewport.dst_width > 0; + } + if (meta_wayland_surface_get_actor (surface) && (!cairo_region_is_empty (pending->surface_damage) || !cairo_region_is_empty (pending->buffer_damage))) @@ -1369,6 +1430,7 @@ meta_wayland_shell_init (MetaWaylandCompositor *compositor) meta_wayland_legacy_xdg_shell_init (compositor); meta_wayland_wl_shell_init (compositor); meta_wayland_init_gtk_shell (compositor); + meta_wayland_init_viewporter (compositor); } void @@ -1804,25 +1866,47 @@ meta_wayland_surface_notify_geometry_changed (MetaWaylandSurface *surface) int meta_wayland_surface_get_width (MetaWaylandSurface *surface) { - int width; - - if (meta_monitor_transform_is_rotated (surface->buffer_transform)) - width = get_buffer_height (surface); + if (surface->viewport.has_dst_size) + { + return surface->viewport.dst_width; + } + else if (surface->viewport.has_src_rect) + { + return ceilf (surface->viewport.src_rect.size.width); + } else - width = get_buffer_width (surface); + { + int width; - return width / surface->scale; + if (meta_monitor_transform_is_rotated (surface->buffer_transform)) + width = get_buffer_height (surface); + else + width = get_buffer_width (surface); + + return width / surface->scale; + } } int meta_wayland_surface_get_height (MetaWaylandSurface *surface) { - int height; - - if (meta_monitor_transform_is_rotated (surface->buffer_transform)) - height = get_buffer_width (surface); + if (surface->viewport.has_dst_size) + { + return surface->viewport.dst_height; + } + else if (surface->viewport.has_src_rect) + { + return ceilf (surface->viewport.src_rect.size.height); + } else - height = get_buffer_height (surface); + { + int height; - return height / surface->scale; + if (meta_monitor_transform_is_rotated (surface->buffer_transform)) + height = get_buffer_width (surface); + else + height = get_buffer_height (surface); + + return height / surface->scale; + } } diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h index 62496cfb7..63e088357 100644 --- a/src/wayland/meta-wayland-surface.h +++ b/src/wayland/meta-wayland-surface.h @@ -109,6 +109,11 @@ struct _MetaWaylandPendingState gboolean has_new_buffer_transform; MetaMonitorTransform buffer_transform; + gboolean has_new_viewport_src_rect; + ClutterRect viewport_src_rect; + gboolean has_new_viewport_dst_size; + int viewport_dst_width; + int viewport_dst_height; }; struct _MetaWaylandDragDestFuncs @@ -198,6 +203,19 @@ struct _MetaWaylandSurface GSList *pending_placement_ops; } sub; + /* wp_viewport */ + struct { + struct wl_resource *resource; + gulong destroy_handler_id; + + gboolean has_src_rect; + ClutterRect src_rect; + + gboolean has_dst_size; + int dst_width; + int dst_height; + } viewport; + /* table of seats for which shortcuts are inhibited */ GHashTable *shortcut_inhibited_seats; }; diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h index 19a558016..674b6c4e5 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 000000000..7548998c1 --- /dev/null +++ b/src/wayland/meta-wayland-viewporter.c @@ -0,0 +1,230 @@ +/* + * Wayland Support + * + * Copyright (C) 2018-2019 Robert Mader + * + * 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 "config.h" + +#include "meta-wayland-viewporter.h" + +#include + +#include "wayland/meta-wayland-private.h" +#include "wayland/meta-wayland-subsurface.h" +#include "wayland/meta-wayland-surface.h" +#include "wayland/meta-wayland-versions.h" + +#include "viewporter-server-protocol.h" + +static void +wp_viewport_destructor (struct wl_resource *resource) +{ + MetaWaylandSurface *surface; + + surface = wl_resource_get_user_data (resource); + if (!surface) + return; + + g_signal_handler_disconnect (surface, surface->viewport.destroy_handler_id); + surface->viewport.destroy_handler_id = 0; + + surface->pending->viewport_src_rect.size.width = -1; + surface->pending->viewport_dst_width = -1; + surface->pending->has_new_viewport_src_rect = TRUE; + surface->pending->has_new_viewport_dst_size = TRUE; + + surface->viewport.resource = NULL; +} + +static void +on_surface_destroyed (MetaWaylandSurface *surface) +{ + wl_resource_set_user_data (surface->viewport.resource, NULL); +} + +static void +wp_viewport_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +wp_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 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)) + { + surface->pending->viewport_src_rect.origin.x = new_x; + surface->pending->viewport_src_rect.origin.y = new_y; + surface->pending->viewport_src_rect.size.width = new_width; + surface->pending->viewport_src_rect.size.height = new_height; + surface->pending->has_new_viewport_src_rect = TRUE; + } + else + { + wl_resource_post_error (resource, + WP_VIEWPORT_ERROR_BAD_VALUE, + "x and y values must be zero or positive and " + "width and height valuest must be positive or " + "all values must be -1 to unset the viewport"); + } +} + +static void +wp_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 no longer exists"); + return; + } + + if ((dst_width > 0 && dst_height > 0) || + (dst_width == -1 && dst_height == -1)) + { + surface->pending->viewport_dst_width = dst_width; + surface->pending->viewport_dst_height = dst_height; + surface->pending->has_new_viewport_dst_size = TRUE; + } + 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 = { + wp_viewport_destroy, + wp_viewport_set_source, + wp_viewport_set_destination, +}; + +static void +wp_viewporter_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +wp_viewporter_get_viewport (struct wl_client *client, + struct wl_resource *resource, + uint32_t viewport_id, + struct wl_resource *surface_resource) +{ + MetaWaylandSurface *surface; + struct wl_resource *viewport_resource; + + surface = wl_resource_get_user_data (surface_resource); + if (surface->viewport.resource) + { + wl_resource_post_error (resource, + WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS, + "viewport already exists on surface"); + return; + } + + viewport_resource = wl_resource_create (client, + &wp_viewport_interface, + wl_resource_get_version (resource), + viewport_id); + wl_resource_set_implementation (viewport_resource, + &meta_wayland_viewport_interface, + surface, + wp_viewport_destructor); + + surface->viewport.resource = viewport_resource; + surface->viewport.destroy_handler_id = + g_signal_connect (surface, + "destroy", + G_CALLBACK (on_surface_destroyed), + NULL); +} + +static const struct wp_viewporter_interface meta_wayland_viewporter_interface = { + wp_viewporter_destroy, + wp_viewporter_get_viewport, +}; + +static void +wp_viewporter_bind (struct wl_client *client, + void *data, + uint32_t version, + uint32_t 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_init_viewporter (MetaWaylandCompositor *compositor) +{ + if (wl_global_create (compositor->wayland_display, + &wp_viewporter_interface, + META_WP_VIEWPORTER_VERSION, + compositor, + wp_viewporter_bind) == NULL) + g_error ("Failed to register a global wl-viewporter object"); +} diff --git a/src/wayland/meta-wayland-viewporter.h b/src/wayland/meta-wayland-viewporter.h new file mode 100644 index 000000000..8cc099b11 --- /dev/null +++ b/src/wayland/meta-wayland-viewporter.h @@ -0,0 +1,30 @@ +/* + * Wayland Support + * + * Copyright (C) 2018-2019 Robert Mader + * + * 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_init_viewporter (MetaWaylandCompositor *compositor); + +#endif /* META_WAYLAND_VIEWPORTER_H */