/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2013 Red Hat
*
* 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, see .
*
* Written by:
* Jasper St. Pierre
*/
#include "config.h"
#include "compositor/meta-surface-actor-wayland.h"
#include
#include "backends/meta-backend-private.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-screen-cast-window.h"
#include "compositor/meta-shaped-texture-private.h"
#include "compositor/meta-window-actor-private.h"
#include "compositor/region-utils.h"
#include "wayland/meta-wayland-buffer.h"
#include "wayland/meta-wayland-private.h"
#include "wayland/meta-wayland-subsurface.h"
#include "wayland/meta-window-wayland.h"
struct _MetaSurfaceActorWayland
{
MetaSurfaceActor parent;
MetaWaylandSurface *surface;
};
G_DEFINE_TYPE (MetaSurfaceActorWayland,
meta_surface_actor_wayland,
META_TYPE_SURFACE_ACTOR)
static void
meta_surface_actor_wayland_process_damage (MetaSurfaceActor *actor,
int x,
int y,
int width,
int height)
{
meta_surface_actor_update_area (actor, x, y, width, height);
}
static gboolean
meta_surface_actor_wayland_is_opaque (MetaSurfaceActor *actor)
{
MetaShapedTexture *stex = meta_surface_actor_get_texture (actor);
return meta_shaped_texture_is_opaque (stex);
}
#define UNOBSCURED_THRESHOLD 0.1
gboolean
meta_surface_actor_wayland_is_view_primary (MetaSurfaceActor *actor,
ClutterStageView *stage_view)
{
ClutterStageView *current_primary_view = NULL;
float highest_refresh_rate = 0.f;
float biggest_unobscurred_fraction = 0.f;
MetaWindowActor *window_actor;
gboolean is_streaming = FALSE;
GList *l;
window_actor = meta_window_actor_from_actor (CLUTTER_ACTOR (actor));
if (window_actor)
is_streaming = meta_window_actor_is_streaming (window_actor);
if (clutter_actor_has_mapped_clones (CLUTTER_ACTOR (actor)) || is_streaming)
{
ClutterStage *stage;
ClutterStageView *fallback_view = NULL;
float fallback_refresh_rate = 0.0;
stage = CLUTTER_STAGE (clutter_actor_get_stage (CLUTTER_ACTOR (actor)));
for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
{
ClutterStageView *view = l->data;
float refresh_rate;
refresh_rate = clutter_stage_view_get_refresh_rate (view);
if (clutter_actor_is_effectively_on_stage_view (CLUTTER_ACTOR (actor),
view))
{
if (refresh_rate > highest_refresh_rate)
{
current_primary_view = view;
highest_refresh_rate = refresh_rate;
}
}
else
{
if (refresh_rate > fallback_refresh_rate)
{
fallback_view = view;
fallback_refresh_rate = refresh_rate;
}
}
}
if (current_primary_view)
return current_primary_view == stage_view;
else if (is_streaming)
return fallback_view == stage_view;
}
l = clutter_actor_peek_stage_views (CLUTTER_ACTOR (actor));
if (!l)
return FALSE;
if (!l->next)
{
return !meta_surface_actor_is_obscured_on_stage_view (actor,
stage_view,
NULL);
}
for (; l; l = l->next)
{
ClutterStageView *view = l->data;
float refresh_rate;
float unobscurred_fraction;
if (meta_surface_actor_is_obscured_on_stage_view (actor,
view,
&unobscurred_fraction))
continue;
refresh_rate = clutter_stage_view_get_refresh_rate (view);
if ((refresh_rate > highest_refresh_rate &&
(biggest_unobscurred_fraction < UNOBSCURED_THRESHOLD ||
unobscurred_fraction > UNOBSCURED_THRESHOLD)) ||
(biggest_unobscurred_fraction < UNOBSCURED_THRESHOLD &&
unobscurred_fraction > UNOBSCURED_THRESHOLD))
{
current_primary_view = view;
highest_refresh_rate = refresh_rate;
biggest_unobscurred_fraction = unobscurred_fraction;
}
}
return current_primary_view == stage_view;
}
static void
meta_surface_actor_wayland_apply_transform (ClutterActor *actor,
graphene_matrix_t *matrix)
{
MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor);
ClutterActorClass *parent_class =
CLUTTER_ACTOR_CLASS (meta_surface_actor_wayland_parent_class);
MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self);
MetaWaylandSurface *root_surface;
MetaWindow *window;
MetaLogicalMonitor *logical_monitor;
g_autoptr (ClutterActorBox) allocation = NULL;
float scale;
float actor_width, actor_height;
float adj_actor_width, adj_actor_height;
float adj_actor_x, adj_actor_y;
float width_scale, height_scale;
float x_off, y_off;
if (!surface)
goto out;
root_surface = surface;
while (root_surface->applied_state.parent)
root_surface = root_surface->applied_state.parent;
window = meta_wayland_surface_get_window (root_surface);
if (!window)
goto out;
if (!META_IS_WINDOW_WAYLAND (window))
goto out;
logical_monitor = meta_window_get_highest_scale_monitor (window);
if (!logical_monitor)
goto out;
scale = meta_logical_monitor_get_scale (logical_monitor);
g_object_get (actor, "allocation", &allocation, NULL);
actor_width = clutter_actor_box_get_width (allocation);
actor_height = clutter_actor_box_get_height (allocation);
if (actor_width == 0.0 || actor_height == 0.0)
goto out;
/* We rely on MetaSurfaceActorContainerWayland to ensure that the toplevel
* surface on-display position is aligned to the physical pixel boundary.
*/
if (META_IS_WAYLAND_SUBSURFACE (surface->role))
{
adj_actor_width =
roundf ((surface->sub.x + actor_width) * scale) / scale -
roundf (surface->sub.x * scale) / scale;
adj_actor_height =
roundf ((surface->sub.y + actor_height) * scale) / scale -
roundf (surface->sub.y * scale) / scale;
adj_actor_x = adj_actor_y = 0.0;
do
{
adj_actor_x += roundf (surface->sub.x * scale) / scale;
adj_actor_y += roundf (surface->sub.y * scale) / scale;
surface = surface->applied_state.parent;
}
while (surface);
}
else
{
adj_actor_width = roundf (actor_width * scale) / scale;
adj_actor_height = roundf (actor_height * scale) / scale;
adj_actor_x = allocation->x1;
adj_actor_y = allocation->y1;
}
width_scale = adj_actor_width / actor_width;
height_scale = adj_actor_height / actor_height;
if (!G_APPROX_VALUE (width_scale, 1.0, FLT_EPSILON) ||
!G_APPROX_VALUE (height_scale, 1.0, FLT_EPSILON))
graphene_matrix_scale (matrix, width_scale, height_scale, 1.0);
parent_class->apply_transform (actor, matrix);
x_off = adj_actor_x - allocation->x1;
y_off = adj_actor_y - allocation->y1;
if (!G_APPROX_VALUE (x_off, 0.0, FLT_EPSILON) ||
!G_APPROX_VALUE (y_off, 0.0, FLT_EPSILON))
graphene_matrix_translate (matrix, &GRAPHENE_POINT3D_INIT (x_off, y_off, 0.0));
return;
out:
parent_class->apply_transform (actor, matrix);
}
static void
meta_surface_actor_wayland_dispose (GObject *object)
{
MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (object);
MetaShapedTexture *stex;
stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
if (stex)
meta_shaped_texture_set_texture (stex, NULL);
if (self->surface)
{
g_object_remove_weak_pointer (G_OBJECT (self->surface),
(gpointer *) &self->surface);
self->surface = NULL;
}
G_OBJECT_CLASS (meta_surface_actor_wayland_parent_class)->dispose (object);
}
static void
meta_surface_actor_wayland_class_init (MetaSurfaceActorWaylandClass *klass)
{
MetaSurfaceActorClass *surface_actor_class = META_SURFACE_ACTOR_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
surface_actor_class->process_damage = meta_surface_actor_wayland_process_damage;
surface_actor_class->is_opaque = meta_surface_actor_wayland_is_opaque;
actor_class->apply_transform = meta_surface_actor_wayland_apply_transform;
object_class->dispose = meta_surface_actor_wayland_dispose;
}
static void
meta_surface_actor_wayland_init (MetaSurfaceActorWayland *self)
{
}
MetaSurfaceActor *
meta_surface_actor_wayland_new (MetaWaylandSurface *surface)
{
MetaSurfaceActorWayland *self = g_object_new (META_TYPE_SURFACE_ACTOR_WAYLAND, NULL);
g_assert (meta_is_wayland_compositor ());
self->surface = surface;
g_object_add_weak_pointer (G_OBJECT (self->surface),
(gpointer *) &self->surface);
return META_SURFACE_ACTOR (self);
}
MetaWaylandSurface *
meta_surface_actor_wayland_get_surface (MetaSurfaceActorWayland *self)
{
return self->surface;
}