mirror of
https://github.com/brl/mutter.git
synced 2024-11-30 12:00:44 -05:00
2310 lines
72 KiB
C
2310 lines
72 KiB
C
/*
|
|
* Wayland Support
|
|
*
|
|
* 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-surface.h"
|
|
|
|
#include <gobject/gvaluecollector.h>
|
|
#include <wayland-server.h>
|
|
|
|
#include "backends/meta-cursor-tracker-private.h"
|
|
#include "clutter/clutter.h"
|
|
#include "cogl/cogl.h"
|
|
#include "compositor/meta-surface-actor-wayland.h"
|
|
#include "compositor/meta-surface-actor.h"
|
|
#include "compositor/meta-window-actor-private.h"
|
|
#include "compositor/region-utils.h"
|
|
#include "core/display-private.h"
|
|
#include "core/window-private.h"
|
|
#include "wayland/meta-wayland-actor-surface.h"
|
|
#include "wayland/meta-wayland-buffer.h"
|
|
#include "wayland/meta-wayland-data-device.h"
|
|
#include "wayland/meta-wayland-gtk-shell.h"
|
|
#include "wayland/meta-wayland-keyboard.h"
|
|
#include "wayland/meta-wayland-outputs.h"
|
|
#include "wayland/meta-wayland-pointer.h"
|
|
#include "wayland/meta-wayland-presentation-time-private.h"
|
|
#include "wayland/meta-wayland-private.h"
|
|
#include "wayland/meta-wayland-region.h"
|
|
#include "wayland/meta-wayland-seat.h"
|
|
#include "wayland/meta-wayland-subsurface.h"
|
|
#include "wayland/meta-wayland-transaction.h"
|
|
#include "wayland/meta-wayland-viewporter.h"
|
|
#include "wayland/meta-wayland-xdg-shell.h"
|
|
#include "wayland/meta-window-wayland.h"
|
|
#include "wayland/meta-xwayland-private.h"
|
|
#include "wayland/meta-xwayland-private.h"
|
|
|
|
enum
|
|
{
|
|
SURFACE_STATE_SIGNAL_APPLIED,
|
|
|
|
SURFACE_STATE_SIGNAL_N_SIGNALS
|
|
};
|
|
|
|
enum
|
|
{
|
|
SURFACE_ROLE_PROP_0,
|
|
|
|
SURFACE_ROLE_PROP_SURFACE,
|
|
};
|
|
|
|
static guint surface_state_signals[SURFACE_STATE_SIGNAL_N_SIGNALS];
|
|
|
|
typedef struct _MetaWaylandSurfaceRolePrivate
|
|
{
|
|
MetaWaylandSurface *surface;
|
|
} MetaWaylandSurfaceRolePrivate;
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_SCANOUT_CANDIDATE,
|
|
|
|
N_PROPS
|
|
};
|
|
|
|
static GParamSpec *obj_props[N_PROPS];
|
|
|
|
G_DEFINE_TYPE (MetaWaylandSurface, meta_wayland_surface, G_TYPE_OBJECT);
|
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaWaylandSurfaceRole,
|
|
meta_wayland_surface_role,
|
|
G_TYPE_OBJECT)
|
|
|
|
G_DEFINE_TYPE (MetaWaylandSurfaceState,
|
|
meta_wayland_surface_state,
|
|
G_TYPE_OBJECT)
|
|
|
|
enum
|
|
{
|
|
SURFACE_DESTROY,
|
|
SURFACE_UNMAPPED,
|
|
SURFACE_CONFIGURE,
|
|
SURFACE_SHORTCUTS_INHIBITED,
|
|
SURFACE_SHORTCUTS_RESTORED,
|
|
SURFACE_GEOMETRY_CHANGED,
|
|
SURFACE_PRE_STATE_APPLIED,
|
|
N_SURFACE_SIGNALS
|
|
};
|
|
|
|
guint surface_signals[N_SURFACE_SIGNALS] = { 0 };
|
|
|
|
static void
|
|
meta_wayland_surface_role_assigned (MetaWaylandSurfaceRole *surface_role);
|
|
|
|
static void
|
|
meta_wayland_surface_role_commit_state (MetaWaylandSurfaceRole *surface_role,
|
|
MetaWaylandTransaction *transaction,
|
|
MetaWaylandSurfaceState *pending);
|
|
|
|
static void
|
|
meta_wayland_surface_role_pre_apply_state (MetaWaylandSurfaceRole *surface_role,
|
|
MetaWaylandSurfaceState *pending);
|
|
|
|
static void
|
|
meta_wayland_surface_role_apply_state (MetaWaylandSurfaceRole *surface_role,
|
|
MetaWaylandSurfaceState *pending);
|
|
|
|
static void
|
|
meta_wayland_surface_role_post_apply_state (MetaWaylandSurfaceRole *surface_role,
|
|
MetaWaylandSurfaceState *pending);
|
|
|
|
static gboolean
|
|
meta_wayland_surface_role_is_on_logical_monitor (MetaWaylandSurfaceRole *surface_role,
|
|
MetaLogicalMonitor *logical_monitor);
|
|
|
|
static MetaWaylandSurface *
|
|
meta_wayland_surface_role_get_toplevel (MetaWaylandSurfaceRole *surface_role);
|
|
|
|
static void
|
|
set_surface_is_on_output (MetaWaylandSurface *surface,
|
|
MetaWaylandOutput *wayland_output,
|
|
gboolean is_on_output);
|
|
|
|
static void
|
|
role_assignment_valist_to_properties (GType role_type,
|
|
const char *first_property_name,
|
|
va_list var_args,
|
|
GArray *names,
|
|
GArray *values)
|
|
{
|
|
GObjectClass *object_class;
|
|
const char *property_name = first_property_name;
|
|
|
|
object_class = g_type_class_ref (role_type);
|
|
|
|
while (property_name)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
GParamSpec *pspec;
|
|
GType ptype;
|
|
gchar *error = NULL;
|
|
|
|
pspec = g_object_class_find_property (object_class,
|
|
property_name);
|
|
g_assert (pspec);
|
|
|
|
ptype = G_PARAM_SPEC_VALUE_TYPE (pspec);
|
|
G_VALUE_COLLECT_INIT (&value, ptype, var_args, 0, &error);
|
|
g_assert (!error);
|
|
|
|
g_array_append_val (names, property_name);
|
|
g_array_append_val (values, value);
|
|
|
|
property_name = va_arg (var_args, const char *);
|
|
}
|
|
|
|
g_type_class_unref (object_class);
|
|
}
|
|
|
|
gboolean
|
|
meta_wayland_surface_assign_role (MetaWaylandSurface *surface,
|
|
GType role_type,
|
|
const char *first_property_name,
|
|
...)
|
|
{
|
|
va_list var_args;
|
|
|
|
if (!surface->role)
|
|
{
|
|
if (first_property_name)
|
|
{
|
|
GArray *names;
|
|
GArray *values;
|
|
const char *surface_prop_name;
|
|
GValue surface_value = G_VALUE_INIT;
|
|
GObject *role_object;
|
|
|
|
names = g_array_new (FALSE, FALSE, sizeof (const char *));
|
|
values = g_array_new (FALSE, FALSE, sizeof (GValue));
|
|
g_array_set_clear_func (values, (GDestroyNotify) g_value_unset);
|
|
|
|
va_start (var_args, first_property_name);
|
|
role_assignment_valist_to_properties (role_type,
|
|
first_property_name,
|
|
var_args,
|
|
names,
|
|
values);
|
|
va_end (var_args);
|
|
|
|
surface_prop_name = "surface";
|
|
g_value_init (&surface_value, META_TYPE_WAYLAND_SURFACE);
|
|
g_value_set_object (&surface_value, surface);
|
|
g_array_append_val (names, surface_prop_name);
|
|
g_array_append_val (values, surface_value);
|
|
|
|
role_object =
|
|
g_object_new_with_properties (role_type,
|
|
values->len,
|
|
(const char **) names->data,
|
|
(const GValue *) values->data);
|
|
surface->role = META_WAYLAND_SURFACE_ROLE (role_object);
|
|
|
|
g_array_free (names, TRUE);
|
|
g_array_free (values, TRUE);
|
|
}
|
|
else
|
|
{
|
|
surface->role = g_object_new (role_type, "surface", surface, NULL);
|
|
}
|
|
|
|
meta_wayland_surface_role_assigned (surface->role);
|
|
|
|
/* Release the use count held on behalf of the just assigned role. */
|
|
if (surface->unassigned.buffer)
|
|
{
|
|
meta_wayland_buffer_dec_use_count (surface->unassigned.buffer);
|
|
g_clear_object (&surface->unassigned.buffer);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else if (G_OBJECT_TYPE (surface->role) != role_type)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
va_start (var_args, first_property_name);
|
|
g_object_set_valist (G_OBJECT (surface->role),
|
|
first_property_name, var_args);
|
|
va_end (var_args);
|
|
|
|
meta_wayland_surface_role_assigned (surface->role);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static int
|
|
get_buffer_width (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface);
|
|
|
|
if (buffer)
|
|
return cogl_texture_get_width (surface->output_state.texture);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
get_buffer_height (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface);
|
|
|
|
if (buffer)
|
|
return cogl_texture_get_height (surface->output_state.texture);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
surface_process_damage (MetaWaylandSurface *surface,
|
|
cairo_region_t *surface_region,
|
|
cairo_region_t *buffer_region)
|
|
{
|
|
MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface);
|
|
cairo_rectangle_int_t buffer_rect;
|
|
MetaSurfaceActor *actor;
|
|
|
|
/* If the client destroyed the buffer it attached before committing, but
|
|
* still posted damage, or posted damage without any buffer, don't try to
|
|
* process it on the non-existing buffer.
|
|
*/
|
|
if (!buffer)
|
|
return;
|
|
|
|
buffer_rect = (cairo_rectangle_int_t) {
|
|
.width = get_buffer_width (surface),
|
|
.height = get_buffer_height (surface),
|
|
};
|
|
|
|
if (!cairo_region_is_empty (surface_region))
|
|
{
|
|
cairo_rectangle_int_t surface_rect;
|
|
cairo_region_t *scaled_region;
|
|
cairo_region_t *transformed_region;
|
|
cairo_region_t *viewport_region;
|
|
graphene_rect_t src_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 (for example INT32_MAX which mesa passes). */
|
|
surface_rect = (cairo_rectangle_int_t) {
|
|
.width = meta_wayland_surface_get_width (surface),
|
|
.height = meta_wayland_surface_get_height (surface),
|
|
};
|
|
cairo_region_intersect_rectangle (surface_region, &surface_rect);
|
|
|
|
/* The damage region must be in the same coordinate space as the buffer,
|
|
* i.e. scaled with surface->scale. */
|
|
if (surface->viewport.has_src_rect)
|
|
{
|
|
src_rect = (graphene_rect_t) {
|
|
.origin.x = surface->viewport.src_rect.origin.x,
|
|
.origin.y = surface->viewport.src_rect.origin.y,
|
|
.size.width = surface->viewport.src_rect.size.width,
|
|
.size.height = surface->viewport.src_rect.size.height
|
|
};
|
|
}
|
|
else
|
|
{
|
|
int width, height;
|
|
|
|
if (meta_monitor_transform_is_rotated (surface->buffer_transform))
|
|
{
|
|
width = get_buffer_height (surface);
|
|
height = get_buffer_width (surface);
|
|
}
|
|
else
|
|
{
|
|
width = get_buffer_width (surface);
|
|
height = get_buffer_height (surface);
|
|
}
|
|
|
|
src_rect = (graphene_rect_t) {
|
|
.size.width = width / surface->scale,
|
|
.size.height = height / surface->scale
|
|
};
|
|
}
|
|
viewport_region = meta_region_crop_and_scale (surface_region,
|
|
&src_rect,
|
|
surface_rect.width,
|
|
surface_rect.height);
|
|
scaled_region = meta_region_scale (viewport_region, surface->scale);
|
|
transformed_region = meta_region_transform (scaled_region,
|
|
surface->buffer_transform,
|
|
buffer_rect.width,
|
|
buffer_rect.height);
|
|
|
|
/* 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_destroy (viewport_region);
|
|
cairo_region_destroy (scaled_region);
|
|
cairo_region_destroy (transformed_region);
|
|
}
|
|
|
|
cairo_region_intersect_rectangle (buffer_region, &buffer_rect);
|
|
|
|
meta_wayland_buffer_process_damage (buffer, surface->output_state.texture,
|
|
buffer_region);
|
|
|
|
actor = meta_wayland_surface_get_actor (surface);
|
|
if (actor)
|
|
{
|
|
int i, n_rectangles;
|
|
|
|
n_rectangles = cairo_region_num_rectangles (buffer_region);
|
|
for (i = 0; i < n_rectangles; i++)
|
|
{
|
|
cairo_rectangle_int_t rect;
|
|
cairo_region_get_rectangle (buffer_region, i, &rect);
|
|
|
|
meta_surface_actor_process_damage (actor,
|
|
rect.x, rect.y,
|
|
rect.width, rect.height);
|
|
}
|
|
}
|
|
}
|
|
|
|
MetaWaylandBuffer *
|
|
meta_wayland_surface_get_buffer (MetaWaylandSurface *surface)
|
|
{
|
|
return surface->buffer;
|
|
}
|
|
|
|
static void
|
|
pending_buffer_resource_destroyed (MetaWaylandBuffer *buffer,
|
|
MetaWaylandSurfaceState *pending)
|
|
{
|
|
g_clear_signal_handler (&pending->buffer_destroy_handler_id, buffer);
|
|
pending->buffer = NULL;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_state_set_default (MetaWaylandSurfaceState *state)
|
|
{
|
|
state->newly_attached = FALSE;
|
|
state->buffer = NULL;
|
|
state->texture = NULL;
|
|
state->buffer_destroy_handler_id = 0;
|
|
state->dx = 0;
|
|
state->dy = 0;
|
|
state->scale = 0;
|
|
|
|
state->input_region = NULL;
|
|
state->input_region_set = FALSE;
|
|
state->opaque_region = NULL;
|
|
state->opaque_region_set = FALSE;
|
|
|
|
state->surface_damage = cairo_region_create ();
|
|
state->buffer_damage = cairo_region_create ();
|
|
wl_list_init (&state->frame_callback_list);
|
|
|
|
state->has_new_geometry = FALSE;
|
|
state->has_acked_configure_serial = FALSE;
|
|
state->has_new_min_size = FALSE;
|
|
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;
|
|
|
|
state->subsurface_placement_ops = NULL;
|
|
|
|
wl_list_init (&state->presentation_feedback_list);
|
|
|
|
state->xdg_popup_reposition_token = 0;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_state_discard_presentation_feedback (MetaWaylandSurfaceState *state)
|
|
{
|
|
while (!wl_list_empty (&state->presentation_feedback_list))
|
|
{
|
|
MetaWaylandPresentationFeedback *feedback =
|
|
wl_container_of (state->presentation_feedback_list.next, feedback, link);
|
|
|
|
meta_wayland_presentation_feedback_discard (feedback);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_state_clear (MetaWaylandSurfaceState *state)
|
|
{
|
|
MetaWaylandFrameCallback *cb, *next;
|
|
|
|
cogl_clear_object (&state->texture);
|
|
|
|
g_clear_pointer (&state->surface_damage, cairo_region_destroy);
|
|
g_clear_pointer (&state->buffer_damage, cairo_region_destroy);
|
|
g_clear_pointer (&state->input_region, cairo_region_destroy);
|
|
g_clear_pointer (&state->opaque_region, cairo_region_destroy);
|
|
g_clear_pointer (&state->xdg_positioner, g_free);
|
|
|
|
if (state->buffer_destroy_handler_id)
|
|
{
|
|
g_clear_signal_handler (&state->buffer_destroy_handler_id, state->buffer);
|
|
state->buffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
g_clear_object (&state->buffer);
|
|
}
|
|
|
|
wl_list_for_each_safe (cb, next, &state->frame_callback_list, link)
|
|
wl_resource_destroy (cb->resource);
|
|
|
|
if (state->subsurface_placement_ops)
|
|
g_slist_free_full (state->subsurface_placement_ops, g_free);
|
|
|
|
meta_wayland_surface_state_discard_presentation_feedback (state);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_state_reset (MetaWaylandSurfaceState *state)
|
|
{
|
|
meta_wayland_surface_state_clear (state);
|
|
meta_wayland_surface_state_set_default (state);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_state_merge_into (MetaWaylandSurfaceState *from,
|
|
MetaWaylandSurfaceState *to)
|
|
{
|
|
if (from->newly_attached)
|
|
{
|
|
if (to->buffer)
|
|
{
|
|
g_warn_if_fail (to->buffer_destroy_handler_id == 0);
|
|
meta_wayland_buffer_dec_use_count (to->buffer);
|
|
g_object_unref (to->buffer);
|
|
}
|
|
|
|
to->newly_attached = TRUE;
|
|
to->buffer = g_steal_pointer (&from->buffer);
|
|
|
|
cogl_clear_object (&to->texture);
|
|
to->texture = g_steal_pointer (&from->texture);
|
|
}
|
|
|
|
to->dx += from->dx;
|
|
to->dy += from->dy;
|
|
|
|
wl_list_insert_list (&to->frame_callback_list, &from->frame_callback_list);
|
|
wl_list_init (&from->frame_callback_list);
|
|
|
|
cairo_region_union (to->surface_damage, from->surface_damage);
|
|
cairo_region_union (to->buffer_damage, from->buffer_damage);
|
|
|
|
if (from->input_region_set)
|
|
{
|
|
if (to->input_region)
|
|
cairo_region_union (to->input_region, from->input_region);
|
|
else
|
|
to->input_region = cairo_region_reference (from->input_region);
|
|
|
|
to->input_region_set = TRUE;
|
|
}
|
|
|
|
if (from->opaque_region_set)
|
|
{
|
|
if (to->opaque_region)
|
|
cairo_region_union (to->opaque_region, from->opaque_region);
|
|
else
|
|
to->opaque_region = cairo_region_reference (from->opaque_region);
|
|
|
|
to->opaque_region_set = TRUE;
|
|
}
|
|
|
|
if (from->has_new_geometry)
|
|
{
|
|
to->new_geometry = from->new_geometry;
|
|
to->has_new_geometry = TRUE;
|
|
}
|
|
|
|
if (from->has_acked_configure_serial)
|
|
{
|
|
to->acked_configure_serial = from->acked_configure_serial;
|
|
to->has_acked_configure_serial = TRUE;
|
|
}
|
|
|
|
if (from->has_new_min_size)
|
|
{
|
|
to->new_min_width = from->new_min_width;
|
|
to->new_min_height = from->new_min_height;
|
|
to->has_new_min_size = TRUE;
|
|
}
|
|
|
|
if (from->has_new_max_size)
|
|
{
|
|
to->new_max_width = from->new_max_width;
|
|
to->new_max_height = from->new_max_height;
|
|
to->has_new_max_size = TRUE;
|
|
}
|
|
|
|
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_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 (from->subsurface_placement_ops != NULL)
|
|
{
|
|
if (to->subsurface_placement_ops != NULL)
|
|
{
|
|
to->subsurface_placement_ops =
|
|
g_slist_concat (to->subsurface_placement_ops,
|
|
from->subsurface_placement_ops);
|
|
}
|
|
else
|
|
{
|
|
to->subsurface_placement_ops = from->subsurface_placement_ops;
|
|
}
|
|
|
|
from->subsurface_placement_ops = NULL;
|
|
}
|
|
|
|
/*
|
|
* A new commit indicates a new content update, so any previous
|
|
* content update did not go on screen and needs to be discarded.
|
|
*/
|
|
meta_wayland_surface_state_discard_presentation_feedback (to);
|
|
wl_list_insert_list (&to->presentation_feedback_list,
|
|
&from->presentation_feedback_list);
|
|
wl_list_init (&from->presentation_feedback_list);
|
|
|
|
if (from->xdg_positioner)
|
|
{
|
|
g_clear_pointer (&to->xdg_positioner, g_free);
|
|
to->xdg_positioner = g_steal_pointer (&from->xdg_positioner);
|
|
to->xdg_popup_reposition_token = from->xdg_popup_reposition_token;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_state_finalize (GObject *object)
|
|
{
|
|
MetaWaylandSurfaceState *state = META_WAYLAND_SURFACE_STATE (object);
|
|
|
|
meta_wayland_surface_state_clear (state);
|
|
|
|
G_OBJECT_CLASS (meta_wayland_surface_state_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_state_init (MetaWaylandSurfaceState *state)
|
|
{
|
|
meta_wayland_surface_state_set_default (state);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_state_class_init (MetaWaylandSurfaceStateClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_wayland_surface_state_finalize;
|
|
|
|
surface_state_signals[SURFACE_STATE_SIGNAL_APPLIED] =
|
|
g_signal_new ("applied",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_discard_presentation_feedback (MetaWaylandSurface *surface)
|
|
{
|
|
while (!wl_list_empty (&surface->presentation_time.feedback_list))
|
|
{
|
|
MetaWaylandPresentationFeedback *feedback =
|
|
wl_container_of (surface->presentation_time.feedback_list.next,
|
|
feedback, link);
|
|
|
|
meta_wayland_presentation_feedback_discard (feedback);
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_apply_placement_ops (MetaWaylandSurface *parent,
|
|
MetaWaylandSurfaceState *state)
|
|
{
|
|
GSList *l;
|
|
|
|
for (l = state->subsurface_placement_ops; l; l = l->next)
|
|
{
|
|
MetaWaylandSubsurfacePlacementOp *op = l->data;
|
|
MetaWaylandSurface *surface = op->surface;
|
|
GNode *sibling_node;
|
|
|
|
g_node_unlink (surface->output_state.subsurface_branch_node);
|
|
|
|
if (!op->sibling)
|
|
{
|
|
surface->output_state.parent = NULL;
|
|
continue;
|
|
}
|
|
|
|
surface->output_state.parent = parent;
|
|
|
|
if (op->sibling == parent)
|
|
sibling_node = parent->output_state.subsurface_leaf_node;
|
|
else
|
|
sibling_node = op->sibling->output_state.subsurface_branch_node;
|
|
|
|
switch (op->placement)
|
|
{
|
|
case META_WAYLAND_SUBSURFACE_PLACEMENT_ABOVE:
|
|
g_node_insert_after (parent->output_state.subsurface_branch_node,
|
|
sibling_node,
|
|
surface->output_state.subsurface_branch_node);
|
|
break;
|
|
case META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW:
|
|
g_node_insert_before (parent->output_state.subsurface_branch_node,
|
|
sibling_node,
|
|
surface->output_state.subsurface_branch_node);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_apply_state (MetaWaylandSurface *surface,
|
|
MetaWaylandSurfaceState *state)
|
|
{
|
|
gboolean had_damage = FALSE;
|
|
int old_width, old_height;
|
|
|
|
old_width = meta_wayland_surface_get_width (surface);
|
|
old_height = meta_wayland_surface_get_height (surface);
|
|
|
|
g_signal_emit (surface, surface_signals[SURFACE_PRE_STATE_APPLIED], 0);
|
|
|
|
if (surface->role)
|
|
{
|
|
meta_wayland_surface_role_pre_apply_state (surface->role, state);
|
|
}
|
|
else
|
|
{
|
|
if (state->newly_attached && surface->unassigned.buffer)
|
|
{
|
|
meta_wayland_buffer_dec_use_count (surface->unassigned.buffer);
|
|
g_clear_object (&surface->unassigned.buffer);
|
|
}
|
|
}
|
|
|
|
if (state->newly_attached)
|
|
{
|
|
/* Always release any previously held buffer. If the buffer held is same
|
|
* as the newly attached buffer, we still need to release it here, because
|
|
* wl_surface.attach+commit and wl_buffer.release on the attached buffer
|
|
* is symmetric.
|
|
*/
|
|
if (surface->buffer_held)
|
|
meta_wayland_buffer_dec_use_count (surface->buffer);
|
|
|
|
g_set_object (&surface->buffer, state->buffer);
|
|
cogl_clear_object (&surface->output_state.texture);
|
|
surface->output_state.texture = g_steal_pointer (&state->texture);
|
|
|
|
/* If the newly attached buffer is going to be accessed directly without
|
|
* making a copy, such as an EGL buffer, mark it as in-use don't release
|
|
* it until is replaced by a subsequent wl_surface.commit or when the
|
|
* wl_surface is destroyed.
|
|
*/
|
|
surface->buffer_held =
|
|
(state->buffer &&
|
|
(state->buffer->type != META_WAYLAND_BUFFER_TYPE_SHM &&
|
|
state->buffer->type != META_WAYLAND_BUFFER_TYPE_SINGLE_PIXEL));
|
|
}
|
|
|
|
if (state->scale > 0)
|
|
surface->scale = state->scale;
|
|
|
|
if ((get_buffer_width (surface) % surface->scale != 0) ||
|
|
(get_buffer_height (surface) % surface->scale != 0))
|
|
{
|
|
struct wl_resource *resource = surface->resource;
|
|
pid_t pid;
|
|
|
|
wl_client_get_credentials (wl_resource_get_client (resource), &pid, NULL,
|
|
NULL);
|
|
|
|
g_warning ("Bug in client with pid %ld: Buffer size (%dx%d) is not an"
|
|
"integer multiple of the buffer_scale (%d).", (long) pid,
|
|
get_buffer_width (surface), get_buffer_height (surface),
|
|
surface->scale);
|
|
}
|
|
|
|
if (state->has_new_buffer_transform)
|
|
surface->buffer_transform = state->buffer_transform;
|
|
|
|
if (state->has_new_viewport_src_rect)
|
|
{
|
|
surface->viewport.src_rect.origin.x = state->viewport_src_rect.origin.x;
|
|
surface->viewport.src_rect.origin.y = state->viewport_src_rect.origin.y;
|
|
surface->viewport.src_rect.size.width = state->viewport_src_rect.size.width;
|
|
surface->viewport.src_rect.size.height = state->viewport_src_rect.size.height;
|
|
surface->viewport.has_src_rect = surface->viewport.src_rect.size.width > 0;
|
|
}
|
|
|
|
if (state->has_new_viewport_dst_size)
|
|
{
|
|
surface->viewport.dst_width = state->viewport_dst_width;
|
|
surface->viewport.dst_height = state->viewport_dst_height;
|
|
surface->viewport.has_dst_size = surface->viewport.dst_width > 0;
|
|
}
|
|
|
|
state->derived.surface_size_changed =
|
|
meta_wayland_surface_get_width (surface) != old_width ||
|
|
meta_wayland_surface_get_height (surface) != old_height;
|
|
|
|
if (!cairo_region_is_empty (state->surface_damage) ||
|
|
!cairo_region_is_empty (state->buffer_damage))
|
|
{
|
|
surface_process_damage (surface,
|
|
state->surface_damage,
|
|
state->buffer_damage);
|
|
had_damage = TRUE;
|
|
}
|
|
|
|
surface->offset_x += state->dx;
|
|
surface->offset_y += state->dy;
|
|
|
|
if (state->opaque_region_set)
|
|
{
|
|
if (surface->opaque_region)
|
|
cairo_region_destroy (surface->opaque_region);
|
|
if (state->opaque_region)
|
|
surface->opaque_region = cairo_region_reference (state->opaque_region);
|
|
else
|
|
surface->opaque_region = NULL;
|
|
}
|
|
|
|
if (state->input_region_set)
|
|
{
|
|
if (surface->input_region)
|
|
cairo_region_destroy (surface->input_region);
|
|
if (state->input_region)
|
|
surface->input_region = cairo_region_reference (state->input_region);
|
|
else
|
|
surface->input_region = NULL;
|
|
}
|
|
|
|
/*
|
|
* A new commit indicates a new content update, so any previous
|
|
* content update did not go on screen and needs to be discarded.
|
|
*/
|
|
meta_wayland_surface_discard_presentation_feedback (surface);
|
|
|
|
wl_list_insert_list (&surface->presentation_time.feedback_list,
|
|
&state->presentation_feedback_list);
|
|
wl_list_init (&state->presentation_feedback_list);
|
|
|
|
if (!wl_list_empty (&surface->presentation_time.feedback_list))
|
|
meta_wayland_compositor_add_presentation_feedback_surface (surface->compositor,
|
|
surface);
|
|
|
|
if (surface->role)
|
|
{
|
|
meta_wayland_surface_role_apply_state (surface->role, state);
|
|
g_assert (wl_list_empty (&state->frame_callback_list));
|
|
}
|
|
else
|
|
{
|
|
wl_list_insert_list (surface->unassigned.pending_frame_callback_list.prev,
|
|
&state->frame_callback_list);
|
|
wl_list_init (&state->frame_callback_list);
|
|
|
|
if (state->buffer)
|
|
{
|
|
/* The need to keep the wl_buffer from being released depends on what
|
|
* role the surface is given. That means we need to also keep a use
|
|
* count for wl_buffer's that are used by unassigned wl_surface's.
|
|
*/
|
|
surface->unassigned.buffer = g_object_ref (state->buffer);
|
|
meta_wayland_buffer_inc_use_count (surface->unassigned.buffer);
|
|
}
|
|
}
|
|
|
|
if (state->subsurface_placement_ops)
|
|
meta_wayland_surface_notify_subsurface_state_changed (surface);
|
|
|
|
/* If we need to hold the newly attached buffer, drop its reference from the
|
|
* state, to prevent meta_wayland_transaction_entry_destroy from decreasing
|
|
* the use count.
|
|
*/
|
|
if (state->newly_attached && surface->buffer_held)
|
|
g_clear_object (&state->buffer);
|
|
|
|
g_signal_emit (state,
|
|
surface_state_signals[SURFACE_STATE_SIGNAL_APPLIED],
|
|
0);
|
|
|
|
if (had_damage)
|
|
{
|
|
MetaWindow *toplevel_window;
|
|
|
|
toplevel_window = meta_wayland_surface_get_toplevel_window (surface);
|
|
if (toplevel_window)
|
|
{
|
|
MetaWindowActor *toplevel_window_actor;
|
|
|
|
toplevel_window_actor =
|
|
meta_window_actor_from_window (toplevel_window);
|
|
if (toplevel_window_actor)
|
|
meta_window_actor_notify_damaged (toplevel_window_actor);
|
|
}
|
|
}
|
|
|
|
if (surface->role)
|
|
meta_wayland_surface_role_post_apply_state (surface->role, state);
|
|
}
|
|
|
|
MetaWaylandSurfaceState *
|
|
meta_wayland_surface_get_pending_state (MetaWaylandSurface *surface)
|
|
{
|
|
return surface->pending_state;
|
|
}
|
|
|
|
MetaWaylandTransaction *
|
|
meta_wayland_surface_ensure_transaction (MetaWaylandSurface *surface)
|
|
{
|
|
if (!surface->sub.transaction)
|
|
surface->sub.transaction = meta_wayland_transaction_new (surface->compositor);
|
|
|
|
return surface->sub.transaction;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_commit (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
MetaWaylandBuffer *buffer = pending->buffer;
|
|
MetaWaylandTransaction *transaction;
|
|
MetaWaylandSurface *subsurface_surface;
|
|
|
|
COGL_TRACE_BEGIN_SCOPED (MetaWaylandSurfaceCommit,
|
|
"WaylandSurface (commit)");
|
|
|
|
if (buffer)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
g_clear_signal_handler (&pending->buffer_destroy_handler_id,
|
|
buffer);
|
|
|
|
if (!meta_wayland_buffer_is_realized (buffer))
|
|
meta_wayland_buffer_realize (buffer);
|
|
|
|
if (!meta_wayland_buffer_attach (buffer,
|
|
&surface->protocol_state.texture,
|
|
&error))
|
|
{
|
|
g_warning ("Could not import pending buffer: %s", error->message);
|
|
|
|
wl_resource_post_error (surface->resource, WL_DISPLAY_ERROR_NO_MEMORY,
|
|
"Failed to attach buffer to surface %i: %s",
|
|
wl_resource_get_id (surface->resource),
|
|
error->message);
|
|
return;
|
|
}
|
|
|
|
pending->texture = cogl_object_ref (surface->protocol_state.texture);
|
|
|
|
g_object_ref (buffer);
|
|
meta_wayland_buffer_inc_use_count (buffer);
|
|
}
|
|
else if (pending->newly_attached)
|
|
{
|
|
cogl_clear_object (&surface->protocol_state.texture);
|
|
}
|
|
|
|
if (meta_wayland_surface_is_synchronized (surface))
|
|
transaction = meta_wayland_surface_ensure_transaction (surface);
|
|
else
|
|
transaction = meta_wayland_transaction_new (surface->compositor);
|
|
|
|
if (surface->role)
|
|
meta_wayland_surface_role_commit_state (surface->role, transaction, pending);
|
|
|
|
meta_wayland_transaction_merge_pending_state (transaction, surface);
|
|
|
|
META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (&surface->protocol_state,
|
|
subsurface_surface)
|
|
{
|
|
if (!subsurface_surface->sub.transaction)
|
|
continue;
|
|
|
|
meta_wayland_transaction_merge_into (subsurface_surface->sub.transaction,
|
|
transaction);
|
|
subsurface_surface->sub.transaction = NULL;
|
|
}
|
|
|
|
/*
|
|
* If this is a sub-surface and it is in effective synchronous mode, only
|
|
* cache the pending surface state until either one of the following two
|
|
* scenarios happens:
|
|
* 1) Its parent surface gets its state applied.
|
|
* 2) Its mode changes from synchronized to desynchronized and its parent
|
|
* surface is in effective desynchronized mode.
|
|
*/
|
|
if (!meta_wayland_surface_is_synchronized (surface))
|
|
meta_wayland_transaction_commit (transaction);
|
|
}
|
|
|
|
static void
|
|
wl_surface_destroy (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
wl_resource_destroy (resource);
|
|
}
|
|
|
|
static void
|
|
wl_surface_attach (struct wl_client *client,
|
|
struct wl_resource *surface_resource,
|
|
struct wl_resource *buffer_resource,
|
|
int32_t dx,
|
|
int32_t dy)
|
|
{
|
|
MetaWaylandSurface *surface =
|
|
wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandCompositor *compositor = surface->compositor;
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
MetaWaylandBuffer *buffer;
|
|
|
|
if (buffer_resource)
|
|
buffer = meta_wayland_buffer_from_resource (compositor, buffer_resource);
|
|
else
|
|
buffer = NULL;
|
|
|
|
if (surface->pending_state->buffer)
|
|
{
|
|
g_clear_signal_handler (&pending->buffer_destroy_handler_id,
|
|
pending->buffer);
|
|
}
|
|
|
|
if (wl_resource_get_version (surface_resource) >=
|
|
WL_SURFACE_OFFSET_SINCE_VERSION &&
|
|
(dx != 0 || dy != 0))
|
|
{
|
|
wl_resource_post_error (surface_resource,
|
|
WL_SURFACE_ERROR_INVALID_OFFSET,
|
|
"Attaching with an offset is no longer allowed");
|
|
return;
|
|
}
|
|
|
|
pending->newly_attached = TRUE;
|
|
pending->buffer = buffer;
|
|
pending->dx = dx;
|
|
pending->dy = dy;
|
|
|
|
if (buffer)
|
|
{
|
|
pending->buffer_destroy_handler_id =
|
|
g_signal_connect (buffer, "resource-destroyed",
|
|
G_CALLBACK (pending_buffer_resource_destroyed),
|
|
pending);
|
|
}
|
|
}
|
|
|
|
static void
|
|
wl_surface_damage (struct wl_client *client,
|
|
struct wl_resource *surface_resource,
|
|
int32_t x,
|
|
int32_t y,
|
|
int32_t width,
|
|
int32_t height)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
cairo_rectangle_int_t rectangle;
|
|
|
|
rectangle = (cairo_rectangle_int_t) {
|
|
.x = x,
|
|
.y = y,
|
|
.width = width,
|
|
.height = height
|
|
};
|
|
cairo_region_union_rectangle (pending->surface_damage, &rectangle);
|
|
}
|
|
|
|
static void
|
|
destroy_frame_callback (struct wl_resource *callback_resource)
|
|
{
|
|
MetaWaylandFrameCallback *callback =
|
|
wl_resource_get_user_data (callback_resource);
|
|
|
|
wl_list_remove (&callback->link);
|
|
g_free (callback);
|
|
}
|
|
|
|
static void
|
|
wl_surface_frame (struct wl_client *client,
|
|
struct wl_resource *surface_resource,
|
|
uint32_t callback_id)
|
|
{
|
|
MetaWaylandFrameCallback *callback;
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
|
|
callback = g_new0 (MetaWaylandFrameCallback, 1);
|
|
callback->surface = surface;
|
|
callback->resource = wl_resource_create (client,
|
|
&wl_callback_interface,
|
|
META_WL_CALLBACK_VERSION,
|
|
callback_id);
|
|
wl_resource_set_implementation (callback->resource, NULL, callback,
|
|
destroy_frame_callback);
|
|
|
|
wl_list_insert (pending->frame_callback_list.prev, &callback->link);
|
|
}
|
|
|
|
static void
|
|
wl_surface_set_opaque_region (struct wl_client *client,
|
|
struct wl_resource *surface_resource,
|
|
struct wl_resource *region_resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
|
|
g_clear_pointer (&pending->opaque_region, cairo_region_destroy);
|
|
if (region_resource)
|
|
{
|
|
MetaWaylandRegion *region = wl_resource_get_user_data (region_resource);
|
|
cairo_region_t *cr_region = meta_wayland_region_peek_cairo_region (region);
|
|
pending->opaque_region = cairo_region_copy (cr_region);
|
|
}
|
|
pending->opaque_region_set = TRUE;
|
|
}
|
|
|
|
static void
|
|
wl_surface_set_input_region (struct wl_client *client,
|
|
struct wl_resource *surface_resource,
|
|
struct wl_resource *region_resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
|
|
g_clear_pointer (&pending->input_region, cairo_region_destroy);
|
|
if (region_resource)
|
|
{
|
|
MetaWaylandRegion *region = wl_resource_get_user_data (region_resource);
|
|
cairo_region_t *cr_region = meta_wayland_region_peek_cairo_region (region);
|
|
pending->input_region = cairo_region_copy (cr_region);
|
|
}
|
|
pending->input_region_set = TRUE;
|
|
}
|
|
|
|
static void
|
|
wl_surface_commit (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
|
|
meta_wayland_surface_commit (surface);
|
|
}
|
|
|
|
static MetaMonitorTransform
|
|
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;
|
|
}
|
|
}
|
|
|
|
static void
|
|
wl_surface_set_buffer_transform (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
int32_t transform)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
MetaMonitorTransform buffer_transform;
|
|
|
|
buffer_transform = 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",
|
|
transform);
|
|
return;
|
|
}
|
|
|
|
pending->buffer_transform = buffer_transform;
|
|
pending->has_new_buffer_transform = TRUE;
|
|
}
|
|
|
|
static void
|
|
wl_surface_set_buffer_scale (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
int scale)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
|
|
if (scale <= 0)
|
|
{
|
|
wl_resource_post_error (resource,
|
|
WL_SURFACE_ERROR_INVALID_SCALE,
|
|
"Trying to set invalid buffer_scale of %d",
|
|
scale);
|
|
return;
|
|
}
|
|
|
|
pending->scale = scale;
|
|
}
|
|
|
|
static void
|
|
wl_surface_damage_buffer (struct wl_client *client,
|
|
struct wl_resource *surface_resource,
|
|
int32_t x,
|
|
int32_t y,
|
|
int32_t width,
|
|
int32_t height)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
cairo_rectangle_int_t rectangle;
|
|
|
|
rectangle = (cairo_rectangle_int_t) {
|
|
.x = x,
|
|
.y = y,
|
|
.width = width,
|
|
.height = height
|
|
};
|
|
cairo_region_union_rectangle (pending->buffer_damage, &rectangle);
|
|
}
|
|
|
|
static void
|
|
wl_surface_offset (struct wl_client *client,
|
|
struct wl_resource *surface_resource,
|
|
int32_t dx,
|
|
int32_t dy)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandSurfaceState *pending = surface->pending_state;
|
|
|
|
pending->dx = dx;
|
|
pending->dy = dy;
|
|
}
|
|
|
|
static const struct wl_surface_interface meta_wayland_wl_surface_interface = {
|
|
wl_surface_destroy,
|
|
wl_surface_attach,
|
|
wl_surface_damage,
|
|
wl_surface_frame,
|
|
wl_surface_set_opaque_region,
|
|
wl_surface_set_input_region,
|
|
wl_surface_commit,
|
|
wl_surface_set_buffer_transform,
|
|
wl_surface_set_buffer_scale,
|
|
wl_surface_damage_buffer,
|
|
wl_surface_offset,
|
|
};
|
|
|
|
static void
|
|
handle_output_destroyed (MetaWaylandOutput *wayland_output,
|
|
MetaWaylandSurface *surface)
|
|
{
|
|
set_surface_is_on_output (surface, wayland_output, FALSE);
|
|
}
|
|
|
|
static void
|
|
handle_output_bound (MetaWaylandOutput *wayland_output,
|
|
struct wl_resource *output_resource,
|
|
MetaWaylandSurface *surface)
|
|
{
|
|
if (wl_resource_get_client (output_resource) ==
|
|
wl_resource_get_client (surface->resource))
|
|
wl_surface_send_enter (surface->resource, output_resource);
|
|
}
|
|
|
|
static void
|
|
surface_entered_output (MetaWaylandSurface *surface,
|
|
MetaWaylandOutput *wayland_output)
|
|
{
|
|
g_signal_connect (wayland_output, "output-destroyed",
|
|
G_CALLBACK (handle_output_destroyed),
|
|
surface);
|
|
|
|
if (surface->resource)
|
|
{
|
|
const GList *l;
|
|
|
|
for (l = meta_wayland_output_get_resources (wayland_output); l; l = l->next)
|
|
{
|
|
struct wl_resource *resource = l->data;
|
|
|
|
if (wl_resource_get_client (resource) !=
|
|
wl_resource_get_client (surface->resource))
|
|
continue;
|
|
|
|
wl_surface_send_enter (surface->resource, resource);
|
|
}
|
|
}
|
|
|
|
g_signal_connect (wayland_output, "output-bound",
|
|
G_CALLBACK (handle_output_bound),
|
|
surface);
|
|
}
|
|
|
|
static void
|
|
surface_left_output (MetaWaylandSurface *surface,
|
|
MetaWaylandOutput *wayland_output)
|
|
{
|
|
const GList *l;
|
|
|
|
g_signal_handlers_disconnect_by_func (wayland_output,
|
|
G_CALLBACK (handle_output_destroyed),
|
|
surface);
|
|
|
|
g_signal_handlers_disconnect_by_func (wayland_output,
|
|
G_CALLBACK (handle_output_bound),
|
|
surface);
|
|
|
|
if (!surface->resource)
|
|
return;
|
|
|
|
for (l = meta_wayland_output_get_resources (wayland_output); l; l = l->next)
|
|
{
|
|
struct wl_resource *resource = l->data;
|
|
|
|
if (wl_resource_get_client (resource) !=
|
|
wl_resource_get_client (surface->resource))
|
|
continue;
|
|
|
|
wl_surface_send_leave (surface->resource, resource);
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_surface_is_on_output (MetaWaylandSurface *surface,
|
|
MetaWaylandOutput *wayland_output,
|
|
gboolean is_on_output)
|
|
{
|
|
gboolean was_on_output;
|
|
|
|
was_on_output = g_hash_table_contains (surface->outputs, wayland_output);
|
|
|
|
if (!was_on_output && is_on_output)
|
|
{
|
|
g_hash_table_add (surface->outputs, wayland_output);
|
|
surface_entered_output (surface, wayland_output);
|
|
}
|
|
else if (was_on_output && !is_on_output)
|
|
{
|
|
g_hash_table_remove (surface->outputs, wayland_output);
|
|
surface_left_output (surface, wayland_output);
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_surface_output_state (gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
MetaWaylandOutput *wayland_output = value;
|
|
MetaWaylandSurface *surface = user_data;
|
|
MetaLogicalMonitor *logical_monitor;
|
|
gboolean is_on_logical_monitor;
|
|
|
|
g_assert (surface->role);
|
|
|
|
logical_monitor = meta_wayland_output_get_logical_monitor (wayland_output);
|
|
if (!logical_monitor)
|
|
{
|
|
set_surface_is_on_output (surface, wayland_output, FALSE);
|
|
return;
|
|
}
|
|
|
|
is_on_logical_monitor =
|
|
meta_wayland_surface_role_is_on_logical_monitor (surface->role,
|
|
logical_monitor);
|
|
set_surface_is_on_output (surface, wayland_output, is_on_logical_monitor);
|
|
}
|
|
|
|
static void
|
|
surface_output_disconnect_signals (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
MetaWaylandOutput *wayland_output = key;
|
|
MetaWaylandSurface *surface = user_data;
|
|
|
|
g_signal_handlers_disconnect_by_func (wayland_output,
|
|
G_CALLBACK (handle_output_destroyed),
|
|
surface);
|
|
|
|
g_signal_handlers_disconnect_by_func (wayland_output,
|
|
G_CALLBACK (handle_output_bound),
|
|
surface);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_update_outputs (MetaWaylandSurface *surface)
|
|
{
|
|
if (!surface->compositor)
|
|
return;
|
|
|
|
g_hash_table_foreach (surface->compositor->outputs,
|
|
update_surface_output_state,
|
|
surface);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_notify_unmapped (MetaWaylandSurface *surface)
|
|
{
|
|
g_signal_emit (surface, surface_signals[SURFACE_UNMAPPED], 0);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_finalize (GObject *object)
|
|
{
|
|
MetaWaylandSurface *surface = META_WAYLAND_SURFACE (object);
|
|
MetaWaylandCompositor *compositor = surface->compositor;
|
|
MetaWaylandFrameCallback *cb, *next;
|
|
|
|
g_clear_object (&surface->scanout_candidate);
|
|
g_clear_object (&surface->role);
|
|
|
|
if (surface->unassigned.buffer)
|
|
{
|
|
meta_wayland_buffer_dec_use_count (surface->unassigned.buffer);
|
|
g_clear_object (&surface->unassigned.buffer);
|
|
}
|
|
|
|
if (surface->buffer_held)
|
|
meta_wayland_buffer_dec_use_count (surface->buffer);
|
|
g_clear_pointer (&surface->output_state.texture, cogl_object_unref);
|
|
g_clear_object (&surface->buffer);
|
|
|
|
if (surface->opaque_region)
|
|
cairo_region_destroy (surface->opaque_region);
|
|
if (surface->input_region)
|
|
cairo_region_destroy (surface->input_region);
|
|
|
|
meta_wayland_compositor_remove_frame_callback_surface (compositor, surface);
|
|
meta_wayland_compositor_remove_presentation_feedback_surface (compositor,
|
|
surface);
|
|
|
|
g_hash_table_foreach (surface->outputs,
|
|
surface_output_disconnect_signals,
|
|
surface);
|
|
g_hash_table_destroy (surface->outputs);
|
|
|
|
wl_list_for_each_safe (cb, next,
|
|
&surface->unassigned.pending_frame_callback_list,
|
|
link)
|
|
wl_resource_destroy (cb->resource);
|
|
|
|
meta_wayland_surface_discard_presentation_feedback (surface);
|
|
|
|
g_clear_pointer (&surface->output_state.subsurface_branch_node, g_node_destroy);
|
|
|
|
g_hash_table_destroy (surface->shortcut_inhibited_seats);
|
|
|
|
G_OBJECT_CLASS (meta_wayland_surface_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
wl_surface_destructor (struct wl_resource *resource)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
|
MetaWaylandSurface *subsurface_surface;
|
|
|
|
g_signal_emit (surface, surface_signals[SURFACE_DESTROY], 0);
|
|
|
|
g_clear_object (&surface->pending_state);
|
|
g_clear_pointer (&surface->sub.transaction, meta_wayland_transaction_free);
|
|
|
|
if (surface->resource)
|
|
wl_resource_set_user_data (g_steal_pointer (&surface->resource), NULL);
|
|
|
|
META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (&surface->protocol_state,
|
|
subsurface_surface)
|
|
meta_wayland_subsurface_parent_destroyed (subsurface_surface);
|
|
|
|
g_clear_pointer (&surface->wl_subsurface, wl_resource_destroy);
|
|
g_clear_pointer (&surface->protocol_state.subsurface_branch_node, g_node_destroy);
|
|
|
|
cogl_clear_object (&surface->protocol_state.texture);
|
|
|
|
/*
|
|
* Any transactions referencing this surface will keep it alive until they get
|
|
* applied/destroyed. The last reference will be dropped in
|
|
* meta_wayland_transaction_free.
|
|
*/
|
|
g_object_unref (surface);
|
|
}
|
|
|
|
MetaWaylandSurface *
|
|
meta_wayland_surface_create (MetaWaylandCompositor *compositor,
|
|
struct wl_client *client,
|
|
struct wl_resource *compositor_resource,
|
|
guint32 id)
|
|
{
|
|
MetaWaylandSurface *surface = g_object_new (META_TYPE_WAYLAND_SURFACE, NULL);
|
|
int surface_version;
|
|
|
|
surface->compositor = compositor;
|
|
surface->scale = 1;
|
|
|
|
surface_version = wl_resource_get_version (compositor_resource);
|
|
surface->resource = wl_resource_create (client,
|
|
&wl_surface_interface,
|
|
surface_version,
|
|
id);
|
|
wl_resource_set_implementation (surface->resource,
|
|
&meta_wayland_wl_surface_interface,
|
|
surface,
|
|
wl_surface_destructor);
|
|
|
|
wl_list_init (&surface->unassigned.pending_frame_callback_list);
|
|
|
|
surface->outputs = g_hash_table_new (NULL, NULL);
|
|
surface->shortcut_inhibited_seats = g_hash_table_new (NULL, NULL);
|
|
|
|
wl_list_init (&surface->presentation_time.feedback_list);
|
|
|
|
meta_wayland_compositor_notify_surface_id (compositor, id, surface);
|
|
|
|
return surface;
|
|
}
|
|
|
|
gboolean
|
|
meta_wayland_surface_begin_grab_op (MetaWaylandSurface *surface,
|
|
MetaWaylandSeat *seat,
|
|
MetaGrabOp grab_op,
|
|
gfloat x,
|
|
gfloat y)
|
|
{
|
|
MetaWindow *window = meta_wayland_surface_get_window (surface);
|
|
|
|
if (grab_op == META_GRAB_OP_NONE)
|
|
return FALSE;
|
|
|
|
/* This is an input driven operation so we set frame_action to
|
|
constrain it in the same way as it would be if the window was
|
|
being moved/resized via a SSD event. */
|
|
return meta_display_begin_grab_op (window->display,
|
|
window,
|
|
grab_op,
|
|
TRUE, /* pointer_already_grabbed */
|
|
TRUE, /* frame_action */
|
|
1, /* button. XXX? */
|
|
0, /* modmask */
|
|
meta_display_get_current_time_roundtrip (window->display),
|
|
x, y);
|
|
}
|
|
|
|
/**
|
|
* meta_wayland_shell_init:
|
|
* @compositor: The #MetaWaylandCompositor object
|
|
*
|
|
* Initializes the Wayland interfaces providing features that deal with
|
|
* desktop-specific conundrums, like XDG shell, etc.
|
|
*/
|
|
void
|
|
meta_wayland_shell_init (MetaWaylandCompositor *compositor)
|
|
{
|
|
meta_wayland_xdg_shell_init (compositor);
|
|
meta_wayland_init_gtk_shell (compositor);
|
|
meta_wayland_init_viewporter (compositor);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_configure_notify (MetaWaylandSurface *surface,
|
|
MetaWaylandWindowConfiguration *configuration)
|
|
{
|
|
MetaWaylandShellSurface *shell_surface =
|
|
META_WAYLAND_SHELL_SURFACE (surface->role);
|
|
|
|
g_signal_emit (surface, surface_signals[SURFACE_CONFIGURE], 0);
|
|
|
|
meta_wayland_shell_surface_configure (shell_surface, configuration);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_ping (MetaWaylandSurface *surface,
|
|
guint32 serial)
|
|
{
|
|
MetaWaylandShellSurface *shell_surface =
|
|
META_WAYLAND_SHELL_SURFACE (surface->role);
|
|
|
|
meta_wayland_shell_surface_ping (shell_surface, serial);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_delete (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandShellSurface *shell_surface =
|
|
META_WAYLAND_SHELL_SURFACE (surface->role);
|
|
|
|
meta_wayland_shell_surface_close (shell_surface);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_window_managed (MetaWaylandSurface *surface,
|
|
MetaWindow *window)
|
|
{
|
|
MetaWaylandShellSurface *shell_surface =
|
|
META_WAYLAND_SHELL_SURFACE (surface->role);
|
|
|
|
meta_wayland_shell_surface_managed (shell_surface, window);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_drag_dest_focus_in (MetaWaylandSurface *surface,
|
|
MetaWaylandDataOffer *offer)
|
|
{
|
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
|
MetaWaylandDataDevice *data_device = &compositor->seat->data_device;
|
|
|
|
surface->dnd.funcs->focus_in (data_device, surface, offer);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_drag_dest_motion (MetaWaylandSurface *surface,
|
|
const ClutterEvent *event)
|
|
{
|
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
|
MetaWaylandDataDevice *data_device = &compositor->seat->data_device;
|
|
|
|
surface->dnd.funcs->motion (data_device, surface, event);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_drag_dest_focus_out (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
|
MetaWaylandDataDevice *data_device = &compositor->seat->data_device;
|
|
|
|
surface->dnd.funcs->focus_out (data_device, surface);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_drag_dest_drop (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
|
MetaWaylandDataDevice *data_device = &compositor->seat->data_device;
|
|
|
|
surface->dnd.funcs->drop (data_device, surface);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_drag_dest_update (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
|
MetaWaylandDataDevice *data_device = &compositor->seat->data_device;
|
|
|
|
surface->dnd.funcs->update (data_device, surface);
|
|
}
|
|
|
|
MetaWaylandSurface *
|
|
meta_wayland_surface_get_toplevel (MetaWaylandSurface *surface)
|
|
{
|
|
if (surface->role)
|
|
return meta_wayland_surface_role_get_toplevel (surface->role);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
MetaWindow *
|
|
meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandSurface *toplevel;
|
|
|
|
toplevel = meta_wayland_surface_get_toplevel (surface);
|
|
if (toplevel)
|
|
return meta_wayland_surface_get_window (toplevel);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface,
|
|
float abs_x,
|
|
float abs_y,
|
|
float *sx,
|
|
float *sy)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *surface_role_class =
|
|
META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface->role);
|
|
|
|
surface_role_class->get_relative_coordinates (surface->role,
|
|
abs_x, abs_y,
|
|
sx, sy);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface,
|
|
float sx,
|
|
float sy,
|
|
float *x,
|
|
float *y)
|
|
{
|
|
ClutterActor *actor =
|
|
CLUTTER_ACTOR (meta_wayland_surface_get_actor (surface));
|
|
graphene_point3d_t sv = {
|
|
.x = sx,
|
|
.y = sy,
|
|
};
|
|
graphene_point3d_t v = { 0 };
|
|
|
|
clutter_actor_apply_relative_transform_to_point (actor, NULL, &sv, &v);
|
|
|
|
*x = v.x;
|
|
*y = v.y;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_init (MetaWaylandSurface *surface)
|
|
{
|
|
surface->pending_state = meta_wayland_surface_state_new ();
|
|
|
|
surface->output_state.subsurface_branch_node = g_node_new (surface);
|
|
surface->output_state.subsurface_leaf_node =
|
|
g_node_prepend_data (surface->output_state.subsurface_branch_node, surface);
|
|
|
|
surface->protocol_state.subsurface_branch_node = g_node_new (surface);
|
|
surface->protocol_state.subsurface_leaf_node =
|
|
g_node_prepend_data (surface->protocol_state.subsurface_branch_node, surface);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaWaylandSurface *surface = META_WAYLAND_SURFACE (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_SCANOUT_CANDIDATE:
|
|
g_value_set_object (value, surface->scanout_candidate);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_class_init (MetaWaylandSurfaceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_wayland_surface_finalize;
|
|
object_class->get_property = meta_wayland_surface_get_property;
|
|
|
|
obj_props[PROP_SCANOUT_CANDIDATE] =
|
|
g_param_spec_object ("scanout-candidate",
|
|
"scanout-candidate",
|
|
"Scanout candidate for given CRTC",
|
|
META_TYPE_CRTC,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
g_object_class_install_properties (object_class, N_PROPS, obj_props);
|
|
|
|
surface_signals[SURFACE_DESTROY] =
|
|
g_signal_new ("destroy",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
surface_signals[SURFACE_UNMAPPED] =
|
|
g_signal_new ("unmapped",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
surface_signals[SURFACE_CONFIGURE] =
|
|
g_signal_new ("configure",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
surface_signals[SURFACE_SHORTCUTS_INHIBITED] =
|
|
g_signal_new ("shortcuts-inhibited",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
surface_signals[SURFACE_SHORTCUTS_RESTORED] =
|
|
g_signal_new ("shortcuts-restored",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
surface_signals[SURFACE_GEOMETRY_CHANGED] =
|
|
g_signal_new ("geometry-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
surface_signals[SURFACE_PRE_STATE_APPLIED] =
|
|
g_signal_new ("pre-state-applied",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (object);
|
|
MetaWaylandSurfaceRolePrivate *priv =
|
|
meta_wayland_surface_role_get_instance_private (surface_role);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case SURFACE_ROLE_PROP_SURFACE:
|
|
priv->surface = g_value_get_object (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (object);
|
|
MetaWaylandSurfaceRolePrivate *priv =
|
|
meta_wayland_surface_role_get_instance_private (surface_role);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case SURFACE_ROLE_PROP_SURFACE:
|
|
g_value_set_object (value, priv->surface);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_init (MetaWaylandSurfaceRole *role)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_class_init (MetaWaylandSurfaceRoleClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->set_property = meta_wayland_surface_role_set_property;
|
|
object_class->get_property = meta_wayland_surface_role_get_property;
|
|
|
|
g_object_class_install_property (object_class,
|
|
SURFACE_ROLE_PROP_SURFACE,
|
|
g_param_spec_object ("surface",
|
|
"MetaWaylandSurface",
|
|
"The MetaWaylandSurface instance",
|
|
META_TYPE_WAYLAND_SURFACE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_assigned (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role)->assigned (surface_role);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_commit_state (MetaWaylandSurfaceRole *surface_role,
|
|
MetaWaylandTransaction *transaction,
|
|
MetaWaylandSurfaceState *pending)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *klass;
|
|
|
|
klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role);
|
|
if (klass->commit_state)
|
|
klass->commit_state (surface_role, transaction, pending);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_pre_apply_state (MetaWaylandSurfaceRole *surface_role,
|
|
MetaWaylandSurfaceState *pending)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *klass;
|
|
|
|
klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role);
|
|
if (klass->pre_apply_state)
|
|
klass->pre_apply_state (surface_role, pending);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_post_apply_state (MetaWaylandSurfaceRole *surface_role,
|
|
MetaWaylandSurfaceState *pending)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *klass;
|
|
|
|
klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role);
|
|
if (klass->post_apply_state)
|
|
klass->post_apply_state (surface_role, pending);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_apply_state (MetaWaylandSurfaceRole *surface_role,
|
|
MetaWaylandSurfaceState *pending)
|
|
{
|
|
META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role)->apply_state (surface_role,
|
|
pending);
|
|
}
|
|
|
|
static gboolean
|
|
meta_wayland_surface_role_is_on_logical_monitor (MetaWaylandSurfaceRole *surface_role,
|
|
MetaLogicalMonitor *logical_monitor)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *klass;
|
|
|
|
klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role);
|
|
if (klass->is_on_logical_monitor)
|
|
return klass->is_on_logical_monitor (surface_role, logical_monitor);
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
static MetaWaylandSurface *
|
|
meta_wayland_surface_role_get_toplevel (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *klass;
|
|
|
|
klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role);
|
|
if (klass->get_toplevel)
|
|
return klass->get_toplevel (surface_role);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static MetaWindow *
|
|
meta_wayland_surface_role_get_window (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *klass;
|
|
|
|
klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role);
|
|
|
|
if (klass->get_window)
|
|
return klass->get_window (surface_role);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
MetaWindow *
|
|
meta_wayland_surface_get_window (MetaWaylandSurface *surface)
|
|
{
|
|
if (!surface->role)
|
|
return NULL;
|
|
|
|
return meta_wayland_surface_role_get_window (surface->role);
|
|
}
|
|
|
|
static gboolean
|
|
meta_wayland_surface_role_is_synchronized (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *klass;
|
|
|
|
klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role);
|
|
if (klass->is_synchronized)
|
|
return klass->is_synchronized (surface_role);
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
meta_wayland_surface_is_synchronized (MetaWaylandSurface *surface)
|
|
{
|
|
if (!surface->role)
|
|
return FALSE;
|
|
|
|
return meta_wayland_surface_role_is_synchronized (surface->role);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_surface_role_notify_subsurface_state_changed (MetaWaylandSurfaceRole *surface_role)
|
|
{
|
|
MetaWaylandSurfaceRoleClass *klass;
|
|
|
|
klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role);
|
|
g_return_if_fail (klass->notify_subsurface_state_changed);
|
|
|
|
klass->notify_subsurface_state_changed (surface_role);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_notify_subsurface_state_changed (MetaWaylandSurface *surface)
|
|
{
|
|
if (surface->role)
|
|
meta_wayland_surface_role_notify_subsurface_state_changed (surface->role);
|
|
}
|
|
|
|
MetaWaylandSurface *
|
|
meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role)
|
|
{
|
|
MetaWaylandSurfaceRolePrivate *priv =
|
|
meta_wayland_surface_role_get_instance_private (role);
|
|
|
|
return priv->surface;
|
|
}
|
|
|
|
cairo_region_t *
|
|
meta_wayland_surface_calculate_input_region (MetaWaylandSurface *surface)
|
|
{
|
|
cairo_region_t *region;
|
|
cairo_rectangle_int_t buffer_rect;
|
|
|
|
if (!surface->buffer)
|
|
return NULL;
|
|
|
|
buffer_rect = (cairo_rectangle_int_t) {
|
|
.width = meta_wayland_surface_get_width (surface),
|
|
.height = meta_wayland_surface_get_height (surface),
|
|
};
|
|
region = cairo_region_create_rectangle (&buffer_rect);
|
|
|
|
if (surface->input_region)
|
|
cairo_region_intersect (region, surface->input_region);
|
|
|
|
return region;
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_inhibit_shortcuts (MetaWaylandSurface *surface,
|
|
MetaWaylandSeat *seat)
|
|
{
|
|
g_hash_table_add (surface->shortcut_inhibited_seats, seat);
|
|
g_signal_emit (surface, surface_signals[SURFACE_SHORTCUTS_INHIBITED], 0);
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_restore_shortcuts (MetaWaylandSurface *surface,
|
|
MetaWaylandSeat *seat)
|
|
{
|
|
g_signal_emit (surface, surface_signals[SURFACE_SHORTCUTS_RESTORED], 0);
|
|
g_hash_table_remove (surface->shortcut_inhibited_seats, seat);
|
|
}
|
|
|
|
gboolean
|
|
meta_wayland_surface_is_shortcuts_inhibited (MetaWaylandSurface *surface,
|
|
MetaWaylandSeat *seat)
|
|
{
|
|
if (surface->shortcut_inhibited_seats == NULL)
|
|
return FALSE;
|
|
|
|
return g_hash_table_contains (surface->shortcut_inhibited_seats, seat);
|
|
}
|
|
|
|
CoglTexture *
|
|
meta_wayland_surface_get_texture (MetaWaylandSurface *surface)
|
|
{
|
|
return surface->output_state.texture;
|
|
}
|
|
|
|
MetaSurfaceActor *
|
|
meta_wayland_surface_get_actor (MetaWaylandSurface *surface)
|
|
{
|
|
if (!surface->role || !META_IS_WAYLAND_ACTOR_SURFACE (surface->role))
|
|
return NULL;
|
|
|
|
return meta_wayland_actor_surface_get_actor (META_WAYLAND_ACTOR_SURFACE (surface->role));
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_notify_geometry_changed (MetaWaylandSurface *surface)
|
|
{
|
|
g_signal_emit (surface, surface_signals[SURFACE_GEOMETRY_CHANGED], 0);
|
|
}
|
|
|
|
int
|
|
meta_wayland_surface_get_width (MetaWaylandSurface *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
|
|
{
|
|
int width;
|
|
|
|
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)
|
|
{
|
|
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
|
|
{
|
|
int height;
|
|
|
|
if (meta_monitor_transform_is_rotated (surface->buffer_transform))
|
|
height = get_buffer_width (surface);
|
|
else
|
|
height = get_buffer_height (surface);
|
|
|
|
return height / surface->scale;
|
|
}
|
|
}
|
|
|
|
static void
|
|
scanout_destroyed (gpointer data,
|
|
GObject *where_the_object_was)
|
|
{
|
|
MetaWaylandBuffer *buffer = data;
|
|
|
|
meta_wayland_buffer_dec_use_count (buffer);
|
|
g_object_unref (buffer);
|
|
}
|
|
|
|
CoglScanout *
|
|
meta_wayland_surface_try_acquire_scanout (MetaWaylandSurface *surface,
|
|
CoglOnscreen *onscreen)
|
|
{
|
|
CoglScanout *scanout;
|
|
MetaWaylandBuffer *buffer;
|
|
|
|
if (!surface->buffer)
|
|
return NULL;
|
|
|
|
if (surface->buffer->use_count == 0)
|
|
return NULL;
|
|
|
|
scanout = meta_wayland_buffer_try_acquire_scanout (surface->buffer,
|
|
onscreen);
|
|
if (!scanout)
|
|
return NULL;
|
|
|
|
buffer = g_object_ref (surface->buffer);
|
|
meta_wayland_buffer_inc_use_count (buffer);
|
|
g_object_weak_ref (G_OBJECT (scanout), scanout_destroyed, buffer);
|
|
|
|
return scanout;
|
|
}
|
|
|
|
MetaCrtc *
|
|
meta_wayland_surface_get_scanout_candidate (MetaWaylandSurface *surface)
|
|
{
|
|
return surface->scanout_candidate;
|
|
}
|
|
|
|
void
|
|
meta_wayland_surface_set_scanout_candidate (MetaWaylandSurface *surface,
|
|
MetaCrtc *crtc)
|
|
{
|
|
if (surface->scanout_candidate == crtc)
|
|
return;
|
|
|
|
g_set_object (&surface->scanout_candidate, crtc);
|
|
g_object_notify_by_pspec (G_OBJECT (surface),
|
|
obj_props[PROP_SCANOUT_CANDIDATE]);
|
|
}
|
|
|
|
gboolean
|
|
meta_wayland_surface_can_scanout_untransformed (MetaWaylandSurface *surface,
|
|
MetaRendererView *view,
|
|
int geometry_scale)
|
|
{
|
|
if (meta_renderer_view_get_transform (view) != surface->buffer_transform)
|
|
{
|
|
meta_topic (META_DEBUG_RENDER,
|
|
"Surface can not be scanned out untransformed: buffer "
|
|
"transform does not match renderer-view transform");
|
|
return FALSE;
|
|
}
|
|
|
|
if (surface->viewport.has_dst_size)
|
|
{
|
|
MetaRectangle view_layout;
|
|
float view_scale;
|
|
|
|
clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout);
|
|
view_scale = clutter_stage_view_get_scale (CLUTTER_STAGE_VIEW (view));
|
|
|
|
if (!G_APPROX_VALUE (view_layout.width, surface->viewport.dst_width,
|
|
FLT_EPSILON) ||
|
|
!G_APPROX_VALUE (view_layout.height, surface->viewport.dst_height,
|
|
FLT_EPSILON) ||
|
|
!G_APPROX_VALUE (view_layout.width * view_scale,
|
|
get_buffer_width (surface),
|
|
FLT_EPSILON) ||
|
|
!G_APPROX_VALUE (view_layout.height * view_scale,
|
|
get_buffer_height (surface),
|
|
FLT_EPSILON))
|
|
{
|
|
meta_topic (META_DEBUG_RENDER,
|
|
"Surface can not be scanned out untransformed: viewport "
|
|
"destination size does not match stage-view layout");
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (meta_is_stage_views_scaled ())
|
|
{
|
|
float view_scale;
|
|
|
|
view_scale = clutter_stage_view_get_scale (CLUTTER_STAGE_VIEW (view));
|
|
if (!G_APPROX_VALUE (view_scale, surface->scale, FLT_EPSILON))
|
|
{
|
|
meta_topic (META_DEBUG_RENDER,
|
|
"Surface can not be scanned out untransformed: "
|
|
"buffer scale does not match stage-view scale");
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (geometry_scale != surface->scale)
|
|
{
|
|
meta_topic (META_DEBUG_RENDER,
|
|
"Surface can not be scanned out untransformed: "
|
|
"buffer scale does not match actor geometry scale");
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (surface->viewport.has_src_rect)
|
|
{
|
|
if (!G_APPROX_VALUE (surface->viewport.src_rect.origin.x, 0.0,
|
|
FLT_EPSILON) ||
|
|
!G_APPROX_VALUE (surface->viewport.src_rect.origin.y, 0.0,
|
|
FLT_EPSILON) ||
|
|
!G_APPROX_VALUE (surface->viewport.src_rect.size.width *
|
|
surface->scale,
|
|
get_buffer_width (surface),
|
|
FLT_EPSILON) ||
|
|
!G_APPROX_VALUE (surface->viewport.src_rect.size.height *
|
|
surface->scale,
|
|
get_buffer_height (surface),
|
|
FLT_EPSILON))
|
|
{
|
|
meta_topic (META_DEBUG_RENDER,
|
|
"Surface can not be scanned out untransformed: viewport "
|
|
"source rect does not cover the whole buffer");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
meta_wayland_surface_get_geometry_scale (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandActorSurface *actor_surface;
|
|
|
|
g_return_val_if_fail (META_IS_WAYLAND_ACTOR_SURFACE (surface->role), 1);
|
|
|
|
actor_surface = META_WAYLAND_ACTOR_SURFACE (surface->role);
|
|
return meta_wayland_actor_surface_get_geometry_scale (actor_surface);
|
|
}
|