summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@bitplanet.net>2013-04-11 21:47:41 -0400
committerKristian Høgsberg <krh@bitplanet.net>2013-05-08 14:23:23 -0400
commit5ee1332c45c04ccc56a2d7fce9f8a5488be4c450 (patch)
treecfd227f7f7b32f5e245970a94c72aead405a5d18
parent9e885d4bec1e2512335c2e52a768ff0a02f2677c (diff)
nested compositor examplenested
-rw-r--r--clients/Makefile.am8
-rw-r--r--clients/gles2-helper.c351
-rw-r--r--clients/gles2-helper.h66
-rw-r--r--clients/nested-client.c170
-rw-r--r--clients/nested.c597
5 files changed, 1192 insertions, 0 deletions
diff --git a/clients/Makefile.am b/clients/Makefile.am
index 8c9bcd4d..a0b06496 100644
--- a/clients/Makefile.am
+++ b/clients/Makefile.am
@@ -60,6 +60,8 @@ clients_programs = \
dnd \
smoke \
resizor \
+ nested \
+ nested-client \
eventdemo \
clickdot \
transformed \
@@ -117,6 +119,12 @@ smoke_LDADD = libtoytoolkit.la
resizor_SOURCES = resizor.c
resizor_LDADD = libtoytoolkit.la
+nested_SOURCES = nested.c gles2-helper.c gles2-helper.h
+nested_LDADD = libtoytoolkit.la
+
+nested_client_SOURCES = nested-client.c gles2-helper.c gles2-helper.h
+nested_client_LDADD = $(SIMPLE_EGL_CLIENT_LIBS) -lm
+
eventdemo_SOURCES = eventdemo.c
eventdemo_LDADD = libtoytoolkit.la
diff --git a/clients/gles2-helper.c b/clients/gles2-helper.c
new file mode 100644
index 00000000..0f8a7f3f
--- /dev/null
+++ b/clients/gles2-helper.c
@@ -0,0 +1,351 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <wayland-client.h>
+#include <wayland-egl.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include "gles2-helper.h"
+
+struct gles2_context *
+gles2_context_create(struct wl_display *display,
+ EGLDisplay egl_display, EGLConfig egl_config)
+{
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ EGLint config_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ struct gles2_context *ctx;
+ EGLint major, minor, n;
+ EGLBoolean ret;
+
+ ctx = malloc(sizeof *ctx);
+ if (ctx == NULL)
+ return NULL;
+
+ memset(ctx, 0, sizeof *ctx);
+ if (egl_display == EGL_NO_DISPLAY) {
+ ctx->egl_display = eglGetDisplay(display);
+ if (ctx->egl_display == NULL)
+ return NULL;
+
+ ret = eglInitialize(ctx->egl_display, &major, &minor);
+ if (!ret)
+ return NULL;
+ ret = eglBindAPI(EGL_OPENGL_ES_API);
+ if (!ret)
+ return NULL;
+
+ ret = eglChooseConfig(ctx->egl_display, config_attribs,
+ &ctx->egl_config, 1, &n);
+ if (!ret || n != 1)
+ return NULL;
+
+ ctx->egl_context = eglCreateContext(ctx->egl_display,
+ ctx->egl_config,
+ EGL_NO_CONTEXT,
+ context_attribs);
+ if (!ctx->egl_context)
+ return NULL;
+ } else {
+ ctx->egl_display = egl_display;
+ ctx->egl_config = egl_config;
+ }
+
+
+ return ctx;
+}
+
+void
+gles2_context_destroy(struct gles2_context *ctx)
+{
+ eglMakeCurrent(ctx->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+
+ eglTerminate(ctx->egl_display);
+ eglReleaseThread();
+
+ wl_egl_window_destroy(ctx->native);
+}
+
+static const char vertex_shader_text[] =
+ "uniform mat4 rotation;\n"
+ "attribute vec4 pos;\n"
+ "attribute vec4 color;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_Position = rotation * pos;\n"
+ " v_color = color;\n"
+ "}\n";
+
+static const char color_fragment_shader_text[] =
+ "precision mediump float;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_FragColor = v_color;\n"
+ "}\n";
+
+static const char texture_fragment_shader_text[] =
+ "precision mediump float;\n"
+ "varying vec4 v_color;\n"
+ "uniform sampler2D tex;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = texture2D(tex, vec2(v_color.x, v_color.y))\n;"
+ "}\n";
+
+static GLuint
+create_shader(const char *source, GLenum shader_type)
+{
+ GLuint shader;
+ GLint status;
+
+ shader = glCreateShader(shader_type);
+ if (shader == 0)
+ return 0;
+
+ glShaderSource(shader, 1, (const char **) &source, NULL);
+ glCompileShader(shader);
+
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetShaderInfoLog(shader, 1000, &len, log);
+ fprintf(stderr, "Error: compiling %s: %*s\n",
+ shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
+ len, log);
+ return 0;
+ }
+
+ return shader;
+}
+
+#define POS 0
+#define COL 1
+
+static struct program *
+create_program(const char *vert, const char *frag)
+{
+ struct program *program;
+ GLint status;
+
+ program = malloc(sizeof *program);
+ program->vert = create_shader(vert, GL_VERTEX_SHADER);
+ program->frag = create_shader(frag, GL_FRAGMENT_SHADER);
+
+ program->program = glCreateProgram();
+ glAttachShader(program->program, program->frag);
+ glAttachShader(program->program, program->vert);
+ glBindAttribLocation(program->program, POS, "pos");
+ glBindAttribLocation(program->program, COL, "color");
+ glLinkProgram(program->program);
+
+ glGetProgramiv(program->program, GL_LINK_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetProgramInfoLog(program->program, 1000, &len, log);
+ fprintf(stderr, "Error: linking:\n%*s\n", len, log);
+ exit(1);
+ }
+
+ program->rotation =
+ glGetUniformLocation(program->program, "rotation");
+ program->texture =
+ glGetUniformLocation(program->program, "tex");
+
+ return program;
+}
+
+static void
+init_gl(struct gles2_context *ctx)
+{
+ ctx->color_program =
+ create_program(vertex_shader_text, color_fragment_shader_text);
+ ctx->texture_program =
+ create_program(vertex_shader_text,
+ texture_fragment_shader_text);
+}
+
+void
+gles2_context_set_surface(struct gles2_context *ctx,
+ struct wl_surface *surface, int width, int height)
+{
+ ctx->width = width;
+ ctx->height = height;
+
+ ctx->native = wl_egl_window_create(surface, width, height);
+
+ ctx->egl_surface =
+ eglCreateWindowSurface(ctx->egl_display,
+ ctx->egl_config,
+ ctx->native, NULL);
+
+ eglMakeCurrent(ctx->egl_display, ctx->egl_surface,
+ ctx->egl_surface, ctx->egl_context);
+
+ wl_egl_window_resize(ctx->native,
+ ctx->width, ctx->height, 0, 0);
+}
+
+void
+gles2_context_set_size(struct gles2_context *ctx, int width, int height)
+{
+ ctx->width = width;
+ ctx->height = height;
+}
+
+void
+gles2_context_clear(struct gles2_context *ctx)
+{
+ glClearColor(0.4, 0.4, 0.4, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void
+gles2_context_render_triangle(struct gles2_context *ctx, uint32_t time)
+{
+ static const GLfloat verts[3][2] = {
+ { -0.5, -0.5 },
+ { 0.5, -0.5 },
+ { 0, 0.5 }
+ };
+ static const GLfloat colors[3][3] = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 }
+ };
+ GLfloat angle;
+ GLfloat rotation[4][4] = {
+ { 1, 0, 0, 0 },
+ { 0, 1, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 }
+ };
+ static const int32_t speed_div = 5;
+ static uint32_t start_time = 0;
+
+ if (ctx->color_program == 0)
+ init_gl(ctx);
+
+ if (start_time == 0)
+ start_time = time;
+
+ angle = ((time - start_time) / speed_div) % 360 * M_PI / 180.0;
+ rotation[0][0] = cos(angle);
+ rotation[0][2] = sin(angle);
+ rotation[2][0] = -sin(angle);
+ rotation[2][2] = cos(angle);
+
+ glUseProgram(ctx->color_program->program);
+
+ glViewport(0, 0, ctx->width, ctx->height);
+
+ glUniformMatrix4fv(ctx->color_program->rotation, 1, GL_FALSE,
+ (GLfloat *) rotation);
+
+ glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors);
+ glEnableVertexAttribArray(POS);
+ glEnableVertexAttribArray(COL);
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glDisableVertexAttribArray(POS);
+ glDisableVertexAttribArray(COL);
+
+ glFlush();
+}
+
+void
+gles2_context_render_quad(struct gles2_context *ctx, GLuint texture)
+{
+ static const GLfloat verts[4][2] = {
+ { -0.5, -0.5 },
+ { -0.5, 0.5 },
+ { 0.5, 0.5 },
+ { 0.5, -0.5 }
+ };
+ static const GLfloat colors[4][3] = {
+ { 0, 0, 0 },
+ { 0, 1, 0 },
+ { 1, 1, 1 },
+ { 1, 0, 1 }
+ };
+ GLfloat rotation[4][4] = {
+ { 1, 0, 0, 0 },
+ { 0, 1, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 }
+ };
+
+ if (ctx->color_program == 0)
+ init_gl(ctx);
+
+ glViewport(50, 50, ctx->width, ctx->height);
+
+ glUseProgram(ctx->texture_program->program);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ glUniformMatrix4fv(ctx->texture_program->rotation, 1, GL_FALSE,
+ (GLfloat *) rotation);
+
+ glUniform1i(ctx->texture_program->texture, 0);
+
+ glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors);
+ glEnableVertexAttribArray(POS);
+ glEnableVertexAttribArray(COL);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glDisableVertexAttribArray(POS);
+ glDisableVertexAttribArray(COL);
+
+ glFlush();
+}
+
+void
+gles2_context_swap(struct gles2_context *ctx)
+{
+ eglSwapBuffers(ctx->egl_display, ctx->egl_surface);
+}
diff --git a/clients/gles2-helper.h b/clients/gles2-helper.h
new file mode 100644
index 00000000..12c732a5
--- /dev/null
+++ b/clients/gles2-helper.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef _GLES2_HELPER_H_
+#define _GLES2_HELPER_H_
+
+#include <wayland-client.h>
+#include <wayland-egl.h>
+
+struct program {
+ GLuint vert, frag, program;
+ GLuint rotation;
+ GLuint texture;
+};
+
+struct gles2_context {
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+ EGLConfig egl_config;
+ EGLSurface egl_surface;
+ struct wl_egl_window *native;
+ struct program *color_program;
+ struct program *texture_program;
+ GLuint texture;
+ int width, height;
+};
+
+struct gles2_context *
+gles2_context_create(struct wl_display *display,
+ EGLDisplay egl_display, EGLConfig egl_config);
+void
+gles2_context_destroy(struct gles2_context *ctx);
+void
+gles2_context_set_surface(struct gles2_context *ctx,
+ struct wl_surface *surface, int width, int height);
+void
+gles2_context_clear(struct gles2_context *ctx);
+void
+gles2_context_set_size(struct gles2_context *ctx, int width, int height);
+void
+gles2_context_render_triangle(struct gles2_context *ctx, uint32_t time);
+void
+gles2_context_render_quad(struct gles2_context *ctx, GLuint texture);
+void
+gles2_context_swap(struct gles2_context *ctx);
+
+#endif
diff --git a/clients/nested-client.c b/clients/nested-client.c
new file mode 100644
index 00000000..66455644
--- /dev/null
+++ b/clients/nested-client.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <wayland-egl.h>
+#include <wayland-cursor.h>
+
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+
+#include "gles2-helper.h"
+
+struct window;
+struct seat;
+
+struct nested_client {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+
+ struct gles2_context *ctx;
+
+ GLuint rotation_uniform;
+ GLuint pos;
+ GLuint col;
+
+ struct wl_surface *surface;
+ int width, height;
+};
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time);
+
+static const struct wl_callback_listener frame_listener = {
+ frame_callback
+};
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct nested_client *client = data;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ callback = wl_surface_frame(client->surface);
+ wl_callback_add_listener(callback, &frame_listener, client);
+
+ gles2_context_clear(client->ctx);
+
+ gles2_context_render_triangle(client->ctx, time);
+
+ gles2_context_swap(client->ctx);
+}
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ struct nested_client *client = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ client->compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static struct nested_client *
+nested_client_create(void)
+{
+ struct nested_client *client;
+
+ client = malloc(sizeof *client);
+ if (client == NULL)
+ return NULL;
+
+ client->width = 250;
+ client->height = 250;
+
+ client->display = wl_display_connect(NULL);
+
+ client->registry = wl_display_get_registry(client->display);
+ wl_registry_add_listener(client->registry,
+ &registry_listener, client);
+
+ /* get globals */
+ wl_display_roundtrip(client->display);
+
+ client->ctx = gles2_context_create(client->display, NULL, NULL);
+
+ client->surface = wl_compositor_create_surface(client->compositor);
+
+ gles2_context_set_surface(client->ctx, client->surface,
+ client->width, client->height);
+
+ frame_callback(client, NULL, 0);
+
+ return client;
+}
+
+static void
+nested_client_destroy(struct nested_client *client)
+{
+ gles2_context_destroy(client->ctx);
+
+ wl_surface_destroy(client->surface);
+
+ if (client->compositor)
+ wl_compositor_destroy(client->compositor);
+
+ wl_registry_destroy(client->registry);
+ wl_display_flush(client->display);
+ wl_display_disconnect(client->display);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct nested_client *client;
+ int ret = 0;
+
+ if (getenv("WAYLAND_SOCKET") == NULL) {
+ fprintf(stderr,
+ "must be run by nested, don't run standalone\n");
+ return -1;
+ }
+
+ client = nested_client_create();
+
+ while (ret != -1)
+ ret = wl_display_dispatch(client->display);
+
+ nested_client_destroy(client);
+
+ return 0;
+}
diff --git a/clients/nested.c b/clients/nested.c
new file mode 100644
index 00000000..a38cfede
--- /dev/null
+++ b/clients/nested.c
@@ -0,0 +1,597 @@
+/*
+ * 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 <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 <wayland-client.h>
+#include <wayland-server.h>
+
+#include "window.h"
+#include "gles2-helper.h"
+
+struct nested {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+
+ struct wl_display *child_display;
+ struct task child_task;
+
+ EGLDisplay egl_display;
+
+ struct gles2_context *ctx;
+
+ struct wl_list surface_list;
+ struct wl_list frame_callback_list;
+};
+
+struct nested_region {
+ struct wl_resource resource;
+ pixman_region32_t region;
+};
+
+struct nested_surface {
+ struct wl_resource resource;
+ struct wl_resource *buffer_resource;
+ struct nested *nested;
+ EGLImageKHR *image;
+ GLuint texture;
+ struct wl_list link;
+};
+
+struct nested_frame_callback {
+ struct wl_resource resource;
+ struct wl_list link;
+};
+
+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 void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct nested *nested = data;
+ struct nested_frame_callback *nc, *next;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ wl_list_for_each_safe(nc, next, &nested->frame_callback_list, link) {
+ wl_callback_send_done(&nc->resource, time);
+ wl_resource_destroy(&nc->resource);
+ }
+ wl_list_init(&nested->frame_callback_list);
+
+ /* FIXME: toytoolkit need a pre-block handler where we can
+ * call this. */
+ wl_display_flush_clients(nested->child_display);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ frame_callback
+};
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct nested *nested = 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);
+
+ 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);
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+
+ display_acquire_window_surface(nested->display,
+ nested->window, NULL);
+
+ gles2_context_set_size(nested->ctx,
+ allocation.width, allocation.height);
+
+ wl_list_for_each(s, &nested->surface_list, link)
+ gles2_context_render_quad(nested->ctx, s->texture);
+
+ display_release_window_surface(nested->display, nested->window);
+
+ callback = wl_surface_frame(window_get_wl_surface(nested->window));
+ wl_callback_add_listener(callback, &frame_listener, nested);
+}
+
+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);
+ return NULL;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ close(sv[0]);
+ close(sv[1]);
+ 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]);
+ 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 =
+ container_of(resource, struct nested_surface, resource);
+
+ 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 = resource->data;
+ struct nested *nested = surface->nested;
+ struct wl_buffer *buffer = buffer_resource->data;
+ EGLint format;
+
+ if (surface->buffer_resource)
+ wl_buffer_send_release(surface->buffer_resource);
+
+ surface->buffer_resource = buffer_resource;
+ if (!query_buffer(nested->egl_display, buffer,
+ EGL_TEXTURE_FORMAT, &format)) {
+ fprintf(stderr, "attaching non-egl wl_buffer\n");
+ return;
+ }
+
+ if (surface->image != EGL_NO_IMAGE_KHR)
+ destroy_image(nested->egl_display, surface->image);
+
+ 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, NULL);
+ if (surface->image == EGL_NO_IMAGE_KHR) {
+ fprintf(stderr, "failed to create img\n");
+ return;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, surface->texture);
+ image_target_texture_2d(GL_TEXTURE_2D, surface->image);
+
+ window_schedule_redraw(nested->window);
+}
+
+static void
+surface_damage(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+}
+
+static void
+surface_frame(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct nested_frame_callback *callback;
+ struct nested_surface *surface = resource->data;
+ struct nested *nested = surface->nested;
+
+ callback = malloc(sizeof *callback);
+ if (callback == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_resource_init(&callback->resource, &wl_callback_interface,
+ NULL, id, callback);
+
+ wl_client_add_resource(client, &callback->resource);
+ wl_list_insert(nested->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
+surface_commit(struct wl_client *client, struct wl_resource *resource)
+{
+}
+
+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
+compositor_create_surface(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct nested *nested = resource->data;
+ struct nested_surface *surface;
+
+ surface = malloc(sizeof *surface);
+ if (surface == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ memset(surface, 0, sizeof *surface);
+ surface->nested = nested;
+
+ 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);
+
+ display_release_window_surface(nested->display, nested->window);
+
+ wl_resource_init(&surface->resource, &wl_surface_interface,
+ &surface_interface, id, surface);
+ surface->resource.destroy = destroy_surface;
+
+ wl_client_add_resource(client, &surface->resource);
+
+ wl_list_insert(nested->surface_list.prev, &surface->link);
+}
+
+static void
+destroy_region(struct wl_resource *resource)
+{
+ struct nested_region *region =
+ container_of(resource, struct nested_region, 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 = resource->data;
+
+ 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 = resource->data;
+ 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;
+ }
+
+ wl_resource_init(&region->resource, &wl_region_interface,
+ &region_interface, id, region);
+ region->resource.destroy = destroy_region;
+
+ pixman_region32_init(&region->region);
+
+ wl_client_add_resource(client, &region->resource);
+}
+
+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;
+
+ wl_client_add_object(client, &wl_compositor_interface,
+ &compositor_interface, id, nested);
+}
+
+static int
+nested_init_compositor(struct nested *nested)
+{
+ const char *extensions;
+ struct wl_event_loop *loop;
+ 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);
+ nested->child_task.run = handle_child_data;
+ display_watch_fd(nested->display, fd,
+ EPOLLIN, &nested->child_task);
+
+ if (!wl_display_add_global(nested->child_display,
+ &wl_compositor_interface,
+ 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;
+ }
+
+ return 0;
+}
+
+static struct nested *
+nested_create(struct display *display)
+{
+ struct nested *nested;
+
+ nested = malloc(sizeof *nested);
+ if (nested == NULL)
+ return nested;
+ memset(nested, 0, sizeof *nested);
+
+ nested->window = window_create(display);
+ nested->widget = 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);
+
+ nested->ctx =
+ gles2_context_create(display_get_display(display),
+ display_get_egl_display(display),
+ display_get_argb_egl_config(display));
+
+ 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);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct nested *nested;
+
+ 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, "nested-client");
+
+ display_run(display);
+
+ nested_destroy(nested);
+ display_destroy(display);
+
+ return 0;
+}