/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "window.h" #define MIN(x,y) (((x) < (y)) ? (x) : (y)) 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; 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; cairo_surface_t *cairo_surface; }; 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); wl_list_for_each(s, &nested->surface_list, link) { 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); } 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 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 = wl_resource_get_user_data(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 = wl_resource_get_user_data(resource); struct nested *nested = surface->nested; struct wl_buffer *buffer = wl_resource_get_user_data(buffer_resource); EGLint format, width, height; cairo_device_t *device; 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); 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, NULL); if (surface->image == EGL_NO_IMAGE_KHR) { fprintf(stderr, "failed to create img\n"); return; } query_buffer(nested->egl_display, buffer, EGL_WIDTH, &width); query_buffer(nested->egl_display, buffer, 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); 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 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); struct nested *nested = surface->nested; 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(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 = wl_resource_get_user_data(resource); 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); 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(®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 = wl_resource_get_user_data(resource); 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 = wl_resource_get_user_data(resource); 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; } pixman_region32_init(®ion->region); region->resource = wl_resource_create(client, &wl_region_interface, 1, id); wl_resource_set_implementation(region->resource, ®ion_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 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_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; } 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); 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; }