diff options
-rw-r--r-- | clients/Makefile.am | 8 | ||||
-rw-r--r-- | clients/gles2-helper.c | 351 | ||||
-rw-r--r-- | clients/gles2-helper.h | 66 | ||||
-rw-r--r-- | clients/nested-client.c | 170 | ||||
-rw-r--r-- | clients/nested.c | 597 |
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, + ®istry_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(®ion->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(®ion->region, ®ion->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(®ion->region, ®ion->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(®ion->resource, &wl_region_interface, + ®ion_interface, id, region); + region->resource.destroy = destroy_region; + + pixman_region32_init(®ion->region); + + wl_client_add_resource(client, ®ion->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; +} |