window-actor/wayland: Draw black background for fullscreen windows

Fullscreen Wayland toplevel surfaces don't need to respect the
configured size in which case it should be shown centered on the monitor
with a black background. The black background becomes part of the window
geometry.

The surface container is responsible for correctly culling the surfaces
and making sure the surface actors are removed from the actor tree to
avoid destroying them.

The window actor culling implementation assumes all surfaces to be direct
children of said actor. The introduction of the surface_container actor
broke that assumption. This implements the culling interface in
MetaWindowActorWayland which is aware of the actor surface_container and
fullscreen state.

v2: Fix forwarding culling to surface even if there is a background.
v2: Don't alter passed geometry.
v2: Update window geometry code documentation to reflect these changes.
v2: Only use constrained rect if we're acked fullscreen.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2338>
This commit is contained in:
Sebastian Wick 2022-05-03 18:47:57 +02:00 committed by Marge Bot
parent 0034b54877
commit 909616b208
7 changed files with 328 additions and 64 deletions

View File

@ -34,6 +34,8 @@ struct _MetaWindowActorClass
void (*update_regions) (MetaWindowActor *actor);
gboolean (*can_freeze_commits) (MetaWindowActor *actor);
void (*sync_geometry) (MetaWindowActor *actor,
const MetaRectangle *actor_rect);
gboolean (*is_single_surface_actor) (MetaWindowActor *actor);
};

View File

@ -22,15 +22,35 @@
#include "config.h"
#include "compositor/clutter-utils.h"
#include "compositor/meta-cullable.h"
#include "compositor/meta-surface-actor-wayland.h"
#include "compositor/meta-window-actor-wayland.h"
#include "compositor/region-utils.h"
#include "meta/meta-window-actor.h"
#include "wayland/meta-wayland-surface.h"
#include "wayland/meta-window-wayland.h"
struct _MetaSurfaceContainerActorWayland
{
ClutterActor parent;
MetaWindowActor *window_actor;
};
static void surface_container_cullable_iface_init (MetaCullableInterface *iface);
G_DEFINE_TYPE_WITH_CODE (MetaSurfaceContainerActorWayland,
meta_surface_container_actor_wayland,
CLUTTER_TYPE_ACTOR,
G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE,
surface_container_cullable_iface_init))
struct _MetaWindowActorWayland
{
MetaWindowActor parent;
ClutterActor *background;
MetaSurfaceContainerActorWayland *surface_container;
};
static void cullable_iface_init (MetaCullableInterface *iface);
@ -42,10 +62,90 @@ G_DEFINE_TYPE_WITH_CODE (MetaWindowActorWayland, meta_window_actor_wayland,
typedef struct _SurfaceTreeTraverseData
{
MetaWindowActor *window_actor;
ClutterActor *surface_container;
int index;
} SurfaceTreeTraverseData;
static MetaSurfaceContainerActorWayland *
surface_container_new (MetaWindowActor *window_actor)
{
MetaSurfaceContainerActorWayland *surface_container;
surface_container = g_object_new (META_TYPE_SURFACE_CONTAINER_ACTOR_WAYLAND,
NULL);
surface_container->window_actor = window_actor;
return surface_container;
}
static void
surface_container_cull_out (MetaCullable *cullable,
cairo_region_t *unobscured_region,
cairo_region_t *clip_region)
{
meta_cullable_cull_out_children (cullable, unobscured_region, clip_region);
}
static gboolean
surface_container_is_untransformed (MetaCullable *cullable)
{
MetaSurfaceContainerActorWayland *surface_container =
META_SURFACE_CONTAINER_ACTOR_WAYLAND (cullable);
ClutterActor *actor = CLUTTER_ACTOR (cullable);
MetaWindowActor *window_actor;
float width, height;
graphene_point3d_t verts[4];
int geometry_scale;
clutter_actor_get_size (actor, &width, &height);
clutter_actor_get_abs_allocation_vertices (actor, verts);
window_actor = surface_container->window_actor;
geometry_scale = meta_window_actor_get_geometry_scale (window_actor);
return meta_actor_vertices_are_untransformed (verts,
width * geometry_scale,
height * geometry_scale,
NULL);
}
static void
surface_container_reset_culling (MetaCullable *cullable)
{
meta_cullable_reset_culling_children (cullable);
}
static void
surface_container_cullable_iface_init (MetaCullableInterface *iface)
{
iface->cull_out = surface_container_cull_out;
iface->is_untransformed = surface_container_is_untransformed;
iface->reset_culling = surface_container_reset_culling;
}
static void
surface_container_dispose (GObject *object)
{
MetaSurfaceContainerActorWayland *self = META_SURFACE_CONTAINER_ACTOR_WAYLAND (object);
clutter_actor_remove_all_children (CLUTTER_ACTOR (self));
G_OBJECT_CLASS (meta_surface_container_actor_wayland_parent_class)->dispose (object);
}
static void
meta_surface_container_actor_wayland_class_init (MetaSurfaceContainerActorWaylandClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = surface_container_dispose;
}
static void
meta_surface_container_actor_wayland_init (MetaSurfaceContainerActorWayland *self)
{
}
static gboolean
get_surface_actor_list (GNode *node,
gpointer data)
@ -64,23 +164,23 @@ set_surface_actor_index (GNode *node,
{
MetaWaylandSurface *surface = node->data;
SurfaceTreeTraverseData *traverse_data = data;
ClutterActor *window_actor = CLUTTER_ACTOR (traverse_data->window_actor);
ClutterActor *container = traverse_data->surface_container;
ClutterActor *surface_actor =
CLUTTER_ACTOR (meta_wayland_surface_get_actor (surface));
if (clutter_actor_contains (window_actor, surface_actor))
if (clutter_actor_contains (container, surface_actor))
{
if (clutter_actor_get_child_at_index (window_actor, traverse_data->index) !=
if (clutter_actor_get_child_at_index (container, traverse_data->index) !=
surface_actor)
{
clutter_actor_set_child_at_index (window_actor,
clutter_actor_set_child_at_index (container,
surface_actor,
traverse_data->index);
}
}
else
{
clutter_actor_insert_child_at_index (window_actor,
clutter_actor_insert_child_at_index (container,
surface_actor,
traverse_data->index);
}
@ -92,6 +192,7 @@ set_surface_actor_index (GNode *node,
void
meta_window_actor_wayland_rebuild_surface_tree (MetaWindowActor *actor)
{
MetaWindowActorWayland *self = META_WINDOW_ACTOR_WAYLAND (actor);
MetaSurfaceActor *surface_actor =
meta_window_actor_get_surface (actor);
MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (
@ -109,18 +210,21 @@ meta_window_actor_wayland_rebuild_surface_tree (MetaWindowActor *actor)
get_surface_actor_list,
&surface_actors);
children = clutter_actor_get_children (CLUTTER_ACTOR (actor));
children =
clutter_actor_get_children (CLUTTER_ACTOR (self->surface_container));
for (l = children; l; l = l->next)
{
ClutterActor *child_actor = l->data;
if (META_IS_SURFACE_ACTOR_WAYLAND (child_actor) &&
!g_list_find (surface_actors, child_actor))
clutter_actor_remove_child (CLUTTER_ACTOR (actor), child_actor);
if (!g_list_find (surface_actors, child_actor))
{
clutter_actor_remove_child (CLUTTER_ACTOR (self->surface_container),
child_actor);
}
}
traverse_data = (SurfaceTreeTraverseData) {
.window_actor = actor,
.surface_container = CLUTTER_ACTOR (self->surface_container),
.index = 0,
};
g_node_traverse (root_node,
@ -131,14 +235,48 @@ meta_window_actor_wayland_rebuild_surface_tree (MetaWindowActor *actor)
&traverse_data);
}
static cairo_region_t *
calculate_background_cull_region (MetaWindowActorWayland *self)
{
MetaWindowActor *window_actor = META_WINDOW_ACTOR (self);
int geometry_scale;
cairo_rectangle_int_t rect;
geometry_scale = meta_window_actor_get_geometry_scale (window_actor);
rect = (cairo_rectangle_int_t) {
.x = 0,
.y = 0,
.width = clutter_actor_get_width (self->background) * geometry_scale,
.height = clutter_actor_get_height (self->background) * geometry_scale,
};
return cairo_region_create_rectangle (&rect);
}
static void
meta_window_actor_wayland_cull_out (MetaCullable *cullable,
cairo_region_t *unobscured_region,
cairo_region_t *clip_region)
{
meta_cullable_cull_out_children (cullable,
MetaWindowActorWayland *self =
META_WINDOW_ACTOR_WAYLAND (cullable);
meta_cullable_cull_out_children (META_CULLABLE (self),
unobscured_region,
clip_region);
if (self->background)
{
cairo_region_t *background_cull_region;
background_cull_region = calculate_background_cull_region (self);
if (unobscured_region)
cairo_region_subtract (unobscured_region, background_cull_region);
if (clip_region)
cairo_region_subtract (clip_region, background_cull_region);
cairo_region_destroy (background_cull_region);
}
}
static void
@ -157,19 +295,25 @@ cullable_iface_init (MetaCullableInterface *iface)
static MetaSurfaceActor *
meta_window_actor_wayland_get_scanout_candidate (MetaWindowActor *actor)
{
MetaWindowActorWayland *self = META_WINDOW_ACTOR_WAYLAND (actor);
ClutterActor *surface_container = CLUTTER_ACTOR (self->surface_container);
ClutterActor *child_actor;
MetaSurfaceActor *topmost_surface_actor;
MetaWindow *window;
child_actor = clutter_actor_get_last_child (CLUTTER_ACTOR (actor));
if (!child_actor || !META_IS_SURFACE_ACTOR_WAYLAND (child_actor))
if (clutter_actor_get_last_child (CLUTTER_ACTOR (self)) != surface_container)
return NULL;
child_actor = clutter_actor_get_last_child (surface_container);
if (!child_actor)
return NULL;
topmost_surface_actor = META_SURFACE_ACTOR (child_actor);
window = meta_window_actor_get_meta_window (actor);
if (!meta_window_is_fullscreen (window) &&
!meta_surface_actor_is_opaque (topmost_surface_actor))
if (!meta_surface_actor_is_opaque (topmost_surface_actor) &&
!(meta_window_is_fullscreen (window) &&
clutter_actor_get_n_children (surface_container) == 1))
return NULL;
return topmost_surface_actor;
@ -223,6 +367,13 @@ static void
meta_window_actor_wayland_set_frozen (MetaWindowActor *actor,
gboolean frozen)
{
MetaWindowActorWayland *self = META_WINDOW_ACTOR_WAYLAND (actor);
ClutterActor *child;
ClutterActorIter iter;
clutter_actor_iter_init (&iter, CLUTTER_ACTOR (self->surface_container));
while (clutter_actor_iter_next (&iter, &child))
meta_surface_actor_set_frozen (META_SURFACE_ACTOR (child), frozen);
}
static void
@ -239,36 +390,122 @@ meta_window_actor_wayland_can_freeze_commits (MetaWindowActor *actor)
static gboolean
meta_window_actor_wayland_is_single_surface_actor (MetaWindowActor *actor)
{
return clutter_actor_get_n_children (CLUTTER_ACTOR (actor)) == 1;
MetaWindowActorWayland *self = META_WINDOW_ACTOR_WAYLAND (actor);
ClutterActor *surface_container = CLUTTER_ACTOR (self->surface_container);
return clutter_actor_get_n_children (surface_container) == 1 &&
!self->background;
}
static gboolean
maybe_configure_black_background (MetaWindowActorWayland *self,
float *surfaces_width,
float *surfaces_height,
float *background_width,
float *background_height)
{
MetaWindowActor *window_actor = META_WINDOW_ACTOR (self);
MetaWindow *window = meta_window_actor_get_meta_window (window_actor);
MetaLogicalMonitor *logical_monitor;
int geometry_scale;
MetaRectangle fullscreen_layout;
ClutterActor *child;
ClutterActorIter iter;
float max_width = 0;
float max_height = 0;
if (!meta_window_wayland_is_acked_fullscreen (META_WINDOW_WAYLAND (window)))
return FALSE;
geometry_scale = meta_window_actor_get_geometry_scale (window_actor);
logical_monitor = meta_window_get_main_logical_monitor (window);
fullscreen_layout = meta_logical_monitor_get_layout (logical_monitor);
clutter_actor_iter_init (&iter, CLUTTER_ACTOR (self->surface_container));
while (clutter_actor_iter_next (&iter, &child))
{
float child_width, child_height;
clutter_actor_get_size (child, &child_width, &child_height);
if (META_IS_SURFACE_ACTOR (child) &&
meta_surface_actor_is_opaque (META_SURFACE_ACTOR (child)) &&
G_APPROX_VALUE (clutter_actor_get_x (child), 0,
CLUTTER_COORDINATE_EPSILON) &&
G_APPROX_VALUE (clutter_actor_get_y (child), 0,
CLUTTER_COORDINATE_EPSILON) &&
G_APPROX_VALUE (child_width, fullscreen_layout.width,
CLUTTER_COORDINATE_EPSILON) &&
G_APPROX_VALUE (child_height, fullscreen_layout.height,
CLUTTER_COORDINATE_EPSILON))
return FALSE;
max_width = MAX (max_width, child_width);
max_height = MAX (max_height, child_height);
}
*surfaces_width = max_width;
*surfaces_height = max_height;
*background_width = window->rect.width / geometry_scale;
*background_height = window->rect.height / geometry_scale;
return TRUE;
}
static void
meta_window_actor_wayland_dispose (GObject *object)
meta_window_actor_wayland_sync_geometry (MetaWindowActor *actor,
const MetaRectangle *actor_rect)
{
MetaWindowActor *window_actor = META_WINDOW_ACTOR (object);
MetaSurfaceActor *surface_actor =
meta_window_actor_get_surface (window_actor);
g_autoptr (GList) children = NULL;
GList *l;
MetaWindowActorWayland *self = META_WINDOW_ACTOR_WAYLAND (actor);
ClutterActor *surface_container = CLUTTER_ACTOR (self->surface_container);
MetaWindow *window;
float surfaces_width, surfaces_height;
float background_width, background_height;
children = clutter_actor_get_children (CLUTTER_ACTOR (window_actor));
for (l = children; l; l = l->next)
window = meta_window_actor_get_meta_window (actor);
if (window->unmanaging)
return;
if (maybe_configure_black_background (self,
&surfaces_width, &surfaces_height,
&background_width, &background_height))
{
ClutterActor *child_actor = l->data;
int geometry_scale;
int child_actor_width, child_actor_height;
if (META_IS_SURFACE_ACTOR_WAYLAND (child_actor) &&
child_actor != CLUTTER_ACTOR (surface_actor))
clutter_actor_remove_child (CLUTTER_ACTOR (window_actor), child_actor);
if (!self->background)
{
self->background = clutter_actor_new ();
clutter_actor_set_background_color (self->background,
CLUTTER_COLOR_Black);
clutter_actor_set_reactive (self->background, TRUE);
clutter_actor_insert_child_below (CLUTTER_ACTOR (self),
self->background,
NULL);
}
geometry_scale =
meta_window_actor_get_geometry_scale (actor);
child_actor_width = actor_rect->width / geometry_scale;
child_actor_height = actor_rect->height / geometry_scale;
clutter_actor_set_size (self->background,
background_width, background_height);
clutter_actor_set_position (surface_container,
(child_actor_width - surfaces_width) / 2,
(child_actor_height - surfaces_height) / 2);
}
else if (self->background)
{
clutter_actor_set_position (surface_container, 0, 0);
g_clear_pointer (&self->background, clutter_actor_destroy);
}
G_OBJECT_CLASS (meta_window_actor_wayland_parent_class)->dispose (object);
}
static void
meta_window_actor_wayland_class_init (MetaWindowActorWaylandClass *klass)
{
MetaWindowActorClass *window_actor_class = META_WINDOW_ACTOR_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
window_actor_class->get_scanout_candidate = meta_window_actor_wayland_get_scanout_candidate;
window_actor_class->assign_surface_actor = meta_window_actor_wayland_assign_surface_actor;
@ -280,12 +517,15 @@ meta_window_actor_wayland_class_init (MetaWindowActorWaylandClass *klass)
window_actor_class->set_frozen = meta_window_actor_wayland_set_frozen;
window_actor_class->update_regions = meta_window_actor_wayland_update_regions;
window_actor_class->can_freeze_commits = meta_window_actor_wayland_can_freeze_commits;
window_actor_class->sync_geometry = meta_window_actor_wayland_sync_geometry;
window_actor_class->is_single_surface_actor = meta_window_actor_wayland_is_single_surface_actor;
object_class->dispose = meta_window_actor_wayland_dispose;
}
static void
meta_window_actor_wayland_init (MetaWindowActorWayland *self)
{
self->surface_container = surface_container_new (META_WINDOW_ACTOR (self));
clutter_actor_add_child (CLUTTER_ACTOR (self),
CLUTTER_ACTOR (self->surface_container));
}

View File

@ -31,6 +31,12 @@ G_DECLARE_FINAL_TYPE (MetaWindowActorWayland,
META, WINDOW_ACTOR_WAYLAND,
MetaWindowActor)
#define META_TYPE_SURFACE_CONTAINER_ACTOR_WAYLAND (meta_surface_container_actor_wayland_get_type())
G_DECLARE_FINAL_TYPE (MetaSurfaceContainerActorWayland,
meta_surface_container_actor_wayland,
META, SURFACE_CONTAINER_ACTOR_WAYLAND,
ClutterActor)
void meta_window_actor_wayland_rebuild_surface_tree (MetaWindowActor *actor);
#endif /*META_WINDOW_ACTOR_WAYLAND_H */

View File

@ -1511,6 +1511,7 @@ meta_window_actor_x11_set_frozen (MetaWindowActor *actor,
return;
actor_x11->is_frozen = frozen;
meta_surface_actor_set_frozen (meta_window_actor_get_surface (actor), frozen);
if (frozen)
meta_window_x11_freeze_commits (window);
@ -1538,6 +1539,12 @@ meta_window_actor_x11_is_single_surface_actor (MetaWindowActor *actor)
return clutter_actor_get_n_children (CLUTTER_ACTOR (actor)) == 1;
}
static void
meta_window_actor_x11_sync_geometry (MetaWindowActor *actor,
const MetaRectangle *actor_rect)
{
}
static void
meta_window_actor_x11_set_property (GObject *object,
guint prop_id,
@ -1672,6 +1679,9 @@ meta_window_actor_x11_dispose (GObject *object)
{
g_clear_signal_handler (&actor_x11->repaint_scheduled_id, surface_actor);
g_clear_signal_handler (&actor_x11->size_changed_id, surface_actor);
clutter_actor_remove_child (CLUTTER_ACTOR (object),
CLUTTER_ACTOR (surface_actor));
}
g_clear_pointer (&actor_x11->shape_region, cairo_region_destroy);
@ -1714,6 +1724,7 @@ meta_window_actor_x11_class_init (MetaWindowActorX11Class *klass)
window_actor_class->set_frozen = meta_window_actor_x11_set_frozen;
window_actor_class->update_regions = meta_window_actor_x11_update_regions;
window_actor_class->can_freeze_commits = meta_window_actor_x11_can_freeze_commits;
window_actor_class->sync_geometry = meta_window_actor_x11_sync_geometry;
window_actor_class->is_single_surface_actor = meta_window_actor_x11_is_single_surface_actor;
actor_class->paint = meta_window_actor_x11_paint;

View File

@ -270,16 +270,6 @@ static void
meta_window_actor_set_frozen (MetaWindowActor *self,
gboolean frozen)
{
ClutterActor *child;
ClutterActorIter iter;
clutter_actor_iter_init (&iter, CLUTTER_ACTOR (self));
while (clutter_actor_iter_next (&iter, &child))
{
if (META_IS_SURFACE_ACTOR (child))
meta_surface_actor_set_frozen (META_SURFACE_ACTOR (child), frozen);
}
META_WINDOW_ACTOR_GET_CLASS (self)->set_frozen (self, frozen);
}
@ -464,12 +454,7 @@ meta_window_actor_dispose (GObject *object)
g_clear_object (&priv->window);
if (priv->surface)
{
clutter_actor_remove_child (CLUTTER_ACTOR (self),
CLUTTER_ACTOR (priv->surface));
g_clear_object (&priv->surface);
}
g_clear_object (&priv->surface);
G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object);
}
@ -861,6 +846,8 @@ meta_window_actor_sync_actor_geometry (MetaWindowActor *self,
if (meta_window_actor_is_frozen (self) && !did_placement)
return META_WINDOW_ACTOR_CHANGE_POSITION | META_WINDOW_ACTOR_CHANGE_SIZE;
META_WINDOW_ACTOR_GET_CLASS (self)->sync_geometry (self, &actor_rect);
if (clutter_actor_has_allocation (actor))
{
ClutterActorBox box;

View File

@ -267,14 +267,13 @@ meta_window_wayland_move_resize_internal (MetaWindow *window,
configured_rect.width = constrained_rect.width;
configured_rect.height = constrained_rect.height;
/* For wayland clients, the size is completely determined by the client,
* and while this allows to avoid some trickery with frames and the resulting
* lagging, we also need to insist a bit when the constraints would apply
* a different size than the client decides.
/* The size is determined by the client, except when the client is explicitly
* fullscreen, in which case the compositor compensates for the size
* difference between what surface configuration the client provided, and the
* size of the area a fullscreen window state is expected to fill.
*
* Note that this is not generally a problem for normal toplevel windows (the
* constraints don't see the size hints, or just change the position), but
* it can be for maximized or fullscreen.
* For non-explicit-fullscreen states, since the size is always determined by
* the client, the we cannot use the size calculated by the constraints.
*/
if (flags & META_MOVE_RESIZE_FORCE_MOVE)
@ -283,16 +282,26 @@ meta_window_wayland_move_resize_internal (MetaWindow *window,
}
else if (flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE)
{
/* This is a call to wl_surface_commit(), ignore the constrained_rect and
* update the real client size to match the buffer size.
*/
MetaWaylandWindowConfiguration *configuration;
int new_width, new_height;
if (window->rect.width != unconstrained_rect.width ||
window->rect.height != unconstrained_rect.height)
configuration = wl_window->last_acked_configuration;
if (configuration && configuration->is_fullscreen)
{
new_width = constrained_rect.width;
new_height = constrained_rect.height;
}
else
{
new_width = unconstrained_rect.width;
new_height = unconstrained_rect.height;
}
if (window->rect.width != new_width ||
window->rect.height != new_height)
{
*result |= META_MOVE_RESIZE_RESULT_RESIZED;
window->rect.width = unconstrained_rect.width;
window->rect.height = unconstrained_rect.height;
window->rect.width = new_width;
window->rect.height = new_height;
}
window->buffer_rect.width =
@ -1290,3 +1299,10 @@ meta_window_wayland_get_max_size (MetaWindow *window,
scale = 1.0 / (float) meta_window_wayland_get_geometry_scale (window);
scale_size (width, height, scale);
}
gboolean
meta_window_wayland_is_acked_fullscreen (MetaWindowWayland *wl_window)
{
return (wl_window->last_acked_configuration &&
wl_window->last_acked_configuration->is_fullscreen);
}

View File

@ -81,4 +81,6 @@ gboolean meta_window_wayland_is_resize (MetaWindowWayland *wl_window,
int width,
int height);
gboolean meta_window_wayland_is_acked_fullscreen (MetaWindowWayland *wl_window);
#endif