blob: 1c4c386f9ac8f8ec0a1155650b51f5675bf3c57a [file] [log] [blame]
/*
* Copyright © 2010-2011 Intel Corporation
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2012-2018 Collabora, Ltd.
* Copyright © 2017, 2018 General Electric Company
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <stdarg.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <unistd.h>
#include <math.h>
#include <linux/input.h>
#include <dlfcn.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include "timeline.h"
#include "compositor.h"
#include "alpha-compositing-unstable-v1-server-protocol.h"
#include "viewporter-server-protocol.h"
#include "presentation-time-server-protocol.h"
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
#include "shared/string-helpers.h"
#include "shared/timespec-util.h"
#include "git-version.h"
#include "version.h"
#include "plugin-registry.h"
#define DEFAULT_REPAINT_WINDOW 16 /* milliseconds */
static void
weston_output_update_matrix(struct weston_output *output);
static void
weston_output_transform_scale_init(struct weston_output *output,
uint32_t transform, uint32_t scale);
static void
weston_compositor_build_view_list(struct weston_compositor *compositor);
static char *
weston_output_create_heads_string(struct weston_output *output);
/** Send wl_output events for mode and scale changes
*
* \param head Send on all resources bound to this head.
* \param mode_changed If true, send the current mode.
* \param scale_changed If true, send the current scale.
*/
static void
weston_mode_switch_send_events(struct weston_head *head,
bool mode_changed, bool scale_changed)
{
struct weston_output *output = head->output;
struct wl_resource *resource;
int version;
wl_resource_for_each(resource, &head->resource_list) {
if (mode_changed) {
wl_output_send_mode(resource,
output->current_mode->flags,
output->current_mode->width,
output->current_mode->height,
output->current_mode->refresh);
}
version = wl_resource_get_version(resource);
if (version >= WL_OUTPUT_SCALE_SINCE_VERSION && scale_changed)
wl_output_send_scale(resource, output->current_scale);
if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
wl_output_send_done(resource);
}
}
static void
weston_mode_switch_finish(struct weston_output *output,
int mode_changed, int scale_changed)
{
struct weston_seat *seat;
struct weston_head *head;
pixman_region32_t old_output_region;
pixman_region32_init(&old_output_region);
pixman_region32_copy(&old_output_region, &output->region);
/* Update output region and transformation matrix */
weston_output_transform_scale_init(output, output->transform, output->current_scale);
pixman_region32_init(&output->previous_damage);
pixman_region32_init_rect(&output->region, output->x, output->y,
output->width, output->height);
weston_output_update_matrix(output);
/* If a pointer falls outside the outputs new geometry, move it to its
* lower-right corner */
wl_list_for_each(seat, &output->compositor->seat_list, link) {
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
int32_t x, y;
if (!pointer)
continue;
x = wl_fixed_to_int(pointer->x);
y = wl_fixed_to_int(pointer->y);
if (!pixman_region32_contains_point(&old_output_region,
x, y, NULL) ||
pixman_region32_contains_point(&output->region,
x, y, NULL))
continue;
if (x >= output->x + output->width)
x = output->x + output->width - 1;
if (y >= output->y + output->height)
y = output->y + output->height - 1;
pointer->x = wl_fixed_from_int(x);
pointer->y = wl_fixed_from_int(y);
}
pixman_region32_fini(&old_output_region);
if (!mode_changed && !scale_changed)
return;
/* notify clients of the changes */
wl_list_for_each(head, &output->head_list, output_link)
weston_mode_switch_send_events(head,
mode_changed, scale_changed);
}
static void
weston_compositor_reflow_outputs(struct weston_compositor *compositor,
struct weston_output *resized_output, int delta_width);
WL_EXPORT int
weston_output_mode_set_native(struct weston_output *output,
struct weston_mode *mode,
int32_t scale)
{
int ret;
int mode_changed = 0, scale_changed = 0;
int32_t old_width;
if (!output->switch_mode)
return -1;
if (!output->original_mode) {
mode_changed = 1;
ret = output->switch_mode(output, mode);
if (ret < 0)
return ret;
if (output->current_scale != scale) {
scale_changed = 1;
output->current_scale = scale;
}
}
old_width = output->width;
output->native_mode = mode;
output->native_scale = scale;
weston_mode_switch_finish(output, mode_changed, scale_changed);
if (mode_changed || scale_changed) {
weston_compositor_reflow_outputs(output->compositor, output, output->width - old_width);
wl_signal_emit(&output->compositor->output_resized_signal, output);
}
return 0;
}
WL_EXPORT int
weston_output_mode_switch_to_native(struct weston_output *output)
{
int ret;
int mode_changed = 0, scale_changed = 0;
if (!output->switch_mode)
return -1;
if (!output->original_mode) {
weston_log("already in the native mode\n");
return -1;
}
/* the non fullscreen clients haven't seen a mode set since we
* switched into a temporary, so we need to notify them if the
* mode at that time is different from the native mode now.
*/
mode_changed = (output->original_mode != output->native_mode);
scale_changed = (output->original_scale != output->native_scale);
ret = output->switch_mode(output, output->native_mode);
if (ret < 0)
return ret;
output->current_scale = output->native_scale;
output->original_mode = NULL;
output->original_scale = 0;
weston_mode_switch_finish(output, mode_changed, scale_changed);
return 0;
}
WL_EXPORT int
weston_output_mode_switch_to_temporary(struct weston_output *output,
struct weston_mode *mode,
int32_t scale)
{
int ret;
if (!output->switch_mode)
return -1;
/* original_mode is the last mode non full screen clients have seen,
* so we shouldn't change it if we already have one set.
*/
if (!output->original_mode) {
output->original_mode = output->native_mode;
output->original_scale = output->native_scale;
}
ret = output->switch_mode(output, mode);
if (ret < 0)
return ret;
output->current_scale = scale;
weston_mode_switch_finish(output, 0, 0);
return 0;
}
static void
region_init_infinite(pixman_region32_t *region)
{
pixman_region32_init_rect(region, INT32_MIN, INT32_MIN,
UINT32_MAX, UINT32_MAX);
}
static struct weston_subsurface *
weston_surface_to_subsurface(struct weston_surface *surface);
WL_EXPORT struct weston_view *
weston_view_create(struct weston_surface *surface)
{
struct weston_view *view;
view = zalloc(sizeof *view);
if (view == NULL)
return NULL;
view->surface = surface;
view->plane = &surface->compositor->primary_plane;
/* Assign to surface */
wl_list_insert(&surface->views, &view->surface_link);
wl_signal_init(&view->destroy_signal);
wl_list_init(&view->link);
wl_list_init(&view->layer_link.link);
pixman_region32_init(&view->clip);
view->alpha = 1.0;
view->blending_alpha = 1.0;
view->blending_equation = ZWP_BLENDING_V1_BLENDING_EQUATION_NONE;
pixman_region32_init(&view->transform.opaque);
wl_list_init(&view->geometry.transformation_list);
wl_list_insert(&view->geometry.transformation_list,
&view->transform.position.link);
weston_matrix_init(&view->transform.position.matrix);
wl_list_init(&view->geometry.child_list);
pixman_region32_init(&view->geometry.scissor);
pixman_region32_init(&view->transform.boundingbox);
view->transform.dirty = 1;
return view;
}
struct weston_frame_callback {
struct wl_resource *resource;
struct wl_list link;
};
struct weston_presentation_feedback {
struct wl_resource *resource;
/* XXX: could use just wl_resource_get_link() instead */
struct wl_list link;
/* The per-surface feedback flags */
uint32_t psf_flags;
};
static void
weston_presentation_feedback_discard(
struct weston_presentation_feedback *feedback)
{
wp_presentation_feedback_send_discarded(feedback->resource);
wl_resource_destroy(feedback->resource);
}
static void
weston_presentation_feedback_discard_list(struct wl_list *list)
{
struct weston_presentation_feedback *feedback, *tmp;
wl_list_for_each_safe(feedback, tmp, list, link)
weston_presentation_feedback_discard(feedback);
}
static void
weston_presentation_feedback_present(
struct weston_presentation_feedback *feedback,
struct weston_output *output,
uint32_t refresh_nsec,
const struct timespec *ts,
uint64_t seq,
uint32_t flags)
{
struct wl_client *client = wl_resource_get_client(feedback->resource);
struct weston_head *head;
struct wl_resource *o;
uint32_t tv_sec_hi;
uint32_t tv_sec_lo;
uint32_t tv_nsec;
bool done = false;
wl_list_for_each(head, &output->head_list, output_link) {
wl_resource_for_each(o, &head->resource_list) {
if (wl_resource_get_client(o) != client)
continue;
wp_presentation_feedback_send_sync_output(feedback->resource, o);
done = true;
}
/* For clone mode, send it for just one wl_output global,
* they are all equivalent anyway.
*/
if (done)
break;
}
timespec_to_proto(ts, &tv_sec_hi, &tv_sec_lo, &tv_nsec);
wp_presentation_feedback_send_presented(feedback->resource,
tv_sec_hi, tv_sec_lo, tv_nsec,
refresh_nsec,
seq >> 32, seq & 0xffffffff,
flags | feedback->psf_flags);
wl_resource_destroy(feedback->resource);
}
static void
weston_presentation_feedback_present_list(struct wl_list *list,
struct weston_output *output,
uint32_t refresh_nsec,
const struct timespec *ts,
uint64_t seq,
uint32_t flags)
{
struct weston_presentation_feedback *feedback, *tmp;
assert(!(flags & WP_PRESENTATION_FEEDBACK_INVALID) ||
wl_list_empty(list));
wl_list_for_each_safe(feedback, tmp, list, link)
weston_presentation_feedback_present(feedback, output,
refresh_nsec, ts, seq,
flags);
}
static void
surface_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
{
struct weston_surface_state *state =
container_of(listener, struct weston_surface_state,
buffer_destroy_listener);
state->buffer = NULL;
}
static void
weston_surface_state_init(struct weston_surface_state *state)
{
state->newly_attached = 0;
state->buffer = NULL;
state->buffer_destroy_listener.notify =
surface_state_handle_buffer_destroy;
state->sx = 0;
state->sy = 0;
pixman_region32_init(&state->damage_surface);
pixman_region32_init(&state->damage_buffer);
pixman_region32_init(&state->opaque);
region_init_infinite(&state->input);
wl_list_init(&state->frame_callback_list);
wl_list_init(&state->feedback_list);
state->buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL;
state->buffer_viewport.buffer.scale = 1;
state->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
state->buffer_viewport.surface.width = -1;
state->buffer_viewport.changed = 0;
state->blending_equation = ZWP_BLENDING_V1_BLENDING_EQUATION_NONE;
state->blending_alpha = 1.0;
}
static void
weston_surface_state_fini(struct weston_surface_state *state)
{
struct weston_frame_callback *cb, *next;
wl_list_for_each_safe(cb, next,
&state->frame_callback_list, link)
wl_resource_destroy(cb->resource);
weston_presentation_feedback_discard_list(&state->feedback_list);
pixman_region32_fini(&state->input);
pixman_region32_fini(&state->opaque);
pixman_region32_fini(&state->damage_surface);
pixman_region32_fini(&state->damage_buffer);
if (state->buffer)
wl_list_remove(&state->buffer_destroy_listener.link);
state->buffer = NULL;
}
static void
weston_surface_state_set_buffer(struct weston_surface_state *state,
struct weston_buffer *buffer)
{
if (state->buffer == buffer)
return;
if (state->buffer)
wl_list_remove(&state->buffer_destroy_listener.link);
state->buffer = buffer;
if (state->buffer)
wl_signal_add(&state->buffer->destroy_signal,
&state->buffer_destroy_listener);
}
WL_EXPORT struct weston_surface *
weston_surface_create(struct weston_compositor *compositor)
{
struct weston_surface *surface;
surface = zalloc(sizeof *surface);
if (surface == NULL)
return NULL;
wl_signal_init(&surface->destroy_signal);
wl_signal_init(&surface->commit_signal);
surface->compositor = compositor;
surface->ref_count = 1;
surface->buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL;
surface->buffer_viewport.buffer.scale = 1;
surface->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
surface->buffer_viewport.surface.width = -1;
weston_surface_state_init(&surface->pending);
pixman_region32_init(&surface->damage);
pixman_region32_init(&surface->opaque);
region_init_infinite(&surface->input);
wl_list_init(&surface->views);
wl_list_init(&surface->frame_callback_list);
wl_list_init(&surface->feedback_list);
wl_list_init(&surface->subsurface_list);
wl_list_init(&surface->subsurface_list_pending);
weston_matrix_init(&surface->buffer_to_surface_matrix);
weston_matrix_init(&surface->surface_to_buffer_matrix);
wl_list_init(&surface->pointer_constraints);
return surface;
}
WL_EXPORT void
weston_surface_set_color(struct weston_surface *surface,
float red, float green, float blue, float alpha)
{
surface->compositor->renderer->surface_set_color(surface, red, green, blue, alpha);
}
WL_EXPORT void
weston_view_to_global_float(struct weston_view *view,
float sx, float sy, float *x, float *y)
{
if (view->transform.enabled) {
struct weston_vector v = { { sx, sy, 0.0f, 1.0f } };
weston_matrix_transform(&view->transform.matrix, &v);
if (fabsf(v.f[3]) < 1e-6) {
weston_log("warning: numerical instability in "
"%s(), divisor = %g\n", __func__,
v.f[3]);
*x = 0;
*y = 0;
return;
}
*x = v.f[0] / v.f[3];
*y = v.f[1] / v.f[3];
} else {
*x = sx + view->geometry.x;
*y = sy + view->geometry.y;
}
}
WL_EXPORT void
weston_transformed_coord(int width, int height,
enum wl_output_transform transform,
int32_t scale,
float sx, float sy, float *bx, float *by)
{
switch (transform) {
case WL_OUTPUT_TRANSFORM_NORMAL:
default:
*bx = sx;
*by = sy;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED:
*bx = width - sx;
*by = sy;
break;
case WL_OUTPUT_TRANSFORM_90:
*bx = height - sy;
*by = sx;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
*bx = height - sy;
*by = width - sx;
break;
case WL_OUTPUT_TRANSFORM_180:
*bx = width - sx;
*by = height - sy;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
*bx = sx;
*by = height - sy;
break;
case WL_OUTPUT_TRANSFORM_270:
*bx = sy;
*by = width - sx;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
*bx = sy;
*by = sx;
break;
}
*bx *= scale;
*by *= scale;
}
WL_EXPORT pixman_box32_t
weston_transformed_rect(int width, int height,
enum wl_output_transform transform,
int32_t scale,
pixman_box32_t rect)
{
float x1, x2, y1, y2;
pixman_box32_t ret;
weston_transformed_coord(width, height, transform, scale,
rect.x1, rect.y1, &x1, &y1);
weston_transformed_coord(width, height, transform, scale,
rect.x2, rect.y2, &x2, &y2);
if (x1 <= x2) {
ret.x1 = x1;
ret.x2 = x2;
} else {
ret.x1 = x2;
ret.x2 = x1;
}
if (y1 <= y2) {
ret.y1 = y1;
ret.y2 = y2;
} else {
ret.y1 = y2;
ret.y2 = y1;
}
return ret;
}
/** Transform a region by a matrix, restricted to axis-aligned transformations
*
* Warning: This function does not work for projective, affine, or matrices
* that encode arbitrary rotations. Only 90-degree step rotations are
* supported.
*/
WL_EXPORT void
weston_matrix_transform_region(pixman_region32_t *dest,
struct weston_matrix *matrix,
pixman_region32_t *src)
{
pixman_box32_t *src_rects, *dest_rects;
int nrects, i;
src_rects = pixman_region32_rectangles(src, &nrects);
dest_rects = malloc(nrects * sizeof(*dest_rects));
if (!dest_rects)
return;
for (i = 0; i < nrects; i++) {
struct weston_vector vec1 = {{
src_rects[i].x1, src_rects[i].y1, 0, 1
}};
weston_matrix_transform(matrix, &vec1);
vec1.f[0] /= vec1.f[3];
vec1.f[1] /= vec1.f[3];
struct weston_vector vec2 = {{
src_rects[i].x2, src_rects[i].y2, 0, 1
}};
weston_matrix_transform(matrix, &vec2);
vec2.f[0] /= vec2.f[3];
vec2.f[1] /= vec2.f[3];
if (vec1.f[0] < vec2.f[0]) {
dest_rects[i].x1 = floor(vec1.f[0]);
dest_rects[i].x2 = ceil(vec2.f[0]);
} else {
dest_rects[i].x1 = floor(vec2.f[0]);
dest_rects[i].x2 = ceil(vec1.f[0]);
}
if (vec1.f[1] < vec2.f[1]) {
dest_rects[i].y1 = floor(vec1.f[1]);
dest_rects[i].y2 = ceil(vec2.f[1]);
} else {
dest_rects[i].y1 = floor(vec2.f[1]);
dest_rects[i].y2 = ceil(vec1.f[1]);
}
}
pixman_region32_clear(dest);
pixman_region32_init_rects(dest, dest_rects, nrects);
free(dest_rects);
}
WL_EXPORT void
weston_transformed_region(int width, int height,
enum wl_output_transform transform,
int32_t scale,
pixman_region32_t *src, pixman_region32_t *dest)
{
pixman_box32_t *src_rects, *dest_rects;
int nrects, i;
if (transform == WL_OUTPUT_TRANSFORM_NORMAL && scale == 1) {
if (src != dest)
pixman_region32_copy(dest, src);
return;
}
src_rects = pixman_region32_rectangles(src, &nrects);
dest_rects = malloc(nrects * sizeof(*dest_rects));
if (!dest_rects)
return;
if (transform == WL_OUTPUT_TRANSFORM_NORMAL) {
memcpy(dest_rects, src_rects, nrects * sizeof(*dest_rects));
} else {
for (i = 0; i < nrects; i++) {
switch (transform) {
default:
case WL_OUTPUT_TRANSFORM_NORMAL:
dest_rects[i].x1 = src_rects[i].x1;
dest_rects[i].y1 = src_rects[i].y1;
dest_rects[i].x2 = src_rects[i].x2;
dest_rects[i].y2 = src_rects[i].y2;
break;
case WL_OUTPUT_TRANSFORM_90:
dest_rects[i].x1 = height - src_rects[i].y2;
dest_rects[i].y1 = src_rects[i].x1;
dest_rects[i].x2 = height - src_rects[i].y1;
dest_rects[i].y2 = src_rects[i].x2;
break;
case WL_OUTPUT_TRANSFORM_180:
dest_rects[i].x1 = width - src_rects[i].x2;
dest_rects[i].y1 = height - src_rects[i].y2;
dest_rects[i].x2 = width - src_rects[i].x1;
dest_rects[i].y2 = height - src_rects[i].y1;
break;
case WL_OUTPUT_TRANSFORM_270:
dest_rects[i].x1 = src_rects[i].y1;
dest_rects[i].y1 = width - src_rects[i].x2;
dest_rects[i].x2 = src_rects[i].y2;
dest_rects[i].y2 = width - src_rects[i].x1;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED:
dest_rects[i].x1 = width - src_rects[i].x2;
dest_rects[i].y1 = src_rects[i].y1;
dest_rects[i].x2 = width - src_rects[i].x1;
dest_rects[i].y2 = src_rects[i].y2;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
dest_rects[i].x1 = height - src_rects[i].y2;
dest_rects[i].y1 = width - src_rects[i].x2;
dest_rects[i].x2 = height - src_rects[i].y1;
dest_rects[i].y2 = width - src_rects[i].x1;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
dest_rects[i].x1 = src_rects[i].x1;
dest_rects[i].y1 = height - src_rects[i].y2;
dest_rects[i].x2 = src_rects[i].x2;
dest_rects[i].y2 = height - src_rects[i].y1;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
dest_rects[i].x1 = src_rects[i].y1;
dest_rects[i].y1 = src_rects[i].x1;
dest_rects[i].x2 = src_rects[i].y2;
dest_rects[i].y2 = src_rects[i].x2;
break;
}
}
}
if (scale != 1) {
for (i = 0; i < nrects; i++) {
dest_rects[i].x1 *= scale;
dest_rects[i].x2 *= scale;
dest_rects[i].y1 *= scale;
dest_rects[i].y2 *= scale;
}
}
pixman_region32_clear(dest);
pixman_region32_init_rects(dest, dest_rects, nrects);
free(dest_rects);
}
static void
viewport_surface_to_buffer(struct weston_surface *surface,
float sx, float sy, float *bx, float *by)
{
struct weston_buffer_viewport *vp = &surface->buffer_viewport;
double src_width, src_height;
double src_x, src_y;
if (vp->buffer.src_width == wl_fixed_from_int(-1)) {
if (vp->surface.width == -1) {
*bx = sx;
*by = sy;
return;
}
src_x = 0.0;
src_y = 0.0;
src_width = surface->width_from_buffer;
src_height = surface->height_from_buffer;
} else {
src_x = wl_fixed_to_double(vp->buffer.src_x);
src_y = wl_fixed_to_double(vp->buffer.src_y);
src_width = wl_fixed_to_double(vp->buffer.src_width);
src_height = wl_fixed_to_double(vp->buffer.src_height);
}
*bx = sx * src_width / surface->width + src_x;
*by = sy * src_height / surface->height + src_y;
}
WL_EXPORT void
weston_surface_to_buffer_float(struct weston_surface *surface,
float sx, float sy, float *bx, float *by)
{
struct weston_buffer_viewport *vp = &surface->buffer_viewport;
/* first transform coordinates if the viewport is set */
viewport_surface_to_buffer(surface, sx, sy, bx, by);
weston_transformed_coord(surface->width_from_buffer,
surface->height_from_buffer,
vp->buffer.transform, vp->buffer.scale,
*bx, *by, bx, by);
}
/** Transform a rectangle from surface coordinates to buffer coordinates
*
* \param surface The surface to fetch wp_viewport and buffer transformation
* from.
* \param rect The rectangle to transform.
* \return The transformed rectangle.
*
* Viewport and buffer transformations can only do translation, scaling,
* and rotations in 90-degree steps. Therefore the only loss in the
* conversion is coordinate rounding.
*
* However, some coordinate rounding takes place as an intermediate
* step before the buffer scale factor is applied, so the rectangle
* boundary may not be exactly as expected.
*
* This is OK for damage tracking since a little extra coverage is
* not a problem.
*/
WL_EXPORT pixman_box32_t
weston_surface_to_buffer_rect(struct weston_surface *surface,
pixman_box32_t rect)
{
struct weston_buffer_viewport *vp = &surface->buffer_viewport;
float xf, yf;
/* first transform box coordinates if the viewport is set */
viewport_surface_to_buffer(surface, rect.x1, rect.y1, &xf, &yf);
rect.x1 = floorf(xf);
rect.y1 = floorf(yf);
viewport_surface_to_buffer(surface, rect.x2, rect.y2, &xf, &yf);
rect.x2 = ceilf(xf);
rect.y2 = ceilf(yf);
return weston_transformed_rect(surface->width_from_buffer,
surface->height_from_buffer,
vp->buffer.transform, vp->buffer.scale,
rect);
}
/** Transform a region from surface coordinates to buffer coordinates
*
* \param surface The surface to fetch wp_viewport and buffer transformation
* from.
* \param surface_region[in] The region in surface coordinates.
* \param buffer_region[out] The region converted to buffer coordinates.
*
* Buffer_region must be init'd, but will be completely overwritten.
*
* Viewport and buffer transformations can only do translation, scaling,
* and rotations in 90-degree steps. Therefore the only loss in the
* conversion is from the coordinate rounding that takes place in
* \ref weston_surface_to_buffer_rect.
*/
WL_EXPORT void
weston_surface_to_buffer_region(struct weston_surface *surface,
pixman_region32_t *surface_region,
pixman_region32_t *buffer_region)
{
pixman_box32_t *src_rects, *dest_rects;
int nrects, i;
src_rects = pixman_region32_rectangles(surface_region, &nrects);
dest_rects = malloc(nrects * sizeof(*dest_rects));
if (!dest_rects)
return;
for (i = 0; i < nrects; i++) {
dest_rects[i] = weston_surface_to_buffer_rect(surface,
src_rects[i]);
}
pixman_region32_fini(buffer_region);
pixman_region32_init_rects(buffer_region, dest_rects, nrects);
free(dest_rects);
}
WL_EXPORT void
weston_view_move_to_plane(struct weston_view *view,
struct weston_plane *plane)
{
if (view->plane == plane)
return;
weston_view_damage_below(view);
view->plane = plane;
weston_surface_damage(view->surface);
}
/** Inflict damage on the plane where the view is visible.
*
* \param view The view that causes the damage.
*
* If the view is currently on a plane (including the primary plane),
* take the view's boundingbox, subtract all the opaque views that cover it,
* and add the remaining region as damage to the plane. This corresponds
* to the damage inflicted to the plane if this view disappeared.
*
* A repaint is scheduled for this view.
*
* The region of all opaque views covering this view is stored in
* weston_view::clip and updated by view_accumulate_damage() during
* weston_output_repaint(). Specifically, that region matches the
* scenegraph as it was last painted.
*/
WL_EXPORT void
weston_view_damage_below(struct weston_view *view)
{
pixman_region32_t damage;
pixman_region32_init(&damage);
pixman_region32_subtract(&damage, &view->transform.boundingbox,
&view->clip);
if (view->plane)
pixman_region32_union(&view->plane->damage,
&view->plane->damage, &damage);
pixman_region32_fini(&damage);
weston_view_schedule_repaint(view);
}
/** Send wl_surface.enter/leave events
*
* \param surface The surface.
* \param head A head of the entered/left output.
* \param enter True if entered.
* \param left True if left.
*
* Send the enter/leave events for all protocol objects bound to the given
* output by the client owning the surface.
*/
static void
weston_surface_send_enter_leave(struct weston_surface *surface,
struct weston_head *head,
bool enter,
bool leave)
{
struct wl_resource *wloutput;
struct wl_client *client;
assert(enter != leave);
client = wl_resource_get_client(surface->resource);
wl_resource_for_each(wloutput, &head->resource_list) {
if (wl_resource_get_client(wloutput) != client)
continue;
if (enter)
wl_surface_send_enter(surface->resource, wloutput);
if (leave)
wl_surface_send_leave(surface->resource, wloutput);
}
}
/**
* \param es The surface
* \param mask The new set of outputs for the surface
*
* Sets the surface's set of outputs to the ones specified by
* the new output mask provided. Identifies the outputs that
* have changed, the posts enter and leave events for these
* outputs as appropriate.
*/
static void
weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
{
uint32_t different = es->output_mask ^ mask;
uint32_t entered = mask & different;
uint32_t left = es->output_mask & different;
uint32_t output_bit;
struct weston_output *output;
struct weston_head *head;
es->output_mask = mask;
if (es->resource == NULL)
return;
if (different == 0)
return;
wl_list_for_each(output, &es->compositor->output_list, link) {
output_bit = 1u << output->id;
if (!(output_bit & different))
continue;
wl_list_for_each(head, &output->head_list, output_link) {
weston_surface_send_enter_leave(es, head,
output_bit & entered,
output_bit & left);
}
}
}
static void
notify_view_output_destroy(struct wl_listener *listener, void *data)
{
struct weston_view *view =
container_of(listener,
struct weston_view, output_destroy_listener);
view->output = NULL;
view->output_destroy_listener.notify = NULL;
}
/** Set the primary output of the view
*
* \param view The view whose primary output to set
* \param output The new primary output for the view
*
* Set \a output to be the primary output of the \a view.
*
* Notice that the assignment may be temporary; the primary output could be
* automatically changed. Hence, one cannot rely on the value persisting.
*
* Passing NULL as /a output will set the primary output to NULL.
*/
WL_EXPORT void
weston_view_set_output(struct weston_view *view, struct weston_output *output)
{
if (view->output_destroy_listener.notify) {
wl_list_remove(&view->output_destroy_listener.link);
view->output_destroy_listener.notify = NULL;
}
view->output = output;
if (output) {
view->output_destroy_listener.notify =
notify_view_output_destroy;
wl_signal_add(&output->destroy_signal,
&view->output_destroy_listener);
}
}
/** Recalculate which output(s) the surface has views displayed on
*
* \param es The surface to remap to outputs
*
* Finds the output that is showing the largest amount of one
* of the surface's various views. This output becomes the
* surface's primary output for vsync and frame callback purposes.
*
* Also notes all outputs of all of the surface's views
* in the output_mask for the surface.
*/
static void
weston_surface_assign_output(struct weston_surface *es)
{
struct weston_output *new_output;
struct weston_view *view;
pixman_region32_t region;
uint32_t max, area, mask;
pixman_box32_t *e;
new_output = NULL;
max = 0;
mask = 0;
pixman_region32_init(&region);
wl_list_for_each(view, &es->views, surface_link) {
if (!view->output)
continue;
pixman_region32_intersect(&region, &view->transform.boundingbox,
&view->output->region);
e = pixman_region32_extents(&region);
area = (e->x2 - e->x1) * (e->y2 - e->y1);
mask |= view->output_mask;
if (area >= max) {
new_output = view->output;
max = area;
}
}
pixman_region32_fini(&region);
es->output = new_output;
weston_surface_update_output_mask(es, mask);
}
/** Recalculate which output(s) the view is displayed on
*
* \param ev The view to remap to outputs
*
* Identifies the set of outputs that the view is visible on,
* noting them into the output_mask. The output that the view
* is most visible on is set as the view's primary output.
*
* Also does the same for the view's surface. See
* weston_surface_assign_output().
*/
static void
weston_view_assign_output(struct weston_view *ev)
{
struct weston_compositor *ec = ev->surface->compositor;
struct weston_output *output, *new_output;
pixman_region32_t region;
uint32_t max, area, mask;
pixman_box32_t *e;
new_output = NULL;
max = 0;
mask = 0;
pixman_region32_init(&region);
wl_list_for_each(output, &ec->output_list, link) {
if (output->destroying)
continue;
pixman_region32_intersect(&region, &ev->transform.boundingbox,
&output->region);
e = pixman_region32_extents(&region);
area = (e->x2 - e->x1) * (e->y2 - e->y1);
if (area > 0)
mask |= 1u << output->id;
if (area >= max) {
new_output = output;
max = area;
}
}
pixman_region32_fini(&region);
weston_view_set_output(ev, new_output);
ev->output_mask = mask;
weston_surface_assign_output(ev->surface);
}
static void
weston_view_to_view_map(struct weston_view *from, struct weston_view *to,
int from_x, int from_y, int *to_x, int *to_y)
{
float x, y;
weston_view_to_global_float(from, from_x, from_y, &x, &y);
weston_view_from_global_float(to, x, y, &x, &y);
*to_x = round(x);
*to_y = round(y);
}
static void
weston_view_transfer_scissor(struct weston_view *from, struct weston_view *to)
{
pixman_box32_t *a;
pixman_box32_t b;
a = pixman_region32_extents(&from->geometry.scissor);
weston_view_to_view_map(from, to, a->x1, a->y1, &b.x1, &b.y1);
weston_view_to_view_map(from, to, a->x2, a->y2, &b.x2, &b.y2);
pixman_region32_fini(&to->geometry.scissor);
pixman_region32_init_with_extents(&to->geometry.scissor, &b);
}
static void
view_compute_bbox(struct weston_view *view, const pixman_box32_t *inbox,
pixman_region32_t *bbox)
{
float min_x = HUGE_VALF, min_y = HUGE_VALF;
float max_x = -HUGE_VALF, max_y = -HUGE_VALF;
int32_t s[4][2] = {
{ inbox->x1, inbox->y1 },
{ inbox->x1, inbox->y2 },
{ inbox->x2, inbox->y1 },
{ inbox->x2, inbox->y2 },
};
float int_x, int_y;
int i;
if (inbox->x1 == inbox->x2 || inbox->y1 == inbox->y2) {
/* avoid rounding empty bbox to 1x1 */
pixman_region32_init(bbox);
return;
}
for (i = 0; i < 4; ++i) {
float x, y;
weston_view_to_global_float(view, s[i][0], s[i][1], &x, &y);
if (x < min_x)
min_x = x;
if (x > max_x)
max_x = x;
if (y < min_y)
min_y = y;
if (y > max_y)
max_y = y;
}
int_x = floorf(min_x);
int_y = floorf(min_y);
pixman_region32_init_rect(bbox, int_x, int_y,
ceilf(max_x) - int_x, ceilf(max_y) - int_y);
}
static void
weston_view_update_transform_disable(struct weston_view *view)
{
view->transform.enabled = 0;
/* round off fractions when not transformed */
view->geometry.x = roundf(view->geometry.x);
view->geometry.y = roundf(view->geometry.y);
/* Otherwise identity matrix, but with x and y translation. */
view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE;
view->transform.position.matrix.d[12] = view->geometry.x;
view->transform.position.matrix.d[13] = view->geometry.y;
view->transform.matrix = view->transform.position.matrix;
view->transform.inverse = view->transform.position.matrix;
view->transform.inverse.d[12] = -view->geometry.x;
view->transform.inverse.d[13] = -view->geometry.y;
pixman_region32_init_rect(&view->transform.boundingbox,
0, 0,
view->surface->width,
view->surface->height);
if (view->geometry.scissor_enabled)
pixman_region32_intersect(&view->transform.boundingbox,
&view->transform.boundingbox,
&view->geometry.scissor);
pixman_region32_translate(&view->transform.boundingbox,
view->geometry.x, view->geometry.y);
if (view->alpha == 1.0) {
pixman_region32_copy(&view->transform.opaque,
&view->surface->opaque);
pixman_region32_translate(&view->transform.opaque,
view->geometry.x,
view->geometry.y);
}
}
static int
weston_view_update_transform_enable(struct weston_view *view)
{
struct weston_view *parent = view->geometry.parent;
struct weston_matrix *matrix = &view->transform.matrix;
struct weston_matrix *inverse = &view->transform.inverse;
struct weston_transform *tform;
pixman_region32_t surfregion;
const pixman_box32_t *surfbox;
view->transform.enabled = 1;
/* Otherwise identity matrix, but with x and y translation. */
view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE;
view->transform.position.matrix.d[12] = view->geometry.x;
view->transform.position.matrix.d[13] = view->geometry.y;
weston_matrix_init(matrix);
wl_list_for_each(tform, &view->geometry.transformation_list, link)
weston_matrix_multiply(matrix, &tform->matrix);
if (parent)
weston_matrix_multiply(matrix, &parent->transform.matrix);
if (weston_matrix_invert(inverse, matrix) < 0) {
/* Oops, bad total transformation, not invertible */
weston_log("error: weston_view %p"
" transformation not invertible.\n", view);
return -1;
}
if (view->alpha == 1.0 &&
matrix->type == WESTON_MATRIX_TRANSFORM_TRANSLATE) {
pixman_region32_copy(&view->transform.opaque,
&view->surface->opaque);
pixman_region32_translate(&view->transform.opaque,
matrix->d[12],
matrix->d[13]);
}
pixman_region32_init_rect(&surfregion, 0, 0,
view->surface->width, view->surface->height);
if (view->geometry.scissor_enabled)
pixman_region32_intersect(&surfregion, &surfregion,
&view->geometry.scissor);
surfbox = pixman_region32_extents(&surfregion);
view_compute_bbox(view, surfbox, &view->transform.boundingbox);
pixman_region32_fini(&surfregion);
return 0;
}
static struct weston_layer *
get_view_layer(struct weston_view *view)
{
if (view->parent_view)
return get_view_layer(view->parent_view);
return view->layer_link.layer;
}
WL_EXPORT void
weston_view_update_transform(struct weston_view *view)
{
struct weston_view *parent = view->geometry.parent;
struct weston_layer *layer;
pixman_region32_t mask;
if (!view->transform.dirty)
return;
if (parent)
weston_view_update_transform(parent);
view->transform.dirty = 0;
weston_view_damage_below(view);
pixman_region32_fini(&view->transform.boundingbox);
pixman_region32_fini(&view->transform.opaque);
pixman_region32_init(&view->transform.opaque);
/* transform.position is always in transformation_list */
if (view->geometry.transformation_list.next ==
&view->transform.position.link &&
view->geometry.transformation_list.prev ==
&view->transform.position.link &&
!parent) {
weston_view_update_transform_disable(view);
} else {
if (weston_view_update_transform_enable(view) < 0)
weston_view_update_transform_disable(view);
}
layer = get_view_layer(view);
if (layer) {
pixman_region32_init_with_extents(&mask, &layer->mask);
pixman_region32_intersect(&view->transform.boundingbox,
&view->transform.boundingbox, &mask);
pixman_region32_intersect(&view->transform.opaque,
&view->transform.opaque, &mask);
pixman_region32_fini(&mask);
}
if (parent) {
if (parent->geometry.scissor_enabled) {
view->geometry.scissor_enabled = true;
weston_view_transfer_scissor(parent, view);
} else {
view->geometry.scissor_enabled = false;
}
}
weston_view_damage_below(view);
weston_view_assign_output(view);
wl_signal_emit(&view->surface->compositor->transform_signal,
view->surface);
}
WL_EXPORT void
weston_view_geometry_dirty(struct weston_view *view)
{
struct weston_view *child;
/*
* The invariant: if view->geometry.dirty, then all views
* in view->geometry.child_list have geometry.dirty too.
* Corollary: if not parent->geometry.dirty, then all ancestors
* are not dirty.
*/
if (view->transform.dirty)
return;
view->transform.dirty = 1;
wl_list_for_each(child, &view->geometry.child_list,
geometry.parent_link)
weston_view_geometry_dirty(child);
}
WL_EXPORT void
weston_view_to_global_fixed(struct weston_view *view,
wl_fixed_t vx, wl_fixed_t vy,
wl_fixed_t *x, wl_fixed_t *y)
{
float xf, yf;
weston_view_to_global_float(view,
wl_fixed_to_double(vx),
wl_fixed_to_double(vy),
&xf, &yf);
*x = wl_fixed_from_double(xf);
*y = wl_fixed_from_double(yf);
}
WL_EXPORT void
weston_view_from_global_float(struct weston_view *view,
float x, float y, float *vx, float *vy)
{
if (view->transform.enabled) {
struct weston_vector v = { { x, y, 0.0f, 1.0f } };
weston_matrix_transform(&view->transform.inverse, &v);
if (fabsf(v.f[3]) < 1e-6) {
weston_log("warning: numerical instability in "
"weston_view_from_global(), divisor = %g\n",
v.f[3]);
*vx = 0;
*vy = 0;
return;
}
*vx = v.f[0] / v.f[3];
*vy = v.f[1] / v.f[3];
} else {
*vx = x - view->geometry.x;
*vy = y - view->geometry.y;
}
}
WL_EXPORT void
weston_view_from_global_fixed(struct weston_view *view,
wl_fixed_t x, wl_fixed_t y,
wl_fixed_t *vx, wl_fixed_t *vy)
{
float vxf, vyf;
weston_view_from_global_float(view,
wl_fixed_to_double(x),
wl_fixed_to_double(y),
&vxf, &vyf);
*vx = wl_fixed_from_double(vxf);
*vy = wl_fixed_from_double(vyf);
}
WL_EXPORT void
weston_view_from_global(struct weston_view *view,
int32_t x, int32_t y, int32_t *vx, int32_t *vy)
{
float vxf, vyf;
weston_view_from_global_float(view, x, y, &vxf, &vyf);
*vx = floorf(vxf);
*vy = floorf(vyf);
}
/**
* \param surface The surface to be repainted
*
* Marks the output(s) that the surface is shown on as needing to be
* repainted. See weston_output_schedule_repaint().
*/
WL_EXPORT void
weston_surface_schedule_repaint(struct weston_surface *surface)
{
struct weston_output *output;
wl_list_for_each(output, &surface->compositor->output_list, link)
if (surface->output_mask & (1u << output->id))
weston_output_schedule_repaint(output);
}
/**
* \param view The view to be repainted
*
* Marks the output(s) that the view is shown on as needing to be
* repainted. See weston_output_schedule_repaint().
*/
WL_EXPORT void
weston_view_schedule_repaint(struct weston_view *view)
{
struct weston_output *output;
wl_list_for_each(output, &view->surface->compositor->output_list, link)
if (view->output_mask & (1u << output->id))
weston_output_schedule_repaint(output);
}
/**
* XXX: This function does it the wrong way.
* surface->damage is the damage from the client, and causes
* surface_flush_damage() to copy pixels. No window management action can
* cause damage to the client-provided content, warranting re-upload!
*
* Instead of surface->damage, this function should record the damage
* with all the views for this surface to avoid extraneous texture
* uploads.
*/
WL_EXPORT void
weston_surface_damage(struct weston_surface *surface)
{
pixman_region32_union_rect(&surface->damage, &surface->damage,
0, 0, surface->width,
surface->height);
weston_surface_schedule_repaint(surface);
}
WL_EXPORT void
weston_view_set_position(struct weston_view *view, float x, float y)
{
if (view->geometry.x == x && view->geometry.y == y)
return;
view->geometry.x = x;
view->geometry.y = y;
weston_view_geometry_dirty(view);
}
static void
transform_parent_handle_parent_destroy(struct wl_listener *listener,
void *data)
{
struct weston_view *view =
container_of(listener, struct weston_view,
geometry.parent_destroy_listener);
weston_view_set_transform_parent(view, NULL);
}
WL_EXPORT void
weston_view_set_transform_parent(struct weston_view *view,
struct weston_view *parent)
{
if (view->geometry.parent) {
wl_list_remove(&view->geometry.parent_destroy_listener.link);
wl_list_remove(&view->geometry.parent_link);
if (!parent)
view->geometry.scissor_enabled = false;
}
view->geometry.parent = parent;
view->geometry.parent_destroy_listener.notify =
transform_parent_handle_parent_destroy;
if (parent) {
wl_signal_add(&parent->destroy_signal,
&view->geometry.parent_destroy_listener);
wl_list_insert(&parent->geometry.child_list,
&view->geometry.parent_link);
}
weston_view_geometry_dirty(view);
}
/** Set a clip mask rectangle on a view
*
* \param view The view to set the clip mask on.
* \param x Top-left corner X coordinate of the clip rectangle.
* \param y Top-left corner Y coordinate of the clip rectangle.
* \param width Width of the clip rectangle, non-negative.
* \param height Height of the clip rectangle, non-negative.
*
* A shell may set a clip mask rectangle on a view. Everything outside
* the rectangle is cut away for input and output purposes: it is
* not drawn and cannot be hit by hit-test based input like pointer
* motion or touch-downs. Everything inside the rectangle will behave
* normally. Clients are unaware of clipping.
*
* The rectangle is set in surface-local coordinates. Setting a clip
* mask rectangle does not affect the view position, the view is positioned
* as it would be without a clip. The clip also does not change
* weston_surface::width,height.
*
* The clip mask rectangle is part of transformation inheritance
* (weston_view_set_transform_parent()). A clip set in the root of the
* transformation inheritance tree will affect all views in the tree.
* A clip can be set only on the root view. Attempting to set a clip
* on view that has a transformation parent will fail. Assigning a parent
* to a view that has a clip set will cause the clip to be forgotten.
*
* Because the clip mask is an axis-aligned rectangle, it poses restrictions
* on the additional transformations in the child views. These transformations
* may not rotate the coordinate axes, i.e., only translation and scaling
* are allowed. Violating this restriction causes the clipping to malfunction.
* Furthermore, using scaling may cause rounding errors in child clipping.
*
* The clip mask rectangle is not automatically adjusted based on
* wl_surface.attach dx and dy arguments.
*
* A clip mask rectangle can be set only if the compositor capability
* WESTON_CAP_VIEW_CLIP_MASK is present.
*
* This function sets the clip mask rectangle and schedules a repaint for
* the view.
*/
WL_EXPORT void
weston_view_set_mask(struct weston_view *view,
int x, int y, int width, int height)
{
struct weston_compositor *compositor = view->surface->compositor;
if (!(compositor->capabilities & WESTON_CAP_VIEW_CLIP_MASK)) {
weston_log("%s not allowed without capability!\n", __func__);
return;
}
if (view->geometry.parent) {
weston_log("view %p has a parent, clip forbidden!\n", view);
return;
}
if (width < 0 || height < 0) {
weston_log("%s: illegal args %d, %d, %d, %d\n", __func__,
x, y, width, height);
return;
}
pixman_region32_fini(&view->geometry.scissor);
pixman_region32_init_rect(&view->geometry.scissor, x, y, width, height);
view->geometry.scissor_enabled = true;
weston_view_geometry_dirty(view);
weston_view_schedule_repaint(view);
}
/** Remove the clip mask from a view
*
* \param view The view to remove the clip mask from.
*
* Removed the clip mask rectangle and schedules a repaint.
*
* \sa weston_view_set_mask
*/
WL_EXPORT void
weston_view_set_mask_infinite(struct weston_view *view)
{
view->geometry.scissor_enabled = false;
weston_view_geometry_dirty(view);
weston_view_schedule_repaint(view);
}
/* Check if view should be displayed
*
* The indicator is set manually when assigning
* a view to a surface.
*
* This needs reworking. See the thread starting at:
*
* https://lists.freedesktop.org/archives/wayland-devel/2016-June/029656.html
*/
WL_EXPORT bool
weston_view_is_mapped(struct weston_view *view)
{
return view->is_mapped;
}
/* Check if a surface has a view assigned to it
*
* The indicator is set manually when mapping
* a surface and creating a view for it.
*
* This needs to go. See the thread starting at:
*
* https://lists.freedesktop.org/archives/wayland-devel/2016-June/029656.html
*
*/
WL_EXPORT bool
weston_surface_is_mapped(struct weston_surface *surface)
{
return surface->is_mapped;
}
static void
surface_set_size(struct weston_surface *surface, int32_t width, int32_t height)
{
struct weston_view *view;
if (surface->width == width && surface->height == height)
return;
surface->width = width;
surface->height = height;
wl_list_for_each(view, &surface->views, surface_link)
weston_view_geometry_dirty(view);
}
WL_EXPORT void
weston_surface_set_size(struct weston_surface *surface,
int32_t width, int32_t height)
{
assert(!surface->resource);
surface_set_size(surface, width, height);
}
static int
fixed_round_up_to_int(wl_fixed_t f)
{
return wl_fixed_to_int(wl_fixed_from_int(1) - 1 + f);
}
static void
convert_size_by_transform_scale(int32_t *width_out, int32_t *height_out,
int32_t width, int32_t height,
uint32_t transform,
int32_t scale)
{
assert(scale > 0);
switch (transform) {
case WL_OUTPUT_TRANSFORM_NORMAL:
case WL_OUTPUT_TRANSFORM_180:
case WL_OUTPUT_TRANSFORM_FLIPPED:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
*width_out = width / scale;
*height_out = height / scale;
break;
case WL_OUTPUT_TRANSFORM_90:
case WL_OUTPUT_TRANSFORM_270:
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
*width_out = height / scale;
*height_out = width / scale;
break;
default:
assert(0 && "invalid transform");
}
}
static void
weston_surface_calculate_size_from_buffer(struct weston_surface *surface)
{
struct weston_buffer_viewport *vp = &surface->buffer_viewport;
if (!surface->buffer_ref.buffer) {
surface->width_from_buffer = 0;
surface->height_from_buffer = 0;
return;
}
convert_size_by_transform_scale(&surface->width_from_buffer,
&surface->height_from_buffer,
surface->buffer_ref.buffer->width,
surface->buffer_ref.buffer->height,
vp->buffer.transform,
vp->buffer.scale);
}
static void
weston_surface_update_size(struct weston_surface *surface)
{
struct weston_buffer_viewport *vp = &surface->buffer_viewport;
int32_t width, height;
width = surface->width_from_buffer;
height = surface->height_from_buffer;
if (width != 0 && vp->surface.width != -1) {
surface_set_size(surface,
vp->surface.width, vp->surface.height);
return;
}
if (width != 0 && vp->buffer.src_width != wl_fixed_from_int(-1)) {
int32_t w = fixed_round_up_to_int(vp->buffer.src_width);
int32_t h = fixed_round_up_to_int(vp->buffer.src_height);
surface_set_size(surface, w ?: 1, h ?: 1);
return;
}
surface_set_size(surface, width, height);
}
WL_EXPORT void
weston_compositor_get_time(struct timespec *time)
{
clock_gettime(CLOCK_REALTIME, time);
}
WL_EXPORT struct weston_view *
weston_compositor_pick_view(struct weston_compositor *compositor,
wl_fixed_t x, wl_fixed_t y,
wl_fixed_t *vx, wl_fixed_t *vy)
{
struct weston_view *view;
wl_fixed_t view_x, view_y;
int view_ix, view_iy;
int ix = wl_fixed_to_int(x);
int iy = wl_fixed_to_int(y);
wl_list_for_each(view, &compositor->view_list, link) {
if (!pixman_region32_contains_point(
&view->transform.boundingbox, ix, iy, NULL))
continue;
weston_view_from_global_fixed(view, x, y, &view_x, &view_y);
view_ix = wl_fixed_to_int(view_x);
view_iy = wl_fixed_to_int(view_y);
if (!pixman_region32_contains_point(&view->surface->input,
view_ix, view_iy, NULL))
continue;
if (view->geometry.scissor_enabled &&
!pixman_region32_contains_point(&view->geometry.scissor,
view_ix, view_iy, NULL))
continue;
*vx = view_x;
*vy = view_y;
return view;
}
*vx = wl_fixed_from_int(-1000000);
*vy = wl_fixed_from_int(-1000000);
return NULL;
}
static void
weston_compositor_repick(struct weston_compositor *compositor)
{
struct weston_seat *seat;
if (!compositor->session_active)
return;
wl_list_for_each(seat, &compositor->seat_list, link)
weston_seat_repick(seat);
}
WL_EXPORT void
weston_view_unmap(struct weston_view *view)
{
struct weston_seat *seat;
if (!weston_view_is_mapped(view))
return;
weston_view_damage_below(view);
weston_view_set_output(view, NULL);
view->plane = NULL;
view->is_mapped = false;
weston_layer_entry_remove(&view->layer_link);
wl_list_remove(&view->link);
wl_list_init(&view->link);
view->output_mask = 0;
weston_surface_assign_output(view->surface);
if (weston_surface_is_mapped(view->surface))
return;
wl_list_for_each(seat, &view->surface->compositor->seat_list, link) {
struct weston_touch *touch = weston_seat_get_touch(seat);
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
struct weston_keyboard *keyboard =
weston_seat_get_keyboard(seat);
if (keyboard && keyboard->focus == view->surface)
weston_keyboard_set_focus(keyboard, NULL);
if (pointer && pointer->focus == view)
weston_pointer_clear_focus(pointer);
if (touch && touch->focus == view)
weston_touch_set_focus(touch, NULL);
}
}
WL_EXPORT void
weston_surface_unmap(struct weston_surface *surface)
{
struct weston_view *view;
surface->is_mapped = false;
wl_list_for_each(view, &surface->views, surface_link)
weston_view_unmap(view);
surface->output = NULL;
}
static void
weston_surface_reset_pending_buffer(struct weston_surface *surface)
{
weston_surface_state_set_buffer(&surface->pending, NULL);
surface->pending.sx = 0;
surface->pending.sy = 0;
surface->pending.newly_attached = 0;
surface->pending.buffer_viewport.changed = 0;
}
WL_EXPORT void
weston_view_destroy(struct weston_view *view)
{
wl_signal_emit(&view->destroy_signal, view);
assert(wl_list_empty(&view->geometry.child_list));
if (weston_view_is_mapped(view)) {
weston_view_unmap(view);
weston_compositor_build_view_list(view->surface->compositor);
}
wl_list_remove(&view->link);
weston_layer_entry_remove(&view->layer_link);
pixman_region32_fini(&view->clip);
pixman_region32_fini(&view->geometry.scissor);
pixman_region32_fini(&view->transform.boundingbox);
pixman_region32_fini(&view->transform.opaque);
weston_view_set_transform_parent(view, NULL);
weston_view_set_output(view, NULL);
wl_list_remove(&view->surface_link);
free(view);
}
WL_EXPORT void
weston_surface_destroy(struct weston_surface *surface)
{
struct weston_frame_callback *cb, *next;
struct weston_view *ev, *nv;
struct weston_pointer_constraint *constraint, *next_constraint;
if (--surface->ref_count > 0)
return;
assert(surface->resource == NULL);
wl_signal_emit(&surface->destroy_signal, surface);
assert(wl_list_empty(&surface->subsurface_list_pending));
assert(wl_list_empty(&surface->subsurface_list));
wl_list_for_each_safe(ev, nv, &surface->views, surface_link)
weston_view_destroy(ev);
weston_surface_state_fini(&surface->pending);
weston_buffer_reference(&surface->buffer_ref, NULL);
pixman_region32_fini(&surface->damage);
pixman_region32_fini(&surface->opaque);
pixman_region32_fini(&surface->input);
wl_list_for_each_safe(cb, next, &surface->frame_callback_list, link)
wl_resource_destroy(cb->resource);
weston_presentation_feedback_discard_list(&surface->feedback_list);
wl_list_for_each_safe(constraint, next_constraint,
&surface->pointer_constraints,
link)
weston_pointer_constraint_destroy(constraint);
free(surface);
}
static void
destroy_surface(struct wl_resource *resource)
{
struct weston_surface *surface = wl_resource_get_user_data(resource);
assert(surface);
/* Set the resource to NULL, since we don't want to leave a
* dangling pointer if the surface was refcounted and survives
* the weston_surface_destroy() call. */
surface->resource = NULL;
if (surface->viewport_resource)
wl_resource_set_user_data(surface->viewport_resource, NULL);
if (surface->blending_resource)
wl_resource_set_user_data(surface->blending_resource, NULL);
weston_surface_destroy(surface);
}
static void
weston_buffer_destroy_handler(struct wl_listener *listener, void *data)
{
struct weston_buffer *buffer =
container_of(listener, struct weston_buffer, destroy_listener);
wl_signal_emit(&buffer->destroy_signal, buffer);
free(buffer);
}
WL_EXPORT struct weston_buffer *
weston_buffer_from_resource(struct wl_resource *resource)
{
struct weston_buffer *buffer;
struct wl_listener *listener;
listener = wl_resource_get_destroy_listener(resource,
weston_buffer_destroy_handler);
if (listener)
return container_of(listener, struct weston_buffer,
destroy_listener);
buffer = zalloc(sizeof *buffer);
if (buffer == NULL)
return NULL;
buffer->resource = resource;
wl_signal_init(&buffer->destroy_signal);
buffer->destroy_listener.notify = weston_buffer_destroy_handler;
buffer->y_inverted = 1;
wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
return buffer;
}
static void
weston_buffer_reference_handle_destroy(struct wl_listener *listener,
void *data)
{
struct weston_buffer_reference *ref =
container_of(listener, struct weston_buffer_reference,
destroy_listener);
assert((struct weston_buffer *)data == ref->buffer);
ref->buffer = NULL;
}
WL_EXPORT void
weston_buffer_reference(struct weston_buffer_reference *ref,
struct weston_buffer *buffer)
{
if (ref->buffer && buffer != ref->buffer) {
ref->buffer->busy_count--;
if (ref->buffer->busy_count == 0) {
assert(wl_resource_get_client(ref->buffer->resource));
wl_buffer_send_release(ref->buffer->resource);
}
wl_list_remove(&ref->destroy_listener.link);
}
if (buffer && buffer != ref->buffer) {
buffer->busy_count++;
wl_signal_add(&buffer->destroy_signal,
&ref->destroy_listener);
}
ref->buffer = buffer;
ref->destroy_listener.notify = weston_buffer_reference_handle_destroy;
}
static void
weston_surface_attach(struct weston_surface *surface,
struct weston_buffer *buffer)
{
weston_buffer_reference(&surface->buffer_ref, buffer);
if (!buffer) {
if (weston_surface_is_mapped(surface))
weston_surface_unmap(surface);
}
surface->compositor->renderer->attach(surface, buffer);
weston_surface_calculate_size_from_buffer(surface);
weston_presentation_feedback_discard_list(&surface->feedback_list);
}
WL_EXPORT void
weston_compositor_damage_all(struct weston_compositor *compositor)
{
struct weston_output *output;
wl_list_for_each(output, &compositor->output_list, link)
weston_output_damage(output);
}
WL_EXPORT void
weston_output_damage(struct weston_output *output)
{
struct weston_compositor *compositor = output->compositor;
pixman_region32_union(&compositor->primary_plane.damage,
&compositor->primary_plane.damage,
&output->region);
weston_output_schedule_repaint(output);
}
static void
surface_flush_damage(struct weston_surface *surface)
{
if (surface->buffer_ref.buffer &&
wl_shm_buffer_get(surface->buffer_ref.buffer->resource))
surface->compositor->renderer->flush_damage(surface);
if (weston_timeline_enabled_ &&
pixman_region32_not_empty(&surface->damage))
TL_POINT("core_flush_damage", TLP_SURFACE(surface),
TLP_OUTPUT(surface->output), TLP_END);
pixman_region32_clear(&surface->damage);
}
static void
view_accumulate_damage(struct weston_view *view,
pixman_region32_t *opaque)
{
pixman_region32_t damage;
pixman_region32_init(&damage);
if (view->transform.enabled) {
pixman_box32_t *extents;
extents = pixman_region32_extents(&view->surface->damage);
view_compute_bbox(view, extents, &damage);
} else {
pixman_region32_copy(&damage, &view->surface->damage);
pixman_region32_translate(&damage,
view->geometry.x, view->geometry.y);
}
pixman_region32_intersect(&damage, &damage,
&view->transform.boundingbox);
pixman_region32_subtract(&damage, &damage, opaque);
pixman_region32_union(&view->plane->damage,
&view->plane->damage, &damage);
pixman_region32_fini(&damage);
pixman_region32_copy(&view->clip, opaque);
pixman_region32_union(opaque, opaque, &view->transform.opaque);
}
static void
compositor_accumulate_damage(struct weston_compositor *ec)
{
struct weston_plane *plane;
struct weston_view *ev;
pixman_region32_t opaque, clip;
pixman_region32_init(&clip);
wl_list_for_each(plane, &ec->plane_list, link) {
pixman_region32_copy(&plane->clip, &clip);
pixman_region32_init(&opaque);
wl_list_for_each(ev, &ec->view_list, link) {
if (ev->plane != plane)
continue;
view_accumulate_damage(ev, &opaque);
}
pixman_region32_union(&clip, &clip, &opaque);
pixman_region32_fini(&opaque);
}
pixman_region32_fini(&clip);
wl_list_for_each(ev, &ec->view_list, link)
ev->surface->touched = false;
wl_list_for_each(ev, &ec->view_list, link) {
if (ev->surface->touched)
continue;
ev->surface->touched = true;
surface_flush_damage(ev->surface);
/* Both the renderer and the backend have seen the buffer
* by now. If renderer needs the buffer, it has its own
* reference set. If the backend wants to keep the buffer
* around for migrating the surface into a non-primary plane
* later, keep_buffer is true. Otherwise, drop the core
* reference now, and allow early buffer release. This enables
* clients to use single-buffering.
*/
if (!ev->surface->keep_buffer)
weston_buffer_reference(&ev->surface->buffer_ref, NULL);
}
}
static void
surface_stash_subsurface_views(struct weston_surface *surface)
{
struct weston_subsurface *sub;
wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
if (sub->surface == surface)
continue;
wl_list_insert_list(&sub->unused_views, &sub->surface->views);
wl_list_init(&sub->surface->views);
surface_stash_subsurface_views(sub->surface);
}
}
static void
surface_free_unused_subsurface_views(struct weston_surface *surface)
{
struct weston_subsurface *sub;
struct weston_view *view, *nv;
wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
if (sub->surface == surface)
continue;
wl_list_for_each_safe(view, nv, &sub->unused_views, surface_link) {
weston_view_unmap (view);
weston_view_destroy(view);
}
surface_free_unused_subsurface_views(sub->surface);
}
}
static void
view_list_add_subsurface_view(struct weston_compositor *compositor,
struct weston_subsurface *sub,
struct weston_view *parent)
{
struct weston_subsurface *child;
struct weston_view *view = NULL, *iv;
if (!weston_surface_is_mapped(sub->surface))
return;
wl_list_for_each(iv, &sub->unused_views, surface_link) {
if (iv->geometry.parent == parent) {
view = iv;
break;
}
}
if (view) {
/* Put it back in the surface's list of views */
wl_list_remove(&view->surface_link);
wl_list_insert(&sub->surface->views, &view->surface_link);
} else {
view = weston_view_create(sub->surface);
weston_view_set_position(view,
sub->position.x,
sub->position.y);
weston_view_set_transform_parent(view, parent);
}
view->parent_view = parent;
weston_view_update_transform(view);
view->is_mapped = true;
if (wl_list_empty(&sub->surface->subsurface_list)) {
wl_list_insert(compositor->view_list.prev, &view->link);
return;
}
wl_list_for_each(child, &sub->surface->subsurface_list, parent_link) {
if (child->surface == sub->surface)
wl_list_insert(compositor->view_list.prev, &view->link);
else
view_list_add_subsurface_view(compositor, child, view);
}
}
/* This recursively adds the sub-surfaces for a view, relying on the
* sub-surface order. Thus, if a client restacks the sub-surfaces, that
* change first happens to the sub-surface list, and then automatically
* propagates here. See weston_surface_damage_subsurfaces() for how the
* sub-surfaces receive damage when the client changes the state.
*/
static void
view_list_add(struct weston_compositor *compositor,
struct weston_view *view)
{
struct weston_subsurface *sub;
weston_view_update_transform(view);
if (wl_list_empty(&view->surface->subsurface_list)) {
wl_list_insert(compositor->view_list.prev, &view->link);
return;
}
wl_list_for_each(sub, &view->surface->subsurface_list, parent_link) {
if (sub->surface == view->surface)
wl_list_insert(compositor->view_list.prev, &view->link);
else
view_list_add_subsurface_view(compositor, sub, view);
}
}
static void
weston_compositor_build_view_list(struct weston_compositor *compositor)
{
struct weston_view *view;
struct weston_layer *layer;
wl_list_for_each(layer, &compositor->layer_list, link)
wl_list_for_each(view, &layer->view_list.link, layer_link.link)
surface_stash_subsurface_views(view->surface);
wl_list_init(&compositor->view_list);
wl_list_for_each(layer, &compositor->layer_list, link) {
wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
view_list_add(compositor, view);
}
}
wl_list_for_each(layer, &compositor->layer_list, link)
wl_list_for_each(view, &layer->view_list.link, layer_link.link)
surface_free_unused_subsurface_views(view->surface);
}
static void
weston_output_take_feedback_list(struct weston_output *output,
struct weston_surface *surface)
{
struct weston_view *view;
struct weston_presentation_feedback *feedback;
uint32_t flags = 0xffffffff;
if (wl_list_empty(&surface->feedback_list))
return;
/* All views must have the flag for the flag to survive. */
wl_list_for_each(view, &surface->views, surface_link) {
/* ignore views that are not on this output at all */
if (view->output_mask & (1u << output->id))
flags &= view->psf_flags;
}
wl_list_for_each(feedback, &surface->feedback_list, link)
feedback->psf_flags = flags;
wl_list_insert_list(&output->feedback_list, &surface->feedback_list);
wl_list_init(&surface->feedback_list);
}
static int
weston_output_repaint(struct weston_output *output, void *repaint_data)
{
struct weston_compositor *ec = output->compositor;
struct weston_view *ev;
struct weston_animation *animation, *next;
struct weston_frame_callback *cb, *cnext;
struct wl_list frame_callback_list;
pixman_region32_t output_damage;
int r;
uint32_t frame_time_msec;
if (output->destroying)
return 0;
TL_POINT("core_repaint_begin", TLP_OUTPUT(output), TLP_END);
/* Rebuild the surface list and update surface transforms up front. */
weston_compositor_build_view_list(ec);
if (output->assign_planes && !output->disable_planes) {
output->assign_planes(output, repaint_data);
} else {
wl_list_for_each(ev, &ec->view_list, link) {
weston_view_move_to_plane(ev, &ec->primary_plane);
ev->psf_flags = 0;
}
}
wl_list_init(&frame_callback_list);
wl_list_for_each(ev, &ec->view_list, link) {
/* Note: This operation is safe to do multiple times on the
* same surface.
*/
if (ev->surface->output == output) {
wl_list_insert_list(&frame_callback_list,
&ev->surface->frame_callback_list);
wl_list_init(&ev->surface->frame_callback_list);
weston_output_take_feedback_list(output, ev->surface);
}
}
compositor_accumulate_damage(ec);
pixman_region32_init(&output_damage);
pixman_region32_intersect(&output_damage,
&ec->primary_plane.damage, &output->region);
pixman_region32_subtract(&output_damage,
&output_damage, &ec->primary_plane.clip);
if (output->dirty)
weston_output_update_matrix(output);
r = output->repaint(output, &output_damage, repaint_data);
pixman_region32_fini(&output_damage);
output->repaint_needed = false;
if (r == 0)
output->repaint_status = REPAINT_AWAITING_COMPLETION;
weston_compositor_repick(ec);
frame_time_msec = timespec_to_msec(&output->frame_time);
wl_list_for_each_safe(cb, cnext, &frame_callback_list, link) {
wl_callback_send_done(cb->resource, frame_time_msec);
wl_resource_destroy(cb->resource);
}
wl_list_for_each_safe(animation, next, &output->animation_list, link) {
animation->frame_counter++;
animation->frame(animation, output, &output->frame_time);
}
TL_POINT("core_repaint_posted", TLP_OUTPUT(output), TLP_END);
return r;
}
static void
weston_output_schedule_repaint_reset(struct weston_output *output)
{
output->repaint_status = REPAINT_NOT_SCHEDULED;
TL_POINT("core_repaint_exit_loop", TLP_OUTPUT(output), TLP_END);
}
static int
weston_output_maybe_repaint(struct weston_output *output, struct timespec *now,
void *repaint_data)
{
struct weston_compositor *compositor = output->compositor;
int ret = 0;
int64_t msec_to_repaint;
/* We're not ready yet; come back to make a decision later. */
if (output->repaint_status != REPAINT_SCHEDULED)
return ret;
msec_to_repaint = timespec_sub_to_msec(&output->next_repaint, now);
if (msec_to_repaint > 1)
return ret;
/* If we're sleeping, drop the repaint machinery entirely; we will
* explicitly repaint all outputs when we come back. */
if (compositor->state == WESTON_COMPOSITOR_SLEEPING ||
compositor->state == WESTON_COMPOSITOR_OFFSCREEN)
goto err;
/* We don't actually need to repaint this output; drop it from
* repaint until something causes damage. */
if (!output->repaint_needed)
goto err;
/* If repaint fails, we aren't going to get weston_output_finish_frame
* to trigger a new repaint, so drop it from repaint and hope
* something schedules a successful repaint later. As repainting may
* take some time, re-read our clock as a courtesy to the next
* output. */
ret = weston_output_repaint(output, repaint_data);
weston_compositor_read_presentation_clock(compositor, now);
if (ret != 0)
goto err;
output->repainted = true;
return ret;
err:
weston_output_schedule_repaint_reset(output);
return ret;
}
static void
output_repaint_timer_arm(struct weston_compositor *compositor)
{
struct weston_output *output;
bool any_should_repaint = false;
struct timespec now;
int64_t msec_to_next = INT64_MAX;
weston_compositor_read_presentation_clock(compositor, &now);
wl_list_for_each(output, &compositor->output_list, link) {
int64_t msec_to_this;
if (output->repaint_status != REPAINT_SCHEDULED)
continue;
msec_to_this = timespec_sub_to_msec(&output->next_repaint,
&now);
if (!any_should_repaint || msec_to_this < msec_to_next)
msec_to_next = msec_to_this;
any_should_repaint = true;
}
if (!any_should_repaint)
return;
/* Even if we should repaint immediately, add the minimum 1 ms delay.
* This is a workaround to allow coalescing multiple output repaints
* particularly from weston_output_finish_frame()
* into the same call, which would not happen if we called
* output_repaint_timer_handler() directly.
*/
if (msec_to_next < 1)
msec_to_next = 1;
wl_event_source_timer_update(compositor->repaint_timer, msec_to_next);
}
static int
output_repaint_timer_handler(void *data)
{
struct weston_compositor *compositor = data;
struct weston_output *output;
struct timespec now;
void *repaint_data = NULL;
int ret = 0;
weston_compositor_read_presentation_clock(compositor, &now);
if (compositor->backend->repaint_begin)
repaint_data = compositor->backend->repaint_begin(compositor);
wl_list_for_each(output, &compositor->output_list, link) {
ret = weston_output_maybe_repaint(output, &now, repaint_data);
if (ret)
break;
}
if (ret == 0) {
if (compositor->backend->repaint_flush)
compositor->backend->repaint_flush(compositor,
repaint_data);
} else {
wl_list_for_each(output, &compositor->output_list, link) {
if (output->repainted)
weston_output_schedule_repaint_reset(output);
}
if (compositor->backend->repaint_cancel)
compositor->backend->repaint_cancel(compositor,
repaint_data);
}
wl_list_for_each(output, &compositor->output_list, link)
output->repainted = false;
output_repaint_timer_arm(compositor);
return 0;
}
WL_EXPORT void
weston_output_finish_frame(struct weston_output *output,
const struct timespec *stamp,
uint32_t presented_flags)
{
struct weston_compositor *compositor = output->compositor;
int32_t refresh_nsec;
struct timespec now;
int64_t msec_rel;
assert(output->repaint_status == REPAINT_AWAITING_COMPLETION);
assert(stamp || (presented_flags & WP_PRESENTATION_FEEDBACK_INVALID));
weston_compositor_read_presentation_clock(compositor, &now);
/* If we haven't been supplied any timestamp at all, we don't have a
* timebase to work against, so any delay just wastes time. Push a
* repaint as soon as possible so we can get on with it. */
if (!stamp) {
output->next_repaint = now;
goto out;
}
TL_POINT("core_repaint_finished", TLP_OUTPUT(output),
TLP_VBLANK(stamp), TLP_END);
refresh_nsec = millihz_to_nsec(output->current_mode->refresh);
weston_presentation_feedback_present_list(&output->feedback_list,
output, refresh_nsec, stamp,
output->msc,
presented_flags);
output->frame_time = *stamp;
timespec_add_nsec(&output->next_repaint, stamp, refresh_nsec);
timespec_add_msec(&output->next_repaint, &output->next_repaint,
-compositor->repaint_msec);
msec_rel = timespec_sub_to_msec(&output->next_repaint, &now);
if (msec_rel < -1000 || msec_rel > 1000) {
static bool warned;
if (!warned)
weston_log("Warning: computed repaint delay is "
"insane: %lld msec\n", (long long) msec_rel);
warned = true;
output->next_repaint = now;
}
/* Called from restart_repaint_loop and restart happens already after
* the deadline given by repaint_msec? In that case we delay until
* the deadline of the next frame, to give clients a more predictable
* timing of the repaint cycle to lock on. */
if (presented_flags == WP_PRESENTATION_FEEDBACK_INVALID &&
msec_rel < 0) {
while (timespec_sub_to_nsec(&output->next_repaint, &now) < 0) {
timespec_add_nsec(&output->next_repaint,
&output->next_repaint,
refresh_nsec);
}
}
out:
output->repaint_status = REPAINT_SCHEDULED;
output_repaint_timer_arm(compositor);
}
static void
idle_repaint(void *data)
{
struct weston_output *output = data;
assert(output->repaint_status == REPAINT_BEGIN_FROM_IDLE);
output->repaint_status = REPAINT_AWAITING_COMPLETION;
output->idle_repaint_source = NULL;
output->start_repaint_loop(output);
}
WL_EXPORT void
weston_layer_entry_insert(struct weston_layer_entry *list,
struct weston_layer_entry *entry)
{
wl_list_insert(&list->link, &entry->link);
entry->layer = list->layer;
}
WL_EXPORT void
weston_layer_entry_remove(struct weston_layer_entry *entry)
{
wl_list_remove(&entry->link);
wl_list_init(&entry->link);
entry->layer = NULL;
}
/** Initialize the weston_layer struct.
*
* \param compositor The compositor instance
* \param layer The layer to initialize
*/
WL_EXPORT void
weston_layer_init(struct weston_layer *layer,
struct weston_compositor *compositor)
{
layer->compositor = compositor;
wl_list_init(&layer->link);
wl_list_init(&layer->view_list.link);
layer->view_list.layer = layer;
weston_layer_set_mask_infinite(layer);
}
/** Sets the position of the layer in the layer list. The layer will be placed
* below any layer with the same position value, if any.
* This function is safe to call if the layer is already on the list, but the
* layer may be moved below other layers at the same position, if any.
*
* \param layer The layer to modify
* \param position The position the layer will be placed at
*/
WL_EXPORT void
weston_layer_set_position(struct weston_layer *layer,
enum weston_layer_position position)
{
struct weston_layer *below;
wl_list_remove(&layer->link);
/* layer_list is ordered from top to bottom, the last layer being the
* background with the smallest position value */
layer->position = position;
wl_list_for_each_reverse(below, &layer->compositor->layer_list, link) {
if (below->position >= layer->position) {
wl_list_insert(&below->link, &layer->link);
return;
}
}
wl_list_insert(&layer->compositor->layer_list, &layer->link);
}
/** Hide a layer by taking it off the layer list.
* This function is safe to call if the layer is not on the list.
*
* \param layer The layer to hide
*/
WL_EXPORT void
weston_layer_unset_position(struct weston_layer *layer)
{
wl_list_remove(&layer->link);
wl_list_init(&layer->link);
}
WL_EXPORT void
weston_layer_set_mask(struct weston_layer *layer,
int x, int y, int width, int height)
{
struct weston_view *view;
layer->mask.x1 = x;
layer->mask.x2 = x + width;
layer->mask.y1 = y;
layer->mask.y2 = y + height;
wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
weston_view_geometry_dirty(view);
}
}
WL_EXPORT void
weston_layer_set_mask_infinite(struct weston_layer *layer)
{
weston_layer_set_mask(layer, INT32_MIN, INT32_MIN,
UINT32_MAX, UINT32_MAX);
}
WL_EXPORT void
weston_output_schedule_repaint(struct weston_output *output)
{
struct weston_compositor *compositor = output->compositor;
struct wl_event_loop *loop;
if (compositor->state == WESTON_COMPOSITOR_SLEEPING ||
compositor->state == WESTON_COMPOSITOR_OFFSCREEN)
return;
if (!output->repaint_needed)
TL_POINT("core_repaint_req", TLP_OUTPUT(output), TLP_END);
loop = wl_display_get_event_loop(compositor->wl_display);
output->repaint_needed = true;
/* If we already have a repaint scheduled for our idle handler,
* no need to set it again. If the repaint has been called but
* not finished, then weston_output_finish_frame() will notice
* that a repaint is needed and schedule one. */
if (output->repaint_status != REPAINT_NOT_SCHEDULED)
return;
output->repaint_status = REPAINT_BEGIN_FROM_IDLE;
assert(!output->idle_repaint_source);
output->idle_repaint_source = wl_event_loop_add_idle(loop, idle_repaint,
output);
TL_POINT("core_repaint_enter_loop", TLP_OUTPUT(output), TLP_END);
}
WL_EXPORT void
weston_compositor_schedule_repaint(struct weston_compositor *compositor)
{
struct weston_output *output;
wl_list_for_each(output, &compositor->output_list, link)
weston_output_schedule_repaint(output);
}
static void
surface_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void
surface_attach(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
{
struct weston_surface *surface = wl_resource_get_user_data(resource);
struct weston_buffer *buffer = NULL;
if (buffer_resource) {
buffer = weston_buffer_from_resource(buffer_resource);
if (buffer == NULL) {
wl_client_post_no_memory(client);
return;
}
}
/* Attach, attach, without commit in between does not send
* wl_buffer.release. */
weston_surface_state_set_buffer(&surface->pending, buffer);
surface->pending.sx = sx;
surface->pending.sy = sy;
surface->pending.newly_attached = 1;
}
static void
surface_damage(struct wl_client *client,
struct wl_resource *resource,
int32_t x, int32_t y, int32_t width, int32_t height)
{
struct weston_surface *surface = wl_resource_get_user_data(resource);
if (width <= 0 || height <= 0)
return;
pixman_region32_union_rect(&surface->pending.damage_surface,
&surface->pending.damage_surface,
x, y, width, height);
}
static void
surface_damage_buffer(struct wl_client *client,
struct wl_resource *resource,
int32_t x, int32_t y, int32_t width, int32_t height)
{
struct weston_surface *surface = wl_resource_get_user_data(resource);
if (width <= 0 || height <= 0)
return;
pixman_region32_union_rect(&surface->pending.damage_buffer,
&surface->pending.damage_buffer,
x, y, width, height);
}
static void
destroy_frame_callback(struct wl_resource *resource)
{
struct weston_frame_callback *cb = wl_resource_get_user_data(resource);
wl_list_remove(&cb->link);
free(cb);
}
static void
surface_frame(struct wl_client *client,
struct wl_resource *resource, uint32_t callback)
{
struct weston_frame_callback *cb;
struct weston_surface *surface = wl_resource_get_user_data(resource);
cb = malloc(sizeof *cb);
if (cb == NULL) {
wl_resource_post_no_memory(resource);
return;
}
cb->resource = wl_resource_create(client, &wl_callback_interface, 1,
callback);
if (cb->resource == NULL) {
free(cb);
wl_resource_post_no_memory(resource);
return;
}
wl_resource_set_implementation(cb->resource, NULL, cb,
destroy_frame_callback);
wl_list_insert(surface->pending.frame_callback_list.prev, &cb->link);
}
static void
surface_set_opaque_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource)
{
struct weston_surface *surface = wl_resource_get_user_data(resource);
struct weston_region *region;
if (region_resource) {
region = wl_resource_get_user_data(region_resource);
pixman_region32_copy(&surface->pending.opaque,
&region->region);
} else {
pixman_region32_clear(&surface->pending.opaque);
}
}
static void
surface_set_input_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource)
{
struct weston_surface *surface = wl_resource_get_user_data(resource);
struct weston_region *region;
if (region_resource) {
region = wl_resource_get_user_data(region_resource);
pixman_region32_copy(&surface->pending.input,
&region->region);
} else {
pixman_region32_fini(&surface->pending.input);
region_init_infinite(&surface->pending.input);
}
}
/* Cause damage to this sub-surface and all its children.
*
* This is useful when there are state changes that need an implicit
* damage, e.g. a z-order change.
*/
static void
weston_surface_damage_subsurfaces(struct weston_subsurface *sub)
{
struct weston_subsurface *child;
weston_surface_damage(sub->surface);
sub->reordered = false;
wl_list_for_each(child, &sub->surface->subsurface_list, parent_link)
if (child != sub)
weston_surface_damage_subsurfaces(child);
}
static void
weston_surface_commit_subsurface_order(struct weston_surface *surface)
{
struct weston_subsurface *sub;
wl_list_for_each_reverse(sub, &surface->subsurface_list_pending,
parent_link_pending) {
wl_list_remove(&sub->parent_link);
wl_list_insert(&surface->subsurface_list, &sub->parent_link);
if (sub->reordered)
weston_surface_damage_subsurfaces(sub);
}
}
static void
weston_surface_build_buffer_matrix(const struct weston_surface *surface,
struct weston_matrix *matrix)
{
const struct weston_buffer_viewport *vp = &surface->buffer_viewport;
double src_width, src_height, dest_width, dest_height;
weston_matrix_init(matrix);
if (vp->buffer.src_width == wl_fixed_from_int(-1)) {
src_width = surface->width_from_buffer;
src_height = surface->height_from_buffer;
} else {
src_width = wl_fixed_to_double(vp->buffer.src_width);
src_height = wl_fixed_to_double(vp->buffer.src_height);
}
if (vp->surface.width == -1) {
dest_width = src_width;
dest_height = src_height;
} else {
dest_width = vp->surface.width;
dest_height = vp->surface.height;
}
if (src_width != dest_width || src_height != dest_height)
weston_matrix_scale(matrix,
src_width / dest_width,
src_height / dest_height, 1);
if (vp->buffer.src_width != wl_fixed_from_int(-1))
weston_matrix_translate(matrix,
wl_fixed_to_double(vp->buffer.src_x),
wl_fixed_to_double(vp->buffer.src_y),
0);
switch (vp->buffer.transform) {
case WL_OUTPUT_TRANSFORM_FLIPPED:
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
weston_matrix_scale(matrix, -1, 1, 1);
weston_matrix_translate(matrix,
surface->width_from_buffer, 0, 0);
break;
}
switch (vp->buffer.transform) {
default:
case WL_OUTPUT_TRANSFORM_NORMAL:
case WL_OUTPUT_TRANSFORM_FLIPPED:
break;
case WL_OUTPUT_TRANSFORM_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
weston_matrix_rotate_xy(matrix, 0, 1);
weston_matrix_translate(matrix,
surface->height_from_buffer, 0, 0);
break;
case WL_OUTPUT_TRANSFORM_180:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
weston_matrix_rotate_xy(matrix, -1, 0);
weston_matrix_translate(matrix,
surface->width_from_buffer,
surface->height_from_buffer, 0);
break;
case WL_OUTPUT_TRANSFORM_270:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
weston_matrix_rotate_xy(matrix, 0, -1);
weston_matrix_translate(matrix,
0, surface->width_from_buffer, 0);
break;
}
weston_matrix_scale(matrix, vp->buffer.scale, vp->buffer.scale, 1);
}
/**
* Compute a + b > c while being safe to overflows.
*/
static bool
fixed_sum_gt(wl_fixed_t a, wl_fixed_t b, wl_fixed_t c)
{
return (int64_t)a + (int64_t)b > (int64_t)c;
}
static bool
weston_surface_is_pending_viewport_source_valid(
const struct weston_surface *surface)
{
const struct weston_surface_state *pend = &surface->pending;
const struct weston_buffer_viewport *vp = &pend->buffer_viewport;
int width_from_buffer = 0;
int height_from_buffer = 0;
wl_fixed_t w;
wl_fixed_t h;
/* If viewport source rect is not set, it is always ok. */
if (vp->buffer.src_width == wl_fixed_from_int(-1))
return true;
if (pend->newly_attached) {
if (pend->buffer) {
convert_size_by_transform_scale(&width_from_buffer,
&height_from_buffer,
pend->buffer->width,
pend->buffer->height,
vp->buffer.transform,
vp->buffer.scale);
} else {
/* No buffer: viewport is irrelevant. */
return true;
}
} else {
width_from_buffer = surface->width_from_buffer;
height_from_buffer = surface->height_from_buffer;
}
assert((width_from_buffer == 0) == (height_from_buffer == 0));
assert(width_from_buffer >= 0 && height_from_buffer >= 0);
/* No buffer: viewport is irrelevant. */
if (width_from_buffer == 0 || height_from_buffer == 0)
return true;
/* overflow checks for wl_fixed_from_int() */
if (width_from_buffer > wl_fixed_to_int(INT32_MAX))
return false;
if (height_from_buffer > wl_fixed_to_int(INT32_MAX))
return false;
w = wl_fixed_from_int(width_from_buffer);
h = wl_fixed_from_int(height_from_buffer);
if (fixed_sum_gt(vp->buffer.src_x, vp->buffer.src_width, w))
return false;
if (fixed_sum_gt(vp->buffer.src_y, vp->buffer.src_height, h))
return false;
return true;
}
static bool
fixed_is_integer(wl_fixed_t v)
{
return (v & 0xff) == 0;
}</