weston/clients/nested.c
2013-06-24 22:53:38 -04:00

615 lines
15 KiB
C

/*
* Copyright © 2013 Intel Corporation
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <pixman.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <cairo-gl.h>
#include <wayland-client.h>
#include <wayland-server.h>
#include "window.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 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 = resource->data;
struct nested *nested = surface->nested;
struct wl_buffer *buffer = buffer_resource->data;
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 = resource->data;
struct nested *nested = surface->nested;
callback = malloc(sizeof *callback);
if (callback == NULL) {
wl_resource_post_no_memory(resource);
return;
}
callback->resource =
wl_client_add_object(client, &wl_callback_interface,
NULL, id, callback);
wl_resource_set_destructor(callback->resource, 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 = 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);
surface->resource =
wl_client_add_object(client, &wl_surface_interface,
&surface_interface, id, surface);
wl_resource_set_destructor(surface->resource, destroy_surface);
wl_list_insert(nested->surface_list.prev, &surface->link);
}
static void
destroy_region(struct wl_resource *resource)
{
struct nested_region *region = wl_resource_get_user_data(resource);
pixman_region32_fini(&region->region);
free(region);
}
static void
region_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void
region_add(struct wl_client *client, struct wl_resource *resource,
int32_t x, int32_t y, int32_t width, int32_t height)
{
struct nested_region *region = 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;
}
pixman_region32_init(&region->region);
region->resource =
wl_client_add_object(client, &wl_region_interface,
&region_interface, id, region);
wl_resource_set_destructor(region->resource, 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;
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);
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;
}