summaryrefslogtreecommitdiff
path: root/clients/nested.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/nested.c')
-rw-r--r--clients/nested.c678
1 files changed, 593 insertions, 85 deletions
diff --git a/clients/nested.c b/clients/nested.c
index 50852340..74767795 100644
--- a/clients/nested.c
+++ b/clients/nested.c
@@ -47,6 +47,18 @@
#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;
@@ -58,7 +70,8 @@ struct nested {
struct program *texture_program;
struct wl_list surface_list;
- struct wl_list frame_callback_list;
+
+ const struct nested_renderer *renderer;
};
struct nested_region {
@@ -66,52 +79,194 @@ struct nested_region {
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 wl_resource *buffer_resource;
struct nested *nested;
EGLImageKHR *image;
- GLuint texture;
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
-frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
{
- struct nested *nested = data;
- struct nested_frame_callback *nc, *next;
+ struct nested_buffer *buffer =
+ container_of(listener, struct nested_buffer, destroy_listener);
- if (callback)
- wl_callback_destroy(callback);
+ 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;
- wl_list_for_each_safe(nc, next, &nested->frame_callback_list, link) {
+ 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(&nested->frame_callback_list);
+ wl_list_init(&surface->frame_callback_list);
/* FIXME: toytoolkit need a pre-block handler where we can
* call this. */
- wl_display_flush_clients(nested->child_display);
+ wl_display_flush_clients(surface->nested->child_display);
}
-static const struct wl_callback_listener frame_listener = {
- frame_callback
-};
-
static void
redraw_handler(struct widget *widget, void *data)
{
@@ -119,8 +274,6 @@ redraw_handler(struct widget *widget, void *data)
cairo_surface_t *surface;
cairo_t *cr;
struct rectangle allocation;
- struct wl_callback *callback;
- struct nested_surface *s;
widget_get_allocation(nested->widget, &allocation);
@@ -136,34 +289,11 @@ redraw_handler(struct widget *widget, void *data)
cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
cairo_fill(cr);
- wl_list_for_each(s, &nested->surface_list, link) {
- display_acquire_window_surface(nested->display,
- nested->window, NULL);
-
- glBindTexture(GL_TEXTURE_2D, s->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, s->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);
- }
+ nested->renderer->render_clients(nested, cr);
cairo_destroy(cr);
cairo_surface_destroy(surface);
-
- callback = wl_surface_frame(window_get_wl_surface(nested->window));
- wl_callback_add_listener(callback, &frame_listener, nested);
}
static void
@@ -264,6 +394,22 @@ 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);
}
@@ -281,54 +427,66 @@ surface_attach(struct wl_client *client,
{
struct nested_surface *surface = wl_resource_get_user_data(resource);
struct nested *nested = surface->nested;
- EGLint format, width, height;
- cairo_device_t *device;
+ struct nested_buffer *buffer = NULL;
- if (surface->buffer_resource)
- wl_buffer_send_release(surface->buffer_resource);
+ if (buffer_resource) {
+ int format;
- surface->buffer_resource = buffer_resource;
- if (!query_buffer(nested->egl_display, (void *) buffer_resource,
- EGL_TEXTURE_FORMAT, &format)) {
- fprintf(stderr, "attaching non-egl wl_buffer\n");
- return;
+ 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);
- if (surface->cairo_surface)
- cairo_surface_destroy(surface->cairo_surface);
-
- switch (format) {
- case EGL_TEXTURE_RGB:
- case EGL_TEXTURE_RGBA:
- break;
- default:
- fprintf(stderr, "unhandled format: %x\n", format);
- return;
- }
surface->image = create_image(nested->egl_display, NULL,
- EGL_WAYLAND_BUFFER_WL, buffer_resource,
+ EGL_WAYLAND_BUFFER_WL, buffer->resource,
NULL);
if (surface->image == EGL_NO_IMAGE_KHR) {
fprintf(stderr, "failed to create img\n");
return;
}
- 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);
- surface->cairo_surface =
- cairo_gl_surface_create_for_texture(device,
- CAIRO_CONTENT_COLOR_ALPHA,
- surface->texture,
- width, height);
-
- window_schedule_redraw(nested->window);
+ nested->renderer->surface_attach(surface, buffer);
}
static void
@@ -336,6 +494,11 @@ 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
@@ -353,7 +516,6 @@ surface_frame(struct wl_client *client,
{
struct nested_frame_callback *callback;
struct nested_surface *surface = wl_resource_get_user_data(resource);
- struct nested *nested = surface->nested;
callback = malloc(sizeof *callback);
if (callback == NULL) {
@@ -366,7 +528,8 @@ surface_frame(struct wl_client *client,
wl_resource_set_implementation(callback->resource, NULL, callback,
destroy_frame_callback);
- wl_list_insert(nested->frame_callback_list.prev, &callback->link);
+ wl_list_insert(surface->pending.frame_callback_list.prev,
+ &callback->link);
}
static void
@@ -386,8 +549,41 @@ surface_set_input_region(struct wl_client *client,
}
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
@@ -409,6 +605,16 @@ static const struct wl_surface_interface surface_interface = {
};
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)
{
@@ -423,15 +629,17 @@ compositor_create_surface(struct wl_client *client,
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);
- glGenTextures(1, &surface->texture);
- glBindTexture(GL_TEXTURE_2D, 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);
+ nested->renderer->surface_init(surface);
display_release_window_surface(nested->display, nested->window);
@@ -531,10 +739,10 @@ 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);
- wl_list_init(&nested->frame_callback_list);
nested->child_display = wl_display_create();
loop = wl_display_get_event_loop(nested->child_display);
fd = wl_event_loop_get_fd(loop);
@@ -570,6 +778,29 @@ nested_init_compositor(struct nested *nested)
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;
}
@@ -583,7 +814,7 @@ nested_create(struct display *display)
return nested;
nested->window = window_create(display);
- nested->widget = frame_create(nested->window, nested);
+ nested->widget = window_frame_create(nested->window, nested);
window_set_title(nested->window, "Wayland Nested");
nested->display = display;
@@ -607,12 +838,289 @@ nested_destroy(struct nested *nested)
free(nested);
}
+/*** blit renderer ***/
+
+static void
+blit_surface_init(struct nested_surface *surface)
+{
+ struct nested_blit_surface *blit_surface =
+ zalloc(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 =
+ zalloc(sizeof *ss_surface);
+ struct rectangle allocation;
+ struct wl_region *region;
+
+ ss_surface->widget =
+ window_add_subsurface(nested->window,
+ nested,
+ SUBSURFACE_SYNCHRONIZED);
+
+ 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");