weston/clients/nested.c
Andrew Wedgbury 9cd661e746 Make sure config.h is included before any system headers
There was an issue recently in screen-share.c where config.h was not
being included, resulting in the wrong definition for off_t being used on
32 bit systems. I checked and I don't think this problem is happening
elsewhere, but to help avoid this sort of problem in the future, I went
through and made sure that config.h is included first whenever system
headers are included.

The config.h header should be included before any system headers, failing
to do this can result in the wrong type sizes being defined on certain
systems, e.g. off_t from sys/types.h

Signed-off-by: Andrew Wedgbury <andrew.wedgbury@realvnc.com>
2014-04-07 10:22:28 -07:00

1145 lines
29 KiB
C

/*
* Copyright © 2013 Intel Corporation
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <pixman.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <cairo-gl.h>
#include <wayland-client.h>
#define WL_HIDE_DEPRECATED
#include <wayland-server.h>
#include "window.h"
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
#ifndef EGL_WL_create_wayland_buffer_from_image
#define EGL_WL_create_wayland_buffer_from_image 1
#ifdef EGL_EGLEXT_PROTOTYPES
EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image);
#endif
typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLImageKHR image);
#endif
static int option_blit;
struct nested {
struct display *display;
struct window *window;
struct widget *widget;
struct wl_display *child_display;
struct task child_task;
EGLDisplay egl_display;
struct program *texture_program;
struct wl_list surface_list;
const struct nested_renderer *renderer;
};
struct nested_region {
struct wl_resource *resource;
pixman_region32_t region;
};
struct nested_buffer_reference {
struct nested_buffer *buffer;
struct wl_listener destroy_listener;
};
struct nested_buffer {
struct wl_resource *resource;
struct wl_signal destroy_signal;
struct wl_listener destroy_listener;
uint32_t busy_count;
/* A buffer in the parent compositor representing the same
* data. This is created on-demand when the subsurface
* renderer is used */
struct wl_buffer *parent_buffer;
/* This reference is used to mark when the parent buffer has
* been attached to the subsurface. It will be unrefenced when
* we receive a buffer release event. That way we won't inform
* the client that the buffer is free until the parent
* compositor is also finished with it */
struct nested_buffer_reference parent_ref;
};
struct nested_surface {
struct wl_resource *resource;
struct nested *nested;
EGLImageKHR *image;
struct wl_list link;
struct wl_list frame_callback_list;
struct {
/* wl_surface.attach */
int newly_attached;
struct nested_buffer *buffer;
struct wl_listener buffer_destroy_listener;
/* wl_surface.frame */
struct wl_list frame_callback_list;
/* wl_surface.damage */
pixman_region32_t damage;
} pending;
void *renderer_data;
};
/* Data used for the blit renderer */
struct nested_blit_surface {
struct nested_buffer_reference buffer_ref;
GLuint texture;
cairo_surface_t *cairo_surface;
};
/* Data used for the subsurface renderer */
struct nested_ss_surface {
struct widget *widget;
struct wl_surface *surface;
struct wl_subsurface *subsurface;
struct wl_callback *frame_callback;
};
struct nested_frame_callback {
struct wl_resource *resource;
struct wl_list link;
};
struct nested_renderer {
void (* surface_init)(struct nested_surface *surface);
void (* surface_fini)(struct nested_surface *surface);
void (* render_clients)(struct nested *nested, cairo_t *cr);
void (* surface_attach)(struct nested_surface *surface,
struct nested_buffer *buffer);
};
static const struct weston_option nested_options[] = {
{ WESTON_OPTION_BOOLEAN, "blit", 'b', &option_blit },
};
static const struct nested_renderer nested_blit_renderer;
static const struct nested_renderer nested_ss_renderer;
static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
static PFNEGLCREATEIMAGEKHRPROC create_image;
static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image;
static void
nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
{
struct nested_buffer *buffer =
container_of(listener, struct nested_buffer, destroy_listener);
wl_signal_emit(&buffer->destroy_signal, buffer);
if (buffer->parent_buffer)
wl_buffer_destroy(buffer->parent_buffer);
free(buffer);
}
static struct nested_buffer *
nested_buffer_from_resource(struct wl_resource *resource)
{
struct nested_buffer *buffer;
struct wl_listener *listener;
listener =
wl_resource_get_destroy_listener(resource,
nested_buffer_destroy_handler);
if (listener)
return container_of(listener, struct nested_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 = nested_buffer_destroy_handler;
wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
return buffer;
}
static void
nested_buffer_reference_handle_destroy(struct wl_listener *listener,
void *data)
{
struct nested_buffer_reference *ref =
container_of(listener, struct nested_buffer_reference,
destroy_listener);
assert((struct nested_buffer *)data == ref->buffer);
ref->buffer = NULL;
}
static void
nested_buffer_reference(struct nested_buffer_reference *ref,
struct nested_buffer *buffer)
{
if (buffer == ref->buffer)
return;
if (ref->buffer) {
ref->buffer->busy_count--;
if (ref->buffer->busy_count == 0) {
assert(wl_resource_get_client(ref->buffer->resource));
wl_resource_queue_event(ref->buffer->resource,
WL_BUFFER_RELEASE);
}
wl_list_remove(&ref->destroy_listener.link);
}
if (buffer) {
buffer->busy_count++;
wl_signal_add(&buffer->destroy_signal,
&ref->destroy_listener);
ref->destroy_listener.notify =
nested_buffer_reference_handle_destroy;
}
ref->buffer = buffer;
}
static void
flush_surface_frame_callback_list(struct nested_surface *surface,
uint32_t time)
{
struct nested_frame_callback *nc, *next;
wl_list_for_each_safe(nc, next, &surface->frame_callback_list, link) {
wl_callback_send_done(nc->resource, time);
wl_resource_destroy(nc->resource);
}
wl_list_init(&surface->frame_callback_list);
/* FIXME: toytoolkit need a pre-block handler where we can
* call this. */
wl_display_flush_clients(surface->nested->child_display);
}
static void
redraw_handler(struct widget *widget, void *data)
{
struct nested *nested = data;
cairo_surface_t *surface;
cairo_t *cr;
struct rectangle allocation;
widget_get_allocation(nested->widget, &allocation);
surface = window_get_surface(nested->window);
cr = cairo_create(surface);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_rectangle(cr,
allocation.x,
allocation.y,
allocation.width,
allocation.height);
cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
cairo_fill(cr);
nested->renderer->render_clients(nested, cr);
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
static void
keyboard_focus_handler(struct window *window,
struct input *device, void *data)
{
struct nested *nested = data;
window_schedule_redraw(nested->window);
}
static void
handle_child_data(struct task *task, uint32_t events)
{
struct nested *nested = container_of(task, struct nested, child_task);
struct wl_event_loop *loop;
loop = wl_display_get_event_loop(nested->child_display);
wl_event_loop_dispatch(loop, -1);
wl_display_flush_clients(nested->child_display);
}
struct nested_client {
struct wl_client *client;
pid_t pid;
};
static struct nested_client *
launch_client(struct nested *nested, const char *path)
{
int sv[2];
pid_t pid;
struct nested_client *client;
client = malloc(sizeof *client);
if (client == NULL)
return NULL;
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
fprintf(stderr, "launch_client: "
"socketpair failed while launching '%s': %m\n",
path);
free(client);
return NULL;
}
pid = fork();
if (pid == -1) {
close(sv[0]);
close(sv[1]);
free(client);
fprintf(stderr, "launch_client: "
"fork failed while launching '%s': %m\n", path);
return NULL;
}
if (pid == 0) {
int clientfd;
char s[32];
/* SOCK_CLOEXEC closes both ends, so we dup the fd to
* get a non-CLOEXEC fd to pass through exec. */
clientfd = dup(sv[1]);
if (clientfd == -1) {
fprintf(stderr, "compositor: dup failed: %m\n");
exit(-1);
}
snprintf(s, sizeof s, "%d", clientfd);
setenv("WAYLAND_SOCKET", s, 1);
execl(path, path, NULL);
fprintf(stderr, "compositor: executing '%s' failed: %m\n",
path);
exit(-1);
}
close(sv[1]);
client->client = wl_client_create(nested->child_display, sv[0]);
if (!client->client) {
close(sv[0]);
free(client);
fprintf(stderr, "launch_client: "
"wl_client_create failed while launching '%s'.\n",
path);
return NULL;
}
client->pid = pid;
return client;
}
static void
destroy_surface(struct wl_resource *resource)
{
struct nested_surface *surface = wl_resource_get_user_data(resource);
struct nested *nested = surface->nested;
struct nested_frame_callback *cb, *next;
wl_list_for_each_safe(cb, next,
&surface->frame_callback_list, link)
wl_resource_destroy(cb->resource);
wl_list_for_each_safe(cb, next,
&surface->pending.frame_callback_list, link)
wl_resource_destroy(cb->resource);
pixman_region32_fini(&surface->pending.damage);
nested->renderer->surface_fini(surface);
wl_list_remove(&surface->link);
free(surface);
}
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 nested_surface *surface = wl_resource_get_user_data(resource);
struct nested *nested = surface->nested;
struct nested_buffer *buffer = NULL;
if (buffer_resource) {
int format;
if (!query_buffer(nested->egl_display, (void *) buffer_resource,
EGL_TEXTURE_FORMAT, &format)) {
wl_resource_post_error(buffer_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"attaching non-egl wl_buffer");
return;
}
switch (format) {
case EGL_TEXTURE_RGB:
case EGL_TEXTURE_RGBA:
break;
default:
wl_resource_post_error(buffer_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"invalid format");
return;
}
buffer = nested_buffer_from_resource(buffer_resource);
if (buffer == NULL) {
wl_client_post_no_memory(client);
return;
}
}
if (surface->pending.buffer)
wl_list_remove(&surface->pending.buffer_destroy_listener.link);
surface->pending.buffer = buffer;
surface->pending.newly_attached = 1;
if (buffer) {
wl_signal_add(&buffer->destroy_signal,
&surface->pending.buffer_destroy_listener);
}
}
static void
nested_surface_attach(struct nested_surface *surface,
struct nested_buffer *buffer)
{
struct nested *nested = surface->nested;
if (surface->image != EGL_NO_IMAGE_KHR)
destroy_image(nested->egl_display, surface->image);
surface->image = create_image(nested->egl_display, NULL,
EGL_WAYLAND_BUFFER_WL, buffer->resource,
NULL);
if (surface->image == EGL_NO_IMAGE_KHR) {
fprintf(stderr, "failed to create img\n");
return;
}
nested->renderer->surface_attach(surface, buffer);
}
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 nested_surface *surface = wl_resource_get_user_data(resource);
pixman_region32_union_rect(&surface->pending.damage,
&surface->pending.damage,
x, y, width, height);
}
static void
destroy_frame_callback(struct wl_resource *resource)
{
struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
wl_list_remove(&callback->link);
free(callback);
}
static void
surface_frame(struct wl_client *client,
struct wl_resource *resource, uint32_t id)
{
struct nested_frame_callback *callback;
struct nested_surface *surface = wl_resource_get_user_data(resource);
callback = malloc(sizeof *callback);
if (callback == NULL) {
wl_resource_post_no_memory(resource);
return;
}
callback->resource = wl_resource_create(client,
&wl_callback_interface, 1, id);
wl_resource_set_implementation(callback->resource, NULL, callback,
destroy_frame_callback);
wl_list_insert(surface->pending.frame_callback_list.prev,
&callback->link);
}
static void
surface_set_opaque_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource)
{
fprintf(stderr, "surface_set_opaque_region\n");
}
static void
surface_set_input_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource)
{
fprintf(stderr, "surface_set_input_region\n");
}
static void
empty_region(pixman_region32_t *region)
{
pixman_region32_fini(region);
pixman_region32_init(region);
}
static void
surface_commit(struct wl_client *client, struct wl_resource *resource)
{
struct nested_surface *surface = wl_resource_get_user_data(resource);
struct nested *nested = surface->nested;
/* wl_surface.attach */
if (surface->pending.newly_attached)
nested_surface_attach(surface, surface->pending.buffer);
if (surface->pending.buffer) {
wl_list_remove(&surface->pending.buffer_destroy_listener.link);
surface->pending.buffer = NULL;
}
surface->pending.newly_attached = 0;
/* wl_surface.damage */
empty_region(&surface->pending.damage);
/* wl_surface.frame */
wl_list_insert_list(&surface->frame_callback_list,
&surface->pending.frame_callback_list);
wl_list_init(&surface->pending.frame_callback_list);
/* FIXME: For the subsurface renderer we don't need to
* actually redraw the window. However we do want to cause a
* commit because the subsurface is synchronized. Ideally we
* would just queue the commit */
window_schedule_redraw(nested->window);
}
static void
surface_set_buffer_transform(struct wl_client *client,
struct wl_resource *resource, int transform)
{
fprintf(stderr, "surface_set_buffer_transform\n");
}
static const struct wl_surface_interface surface_interface = {
surface_destroy,
surface_attach,
surface_damage,
surface_frame,
surface_set_opaque_region,
surface_set_input_region,
surface_commit,
surface_set_buffer_transform
};
static void
surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
{
struct nested_surface *surface =
container_of(listener, struct nested_surface,
pending.buffer_destroy_listener);
surface->pending.buffer = NULL;
}
static void
compositor_create_surface(struct wl_client *client,
struct wl_resource *resource, uint32_t id)
{
struct nested *nested = wl_resource_get_user_data(resource);
struct nested_surface *surface;
surface = zalloc(sizeof *surface);
if (surface == NULL) {
wl_resource_post_no_memory(resource);
return;
}
surface->nested = nested;
wl_list_init(&surface->frame_callback_list);
wl_list_init(&surface->pending.frame_callback_list);
surface->pending.buffer_destroy_listener.notify =
surface_handle_pending_buffer_destroy;
pixman_region32_init(&surface->pending.damage);
display_acquire_window_surface(nested->display,
nested->window, NULL);
nested->renderer->surface_init(surface);
display_release_window_surface(nested->display, nested->window);
surface->resource =
wl_resource_create(client, &wl_surface_interface, 1, id);
wl_resource_set_implementation(surface->resource,
&surface_interface, surface,
destroy_surface);
wl_list_insert(nested->surface_list.prev, &surface->link);
}
static void
destroy_region(struct wl_resource *resource)
{
struct nested_region *region = wl_resource_get_user_data(resource);
pixman_region32_fini(&region->region);
free(region);
}
static void
region_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void
region_add(struct wl_client *client, struct wl_resource *resource,
int32_t x, int32_t y, int32_t width, int32_t height)
{
struct nested_region *region = wl_resource_get_user_data(resource);
pixman_region32_union_rect(&region->region, &region->region,
x, y, width, height);
}
static void
region_subtract(struct wl_client *client, struct wl_resource *resource,
int32_t x, int32_t y, int32_t width, int32_t height)
{
struct nested_region *region = wl_resource_get_user_data(resource);
pixman_region32_t rect;
pixman_region32_init_rect(&rect, x, y, width, height);
pixman_region32_subtract(&region->region, &region->region, &rect);
pixman_region32_fini(&rect);
}
static const struct wl_region_interface region_interface = {
region_destroy,
region_add,
region_subtract
};
static void
compositor_create_region(struct wl_client *client,
struct wl_resource *resource, uint32_t id)
{
struct nested_region *region;
region = malloc(sizeof *region);
if (region == NULL) {
wl_resource_post_no_memory(resource);
return;
}
pixman_region32_init(&region->region);
region->resource =
wl_resource_create(client, &wl_region_interface, 1, id);
wl_resource_set_implementation(region->resource, &region_interface,
region, destroy_region);
}
static const struct wl_compositor_interface compositor_interface = {
compositor_create_surface,
compositor_create_region
};
static void
compositor_bind(struct wl_client *client,
void *data, uint32_t version, uint32_t id)
{
struct nested *nested = data;
struct wl_resource *resource;
resource = wl_resource_create(client, &wl_compositor_interface,
MIN(version, 3), id);
wl_resource_set_implementation(resource, &compositor_interface,
nested, NULL);
}
static int
nested_init_compositor(struct nested *nested)
{
const char *extensions;
struct wl_event_loop *loop;
int use_ss_renderer = 0;
int fd, ret;
wl_list_init(&nested->surface_list);
nested->child_display = wl_display_create();
loop = wl_display_get_event_loop(nested->child_display);
fd = wl_event_loop_get_fd(loop);
nested->child_task.run = handle_child_data;
display_watch_fd(nested->display, fd,
EPOLLIN, &nested->child_task);
if (!wl_global_create(nested->child_display,
&wl_compositor_interface, 1,
nested, compositor_bind))
return -1;
wl_display_init_shm(nested->child_display);
nested->egl_display = display_get_egl_display(nested->display);
extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
return -1;
}
bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
image_target_texture_2d =
(void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
ret = bind_display(nested->egl_display, nested->child_display);
if (!ret) {
fprintf(stderr, "failed to bind wl_display\n");
return -1;
}
if (display_has_subcompositor(nested->display)) {
const char *func = "eglCreateWaylandBufferFromImageWL";
const char *ext = "EGL_WL_create_wayland_buffer_from_image";
if (strstr(extensions, ext)) {
create_wayland_buffer_from_image =
(void *) eglGetProcAddress(func);
use_ss_renderer = 1;
}
}
if (option_blit)
use_ss_renderer = 0;
if (use_ss_renderer) {
printf("Using subsurfaces to render client surfaces\n");
nested->renderer = &nested_ss_renderer;
} else {
printf("Using local compositing with blits to "
"render client surfaces\n");
nested->renderer = &nested_blit_renderer;
}
return 0;
}
static struct nested *
nested_create(struct display *display)
{
struct nested *nested;
nested = zalloc(sizeof *nested);
if (nested == NULL)
return nested;
nested->window = window_create(display);
nested->widget = window_frame_create(nested->window, nested);
window_set_title(nested->window, "Wayland Nested");
nested->display = display;
window_set_user_data(nested->window, nested);
widget_set_redraw_handler(nested->widget, redraw_handler);
window_set_keyboard_focus_handler(nested->window,
keyboard_focus_handler);
nested_init_compositor(nested);
widget_schedule_resize(nested->widget, 400, 400);
return nested;
}
static void
nested_destroy(struct nested *nested)
{
widget_destroy(nested->widget);
window_destroy(nested->window);
free(nested);
}
/*** blit renderer ***/
static void
blit_surface_init(struct nested_surface *surface)
{
struct nested_blit_surface *blit_surface =
xzalloc(sizeof *blit_surface);
glGenTextures(1, &blit_surface->texture);
glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
surface->renderer_data = blit_surface;
}
static void
blit_surface_fini(struct nested_surface *surface)
{
struct nested_blit_surface *blit_surface = surface->renderer_data;
nested_buffer_reference(&blit_surface->buffer_ref, NULL);
glDeleteTextures(1, &blit_surface->texture);
free(blit_surface);
}
static void
blit_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct nested *nested = data;
struct nested_surface *surface;
wl_list_for_each(surface, &nested->surface_list, link)
flush_surface_frame_callback_list(surface, time);
if (callback)
wl_callback_destroy(callback);
}
static const struct wl_callback_listener blit_frame_listener = {
blit_frame_callback
};
static void
blit_render_clients(struct nested *nested,
cairo_t *cr)
{
struct nested_surface *s;
struct rectangle allocation;
struct wl_callback *callback;
widget_get_allocation(nested->widget, &allocation);
wl_list_for_each(s, &nested->surface_list, link) {
struct nested_blit_surface *blit_surface = s->renderer_data;
display_acquire_window_surface(nested->display,
nested->window, NULL);
glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
image_target_texture_2d(GL_TEXTURE_2D, s->image);
display_release_window_surface(nested->display,
nested->window);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
cairo_set_source_surface(cr, blit_surface->cairo_surface,
allocation.x + 10,
allocation.y + 10);
cairo_rectangle(cr, allocation.x + 10,
allocation.y + 10,
allocation.width - 10,
allocation.height - 10);
cairo_fill(cr);
}
callback = wl_surface_frame(window_get_wl_surface(nested->window));
wl_callback_add_listener(callback, &blit_frame_listener, nested);
}
static void
blit_surface_attach(struct nested_surface *surface,
struct nested_buffer *buffer)
{
struct nested *nested = surface->nested;
struct nested_blit_surface *blit_surface = surface->renderer_data;
EGLint width, height;
cairo_device_t *device;
nested_buffer_reference(&blit_surface->buffer_ref, buffer);
if (blit_surface->cairo_surface)
cairo_surface_destroy(blit_surface->cairo_surface);
query_buffer(nested->egl_display, (void *) buffer->resource,
EGL_WIDTH, &width);
query_buffer(nested->egl_display, (void *) buffer->resource,
EGL_HEIGHT, &height);
device = display_get_cairo_device(nested->display);
blit_surface->cairo_surface =
cairo_gl_surface_create_for_texture(device,
CAIRO_CONTENT_COLOR_ALPHA,
blit_surface->texture,
width, height);
}
static const struct nested_renderer
nested_blit_renderer = {
.surface_init = blit_surface_init,
.surface_fini = blit_surface_fini,
.render_clients = blit_render_clients,
.surface_attach = blit_surface_attach
};
/*** subsurface renderer ***/
static void
ss_surface_init(struct nested_surface *surface)
{
struct nested *nested = surface->nested;
struct wl_compositor *compositor =
display_get_compositor(nested->display);
struct nested_ss_surface *ss_surface =
xzalloc(sizeof *ss_surface);
struct rectangle allocation;
struct wl_region *region;
ss_surface->widget =
window_add_subsurface(nested->window,
nested,
SUBSURFACE_SYNCHRONIZED);
widget_set_use_cairo(ss_surface->widget, 0);
ss_surface->surface = widget_get_wl_surface(ss_surface->widget);
ss_surface->subsurface = widget_get_wl_subsurface(ss_surface->widget);
/* The toy toolkit gets confused about the pointer position
* when it gets motion events for a subsurface so we'll just
* disable input on it */
region = wl_compositor_create_region(compositor);
wl_surface_set_input_region(ss_surface->surface, region);
wl_region_destroy(region);
widget_get_allocation(nested->widget, &allocation);
wl_subsurface_set_position(ss_surface->subsurface,
allocation.x + 10,
allocation.y + 10);
surface->renderer_data = ss_surface;
}
static void
ss_surface_fini(struct nested_surface *surface)
{
struct nested_ss_surface *ss_surface = surface->renderer_data;
widget_destroy(ss_surface->widget);
if (ss_surface->frame_callback)
wl_callback_destroy(ss_surface->frame_callback);
free(ss_surface);
}
static void
ss_render_clients(struct nested *nested,
cairo_t *cr)
{
/* The clients are composited by the parent compositor so we
* don't need to do anything here */
}
static void
ss_buffer_release(void *data, struct wl_buffer *wl_buffer)
{
struct nested_buffer *buffer = data;
nested_buffer_reference(&buffer->parent_ref, NULL);
}
static struct wl_buffer_listener ss_buffer_listener = {
ss_buffer_release
};
static void
ss_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct nested_surface *surface = data;
struct nested_ss_surface *ss_surface = surface->renderer_data;
flush_surface_frame_callback_list(surface, time);
if (callback)
wl_callback_destroy(callback);
ss_surface->frame_callback = NULL;
}
static const struct wl_callback_listener ss_frame_listener = {
ss_frame_callback
};
static void
ss_surface_attach(struct nested_surface *surface,
struct nested_buffer *buffer)
{
struct nested *nested = surface->nested;
struct nested_ss_surface *ss_surface = surface->renderer_data;
struct wl_buffer *parent_buffer;
const pixman_box32_t *rects;
int n_rects, i;
if (buffer) {
/* Create a representation of the buffer in the parent
* compositor if we haven't already */
if (buffer->parent_buffer == NULL) {
EGLDisplay *edpy = nested->egl_display;
EGLImageKHR image = surface->image;
buffer->parent_buffer =
create_wayland_buffer_from_image(edpy, image);
wl_buffer_add_listener(buffer->parent_buffer,
&ss_buffer_listener,
buffer);
}
parent_buffer = buffer->parent_buffer;
/* We'll take a reference to the buffer while the parent
* compositor is using it so that we won't report the release
* event until the parent has also finished with it */
nested_buffer_reference(&buffer->parent_ref, buffer);
} else {
parent_buffer = NULL;
}
wl_surface_attach(ss_surface->surface, parent_buffer, 0, 0);
rects = pixman_region32_rectangles(&surface->pending.damage, &n_rects);
for (i = 0; i < n_rects; i++) {
const pixman_box32_t *rect = rects + i;
wl_surface_damage(ss_surface->surface,
rect->x1,
rect->y1,
rect->x2 - rect->x1,
rect->y2 - rect->y1);
}
if (ss_surface->frame_callback)
wl_callback_destroy(ss_surface->frame_callback);
ss_surface->frame_callback = wl_surface_frame(ss_surface->surface);
wl_callback_add_listener(ss_surface->frame_callback,
&ss_frame_listener,
surface);
wl_surface_commit(ss_surface->surface);
}
static const struct nested_renderer
nested_ss_renderer = {
.surface_init = ss_surface_init,
.surface_fini = ss_surface_fini,
.render_clients = ss_render_clients,
.surface_attach = ss_surface_attach
};
int
main(int argc, char *argv[])
{
struct display *display;
struct nested *nested;
parse_options(nested_options,
ARRAY_LENGTH(nested_options), &argc, argv);
display = display_create(&argc, argv);
if (display == NULL) {
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
nested = nested_create(display);
launch_client(nested, "weston-nested-client");
display_run(display);
nested_destroy(nested);
display_destroy(display);
return 0;
}