mirror of
https://github.com/brl/mutter.git
synced 2024-11-30 12:00:44 -05:00
225f7d46a3
The intention here was to check if the subsurface belongs to a window. Thus it didn't behave as expected for subsurfaces belonging to non-toplevel windows. After the previous commit we can use `get_window()` to check for what we actually want here. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2717>
592 lines
18 KiB
C
592 lines
18 KiB
C
/*
|
|
* Copyright (C) 2012,2013 Intel Corporation
|
|
* Copyright (C) 2013-2017 Red Hat, Inc.
|
|
*
|
|
* 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 "wayland/meta-wayland-subsurface.h"
|
|
|
|
#include "compositor/meta-surface-actor-wayland.h"
|
|
#include "compositor/meta-window-actor-wayland.h"
|
|
#include "wayland/meta-wayland.h"
|
|
#include "wayland/meta-wayland-actor-surface.h"
|
|
#include "wayland/meta-wayland-buffer.h"
|
|
#include "wayland/meta-wayland-surface.h"
|
|
#include "wayland/meta-wayland-transaction.h"
|
|
#include "wayland/meta-window-wayland.h"
|
|
|
|
struct _MetaWaylandSubsurface
|
|
{
|
|
MetaWaylandActorSurface parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaWaylandSubsurface,
|
|
meta_wayland_subsurface,
|
|
META_TYPE_WAYLAND_ACTOR_SURFACE)
|
|
|
|
static void
|
|
transform_subsurface_position (MetaWaylandSurface *surface,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
do
|
|
{
|
|
*x += surface->sub.x;
|
|
*y += surface->sub.y;
|
|
|
|
surface = surface->output_state.parent;
|
|
}
|
|
while (surface);
|
|
}
|
|
|
|
static gboolean
|
|
should_show (MetaWaylandSurface *surface)
|
|
{
|
|
if (!surface->buffer)
|
|
return FALSE;
|
|
else if (surface->output_state.parent)
|
|
return should_show (surface->output_state.parent);
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
sync_actor_subsurface_state (MetaWaylandSurface *surface)
|
|
{
|
|
ClutterActor *actor = CLUTTER_ACTOR (meta_wayland_surface_get_actor (surface));
|
|
MetaWindow *toplevel_window;
|
|
int x, y;
|
|
|
|
toplevel_window = meta_wayland_surface_get_toplevel_window (surface);
|
|
if (!toplevel_window)
|
|
return;
|
|
|
|
if (toplevel_window->client_type == META_WINDOW_CLIENT_TYPE_X11)
|
|
return;
|
|
|
|
x = y = 0;
|
|
transform_subsurface_position (surface, &x, &y);
|
|
|
|
clutter_actor_set_position (actor, x, y);
|
|
clutter_actor_set_reactive (actor, TRUE);
|
|
|
|
if (should_show (surface))
|
|
clutter_actor_show (actor);
|
|
else
|
|
clutter_actor_hide (actor);
|
|
}
|
|
|
|
static gboolean
|
|
is_child (MetaWaylandSurface *surface,
|
|
MetaWaylandSurface *sibling)
|
|
{
|
|
return surface->protocol_state.parent == sibling;
|
|
}
|
|
|
|
static gboolean
|
|
is_sibling (MetaWaylandSurface *surface,
|
|
MetaWaylandSurface *sibling)
|
|
{
|
|
return surface != sibling &&
|
|
surface->protocol_state.parent == sibling->protocol_state.parent;
|
|
}
|
|
|
|
void
|
|
meta_wayland_subsurface_union_geometry (MetaWaylandSubsurface *subsurface,
|
|
int parent_x,
|
|
int parent_y,
|
|
MetaRectangle *out_geometry)
|
|
{
|
|
MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (subsurface);
|
|
MetaWaylandSurface *surface =
|
|
meta_wayland_surface_role_get_surface (surface_role);
|
|
MetaRectangle geometry;
|
|
MetaWaylandSurface *subsurface_surface;
|
|
|
|
geometry = (MetaRectangle) {
|
|
.x = surface->offset_x + surface->sub.x,
|
|
.y = surface->offset_y + surface->sub.y,
|
|
.width = meta_wayland_surface_get_width (surface),
|
|
.height = meta_wayland_surface_get_height (surface),
|
|
};
|
|
|
|
if (surface->buffer)
|
|
meta_rectangle_union (out_geometry, &geometry, out_geometry);
|
|
|
|
META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (&surface->output_state,
|
|
subsurface_surface)
|
|
{
|
|
MetaWaylandSubsurface *subsurface;
|
|
|
|
subsurface = META_WAYLAND_SUBSURFACE (subsurface_surface->role);
|
|
meta_wayland_subsurface_union_geometry (subsurface,
|
|
parent_x + geometry.x,
|
|
parent_y + geometry.y,
|
|
out_geometry);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_subsurface_assigned (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
MetaWaylandSurface *surface =
|
|
meta_wayland_surface_role_get_surface (surface_role);
|
|
MetaWaylandSurfaceRoleClass *surface_role_class =
|
|
META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_subsurface_parent_class);
|
|
|
|
surface->dnd.funcs = meta_wayland_data_device_get_drag_dest_funcs ();
|
|
|
|
surface_role_class->assigned (surface_role);
|
|
}
|
|
|
|
static MetaWaylandSurface *
|
|
meta_wayland_subsurface_get_toplevel (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
MetaWaylandSurface *surface =
|
|
meta_wayland_surface_role_get_surface (surface_role);
|
|
MetaWaylandSurface *parent = surface->output_state.parent;
|
|
|
|
if (parent)
|
|
return meta_wayland_surface_get_toplevel (parent);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static MetaWindow *
|
|
meta_wayland_subsurface_get_window (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
MetaWaylandSurface *surface =
|
|
meta_wayland_surface_role_get_surface (surface_role);
|
|
MetaWaylandSurface *parent;
|
|
|
|
parent = surface->protocol_state.parent;
|
|
if (parent)
|
|
return meta_wayland_surface_get_window (parent);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
meta_wayland_subsurface_is_synchronized (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
MetaWaylandSurface *surface =
|
|
meta_wayland_surface_role_get_surface (surface_role);
|
|
MetaWaylandSurface *parent;
|
|
|
|
if (surface->sub.synchronous)
|
|
return TRUE;
|
|
|
|
parent = surface->protocol_state.parent;
|
|
if (parent)
|
|
return meta_wayland_surface_is_synchronized (parent);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_subsurface_notify_subsurface_state_changed (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
MetaWaylandSurface *surface =
|
|
meta_wayland_surface_role_get_surface (surface_role);
|
|
MetaWaylandSurface *parent = surface->output_state.parent;
|
|
|
|
if (parent)
|
|
return meta_wayland_surface_notify_subsurface_state_changed (parent);
|
|
}
|
|
|
|
static int
|
|
meta_wayland_subsurface_get_geometry_scale (MetaWaylandActorSurface *actor_surface)
|
|
{
|
|
MetaWaylandSurfaceRole *surface_role =
|
|
META_WAYLAND_SURFACE_ROLE (actor_surface);
|
|
MetaWaylandSurface *surface =
|
|
meta_wayland_surface_role_get_surface (surface_role);
|
|
MetaWaylandSurface *parent = surface->output_state.parent;
|
|
|
|
if (parent)
|
|
{
|
|
MetaWaylandActorSurface *parent_actor;
|
|
|
|
parent_actor =
|
|
META_WAYLAND_ACTOR_SURFACE (surface->output_state.parent->role);
|
|
return meta_wayland_actor_surface_get_geometry_scale (parent_actor);
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_subsurface_sync_actor_state (MetaWaylandActorSurface *actor_surface)
|
|
{
|
|
MetaWaylandSurfaceRole *surface_role =
|
|
META_WAYLAND_SURFACE_ROLE (actor_surface);
|
|
MetaWaylandSurface *surface =
|
|
meta_wayland_surface_role_get_surface (surface_role);
|
|
MetaWaylandActorSurfaceClass *actor_surface_class =
|
|
META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_subsurface_parent_class);
|
|
|
|
if (meta_wayland_surface_get_window (surface))
|
|
actor_surface_class->sync_actor_state (actor_surface);
|
|
|
|
sync_actor_subsurface_state (surface);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_subsurface_init (MetaWaylandSubsurface *role)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_wayland_subsurface_class_init (MetaWaylandSubsurfaceClass *klass)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *surface_role_class =
|
|
META_WAYLAND_SURFACE_ROLE_CLASS (klass);
|
|
MetaWaylandActorSurfaceClass *actor_surface_class =
|
|
META_WAYLAND_ACTOR_SURFACE_CLASS (klass);
|
|
|
|
surface_role_class->assigned = meta_wayland_subsurface_assigned;
|
|
surface_role_class->get_toplevel = meta_wayland_subsurface_get_toplevel;
|
|
surface_role_class->get_window = meta_wayland_subsurface_get_window;
|
|
surface_role_class->is_synchronized = meta_wayland_subsurface_is_synchronized;
|
|
surface_role_class->notify_subsurface_state_changed =
|
|
meta_wayland_subsurface_notify_subsurface_state_changed;
|
|
|
|
actor_surface_class->get_geometry_scale =
|
|
meta_wayland_subsurface_get_geometry_scale;
|
|
actor_surface_class->sync_actor_state =
|
|
meta_wayland_subsurface_sync_actor_state;
|
|
}
|
|
|
|
static void
|
|
wl_subsurface_destroy (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
wl_resource_destroy (resource);
|
|
}
|
|
|
|
static void
|
|
wl_subsurface_set_position (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
int32_t x,
|
|
int32_t y)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
MetaWaylandTransaction *transaction;
|
|
|
|
transaction = meta_wayland_surface_ensure_transaction (surface);
|
|
meta_wayland_transaction_add_subsurface_position (transaction, surface, x, y);
|
|
}
|
|
|
|
static gboolean
|
|
is_valid_sibling (MetaWaylandSurface *surface,
|
|
MetaWaylandSurface *sibling)
|
|
{
|
|
if (is_child (surface, sibling))
|
|
return TRUE;
|
|
if (is_sibling (surface, sibling))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
queue_subsurface_placement (MetaWaylandSurface *surface,
|
|
MetaWaylandSurface *sibling,
|
|
MetaWaylandSubsurfacePlacement placement)
|
|
{
|
|
MetaWaylandSurface *parent = surface->protocol_state.parent;
|
|
gboolean have_synced_parent;
|
|
MetaWaylandTransaction *transaction;
|
|
MetaWaylandSubsurfacePlacementOp *op =
|
|
g_new0 (MetaWaylandSubsurfacePlacementOp, 1);
|
|
GNode *sibling_node;
|
|
|
|
have_synced_parent = sibling && meta_wayland_surface_is_synchronized (parent);
|
|
if (have_synced_parent)
|
|
transaction = meta_wayland_surface_ensure_transaction (parent);
|
|
else
|
|
transaction = meta_wayland_transaction_new (surface->compositor);
|
|
|
|
op->placement = placement;
|
|
op->sibling = sibling;
|
|
op->surface = surface;
|
|
|
|
g_node_unlink (surface->protocol_state.subsurface_branch_node);
|
|
|
|
if (!sibling)
|
|
goto out;
|
|
|
|
if (sibling == parent)
|
|
sibling_node = parent->protocol_state.subsurface_leaf_node;
|
|
else
|
|
sibling_node = sibling->protocol_state.subsurface_branch_node;
|
|
|
|
switch (placement)
|
|
{
|
|
case META_WAYLAND_SUBSURFACE_PLACEMENT_ABOVE:
|
|
g_node_insert_after (parent->protocol_state.subsurface_branch_node,
|
|
sibling_node,
|
|
surface->protocol_state.subsurface_branch_node);
|
|
break;
|
|
case META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW:
|
|
g_node_insert_before (parent->protocol_state.subsurface_branch_node,
|
|
sibling_node,
|
|
surface->protocol_state.subsurface_branch_node);
|
|
break;
|
|
}
|
|
|
|
out:
|
|
meta_wayland_transaction_add_placement_op (transaction, parent, op);
|
|
|
|
if (!have_synced_parent)
|
|
meta_wayland_transaction_commit (transaction);
|
|
}
|
|
|
|
static void
|
|
wl_subsurface_place_above (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
struct wl_resource *sibling_resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
MetaWaylandSurface *sibling = wl_resource_get_user_data (sibling_resource);
|
|
|
|
if (!is_valid_sibling (surface, sibling))
|
|
{
|
|
wl_resource_post_error (resource, WL_SUBSURFACE_ERROR_BAD_SURFACE,
|
|
"wl_subsurface::place_above: wl_surface@%d is "
|
|
"not a valid parent or sibling",
|
|
wl_resource_get_id (sibling->resource));
|
|
return;
|
|
}
|
|
|
|
queue_subsurface_placement (surface,
|
|
sibling,
|
|
META_WAYLAND_SUBSURFACE_PLACEMENT_ABOVE);
|
|
}
|
|
|
|
static void
|
|
wl_subsurface_place_below (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
struct wl_resource *sibling_resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
MetaWaylandSurface *sibling = wl_resource_get_user_data (sibling_resource);
|
|
|
|
if (!is_valid_sibling (surface, sibling))
|
|
{
|
|
wl_resource_post_error (resource, WL_SUBSURFACE_ERROR_BAD_SURFACE,
|
|
"wl_subsurface::place_below: wl_surface@%d is "
|
|
"not a valid parent or sibling",
|
|
wl_resource_get_id (sibling->resource));
|
|
return;
|
|
}
|
|
|
|
queue_subsurface_placement (surface,
|
|
sibling,
|
|
META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW);
|
|
}
|
|
|
|
static void
|
|
wl_subsurface_destructor (struct wl_resource *resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
|
|
if (surface->protocol_state.parent)
|
|
{
|
|
queue_subsurface_placement (surface, NULL,
|
|
META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW);
|
|
surface->protocol_state.parent = NULL;
|
|
}
|
|
else
|
|
{
|
|
g_node_unlink (surface->protocol_state.subsurface_branch_node);
|
|
}
|
|
|
|
surface->wl_subsurface = NULL;
|
|
}
|
|
|
|
static void
|
|
wl_subsurface_set_sync (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
|
|
surface->sub.synchronous = TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_subsurface_parent_desynced (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandSurface *subsurface_surface;
|
|
|
|
if (surface->sub.synchronous)
|
|
return;
|
|
|
|
if (surface->sub.transaction)
|
|
meta_wayland_transaction_commit (g_steal_pointer (&surface->sub.transaction));
|
|
|
|
META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (&surface->protocol_state,
|
|
subsurface_surface)
|
|
meta_wayland_subsurface_parent_desynced (subsurface_surface);
|
|
}
|
|
|
|
static void
|
|
wl_subsurface_set_desync (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
|
|
if (!surface->sub.synchronous)
|
|
return;
|
|
|
|
surface->sub.synchronous = FALSE;
|
|
|
|
if (!meta_wayland_surface_is_synchronized (surface))
|
|
meta_wayland_subsurface_parent_desynced (surface);
|
|
}
|
|
|
|
static const struct wl_subsurface_interface meta_wayland_wl_subsurface_interface = {
|
|
wl_subsurface_destroy,
|
|
wl_subsurface_set_position,
|
|
wl_subsurface_place_above,
|
|
wl_subsurface_place_below,
|
|
wl_subsurface_set_sync,
|
|
wl_subsurface_set_desync,
|
|
};
|
|
|
|
static void
|
|
wl_subcompositor_destroy (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
wl_resource_destroy (resource);
|
|
}
|
|
|
|
void
|
|
meta_wayland_subsurface_parent_destroyed (MetaWaylandSurface *surface)
|
|
{
|
|
queue_subsurface_placement (surface, NULL,
|
|
META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW);
|
|
surface->protocol_state.parent = NULL;
|
|
}
|
|
|
|
static gboolean
|
|
is_same_or_ancestor (MetaWaylandSurface *surface,
|
|
MetaWaylandSurface *other_surface)
|
|
{
|
|
if (surface == other_surface)
|
|
return TRUE;
|
|
if (other_surface->protocol_state.parent)
|
|
return is_same_or_ancestor (surface, other_surface->protocol_state.parent);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
wl_subcompositor_get_subsurface (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t id,
|
|
struct wl_resource *surface_resource,
|
|
struct wl_resource *parent_resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandSurface *parent = wl_resource_get_user_data (parent_resource);
|
|
MetaWaylandSurface *reference;
|
|
MetaWindow *toplevel_window;
|
|
|
|
if (surface->wl_subsurface)
|
|
{
|
|
wl_resource_post_error (surface_resource,
|
|
WL_DISPLAY_ERROR_INVALID_OBJECT,
|
|
"wl_subcompositor::get_subsurface already requested");
|
|
return;
|
|
}
|
|
|
|
if (is_same_or_ancestor (surface, parent))
|
|
{
|
|
wl_resource_post_error (resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
|
"Circular relationship between wl_surface@%d "
|
|
"and parent surface wl_surface@%d",
|
|
wl_resource_get_id (surface->resource),
|
|
wl_resource_get_id (parent->resource));
|
|
return;
|
|
}
|
|
|
|
if (!meta_wayland_surface_assign_role (surface,
|
|
META_TYPE_WAYLAND_SUBSURFACE,
|
|
NULL))
|
|
{
|
|
wl_resource_post_error (resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
|
"wl_surface@%d already has a different role",
|
|
wl_resource_get_id (surface->resource));
|
|
return;
|
|
}
|
|
|
|
toplevel_window = meta_wayland_surface_get_toplevel_window (parent);
|
|
if (toplevel_window &&
|
|
toplevel_window->client_type == META_WINDOW_CLIENT_TYPE_X11)
|
|
g_warning ("XWayland subsurfaces not currently supported");
|
|
|
|
surface->wl_subsurface =
|
|
wl_resource_create (client,
|
|
&wl_subsurface_interface,
|
|
wl_resource_get_version (resource),
|
|
id);
|
|
wl_resource_set_implementation (surface->wl_subsurface,
|
|
&meta_wayland_wl_subsurface_interface,
|
|
surface,
|
|
wl_subsurface_destructor);
|
|
|
|
surface->sub.synchronous = TRUE;
|
|
surface->protocol_state.parent = parent;
|
|
|
|
reference =
|
|
g_node_last_child (parent->protocol_state.subsurface_branch_node)->data;
|
|
queue_subsurface_placement (surface, reference,
|
|
META_WAYLAND_SUBSURFACE_PLACEMENT_ABOVE);
|
|
}
|
|
|
|
static const struct wl_subcompositor_interface meta_wayland_subcompositor_interface = {
|
|
wl_subcompositor_destroy,
|
|
wl_subcompositor_get_subsurface,
|
|
};
|
|
|
|
static void
|
|
bind_subcompositor (struct wl_client *client,
|
|
void *data,
|
|
uint32_t version,
|
|
uint32_t id)
|
|
{
|
|
struct wl_resource *resource;
|
|
|
|
resource = wl_resource_create (client, &wl_subcompositor_interface,
|
|
version, id);
|
|
wl_resource_set_implementation (resource, &meta_wayland_subcompositor_interface,
|
|
data, NULL);
|
|
}
|
|
|
|
void
|
|
meta_wayland_subsurfaces_init (MetaWaylandCompositor *compositor)
|
|
{
|
|
if (wl_global_create (compositor->wayland_display,
|
|
&wl_subcompositor_interface,
|
|
META_WL_SUBCOMPOSITOR_VERSION,
|
|
compositor, bind_subcompositor) == NULL)
|
|
g_error ("Failed to register a global wl-subcompositor object");
|
|
}
|