compositor: Add OpenWF Display backend
diff --git a/compositor/Makefile.am b/compositor/Makefile.am
index efee1fe..9c62355 100644
--- a/compositor/Makefile.am
+++ b/compositor/Makefile.am
@@ -23,6 +23,12 @@
 wayland_compositor_sources = compositor-wayland.c
 endif
 
+if ENABLE_OPENWFD_COMPOSITOR
+openwfd_compositor_sources = compositor-openwfd.c
+AM_CPPFLAGS += $(OPENWFD_CFLAGS)
+compositor_LDADD += $(OPENWFD_LIBS)
+endif
+
 compositor_SOURCES =				\
 	compositor.c				\
 	compositor.h				\
@@ -33,6 +39,7 @@
 	screenshooter-server-protocol.h		\
 	shm.c					\
 	$(drm_compositor_sources)		\
+	$(openwfd_compositor_sources)		\
 	$(x11_compositor_sources)		\
 	$(wayland_compositor_sources)
 
diff --git a/compositor/compositor-openwfd.c b/compositor/compositor-openwfd.c
new file mode 100644
index 0000000..952c023
--- /dev/null
+++ b/compositor/compositor-openwfd.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/time.h>
+
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <WF/wfd.h>
+#include <WF/wfdext.h>
+
+#include "compositor.h"
+
+struct wfd_compositor {
+	struct wlsc_compositor base;
+
+	struct udev *udev;
+	WFDDevice dev;
+
+	WFDEvent event;
+	int wfd_fd;
+	struct wl_event_source *wfd_source;
+
+	struct tty *tty;
+
+	uint32_t start_time;
+	uint32_t used_pipelines;
+};
+
+struct wfd_output {
+	struct wlsc_output   base;
+
+	WFDPort port;
+
+	WFDPipeline pipeline;
+	WFDint pipeline_id;
+
+	WFDPortMode mode;
+	WFDSource source[2];
+
+	EGLImageKHR image[2];
+	GLuint rbo[2];
+	uint32_t current;
+};
+
+static int
+wfd_output_prepare_render(struct wlsc_output *output_base)
+{
+	struct wfd_output *output = (struct wfd_output *) output_base;
+
+	glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+				  GL_COLOR_ATTACHMENT0,
+				  GL_RENDERBUFFER,
+				  output->rbo[output->current]);
+
+	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+		return -1;
+
+	return 0;
+}
+
+static int
+wfd_output_present(struct wlsc_output *output_base)
+{
+	struct wfd_output *output = (struct wfd_output *) output_base;
+	struct wfd_compositor *c =
+		(struct wfd_compositor *) output->base.compositor;
+
+	if (wfd_output_prepare_render(&output->base))
+		return -1;
+	glFlush();
+
+	output->current ^= 1;
+
+	wfdBindSourceToPipeline(c->dev, output->pipeline,
+				output->source[output->current ^ 1],
+				WFD_TRANSITION_AT_VSYNC, NULL);
+
+	wfdDeviceCommit(c->dev, WFD_COMMIT_PIPELINE, output->pipeline);
+
+	return 0;
+}
+
+static int
+init_egl(struct wfd_compositor *ec)
+{
+	EGLint major, minor;
+	const char *extensions;
+	int fd;
+	static const EGLint context_attribs[] = {
+		EGL_CONTEXT_CLIENT_VERSION, 2,
+		EGL_NONE
+	};
+
+	fd = wfdDeviceGetDRMfd(ec->dev);
+	if (fd < 0)
+		return -1;
+
+	ec->wfd_fd = fd;
+	ec->base.display = eglGetDRMDisplayMESA(ec->wfd_fd);
+	if (ec->base.display == NULL) {
+		fprintf(stderr, "failed to create display\n");
+		return -1;
+	}
+
+	if (!eglInitialize(ec->base.display, &major, &minor)) {
+		fprintf(stderr, "failed to initialize display\n");
+		return -1;
+	}
+
+	extensions = eglQueryString(ec->base.display, EGL_EXTENSIONS);
+	if (!strstr(extensions, "EGL_KHR_surfaceless_opengl")) {
+		fprintf(stderr, "EGL_KHR_surfaceless_opengl not available\n");
+		return -1;
+	}
+
+	if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+		fprintf(stderr, "failed to bind api EGL_OPENGL_ES_API\n");
+		return -1;
+	}
+
+	ec->base.context = eglCreateContext(ec->base.display, NULL,
+					    EGL_NO_CONTEXT, context_attribs);
+	if (ec->base.context == NULL) {
+		fprintf(stderr, "failed to create context\n");
+		return -1;
+	}
+
+	if (!eglMakeCurrent(ec->base.display, EGL_NO_SURFACE,
+			    EGL_NO_SURFACE, ec->base.context)) {
+		fprintf(stderr, "failed to make context current\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+create_output_for_port(struct wfd_compositor *ec,
+		       WFDHandle port,
+		       int x, int y)
+{
+	struct wfd_output *output;
+	int i;
+	EGLint attribs[] = {
+		EGL_WIDTH,		0,
+		EGL_HEIGHT,		0,
+		EGL_DRM_BUFFER_FORMAT_MESA,	EGL_DRM_BUFFER_FORMAT_ARGB32_MESA,
+		EGL_DRM_BUFFER_USE_MESA,	EGL_DRM_BUFFER_USE_SCANOUT_MESA,
+		EGL_NONE
+	};
+	WFDint num_pipelines, *pipelines;
+	WFDint num_modes;
+	WFDint rect[4] = { 0, 0, 0, 0 };
+	int width, height;
+
+	output = malloc(sizeof *output);
+	if (output == NULL)
+		return -1;
+
+	memset(output, 0, sizeof *output);
+
+	output->port = port;
+
+	wfdSetPortAttribi(ec->dev, output->port,
+			  WFD_PORT_POWER_MODE, WFD_POWER_MODE_ON);
+
+	num_modes = wfdGetPortModes(ec->dev, output->port, &output->mode, 1);
+	if (num_modes != 1) {
+		fprintf(stderr, "failed to get port mode\n");
+		goto cleanup_port;
+	}
+
+	width = wfdGetPortModeAttribi(ec->dev, output->port, output->mode,
+				      WFD_PORT_MODE_WIDTH);
+	height = wfdGetPortModeAttribi(ec->dev, output->port, output->mode,
+				       WFD_PORT_MODE_HEIGHT);
+	
+	wfdSetPortMode(ec->dev, output->port, output->mode);
+
+	wfdEnumeratePipelines(ec->dev, NULL, 0, NULL);
+
+	num_pipelines = wfdGetPortAttribi(ec->dev, output->port,
+					  WFD_PORT_PIPELINE_ID_COUNT);
+	if (num_pipelines < 1) {
+		fprintf(stderr, "failed to get a bindable pipeline\n");
+		goto cleanup_port;
+	}
+	pipelines = calloc(num_pipelines, sizeof *pipelines);
+	if (pipelines == NULL)
+		goto cleanup_port;
+
+	wfdGetPortAttribiv(ec->dev, output->port,
+			   WFD_PORT_BINDABLE_PIPELINE_IDS,
+			   num_pipelines, pipelines);
+
+	output->pipeline_id = WFD_INVALID_PIPELINE_ID;
+	for (i = 0; i < num_pipelines; ++i) {
+		if (!(ec->used_pipelines & (1 << pipelines[i]))) {
+		    output->pipeline_id = pipelines[i];
+		    break;
+		}
+	}
+	if (output->pipeline_id == WFD_INVALID_PIPELINE_ID) {
+		fprintf(stderr, "no pipeline found for port: %d\n", port);
+		goto cleanup_pipelines;
+	}
+
+	ec->used_pipelines |= (1 << output->pipeline_id);
+
+	wlsc_output_init(&output->base, &ec->base, x, y,
+			 width, height, 0);
+
+	output->pipeline = wfdCreatePipeline(ec->dev, output->pipeline_id, NULL);
+	if (output->pipeline == WFD_INVALID_HANDLE) {
+		fprintf(stderr, "failed to create a pipeline\n");
+		goto cleanup_wlsc_output;
+	}
+
+	glGenRenderbuffers(2, output->rbo);
+	for (i = 0; i < 2; i++) {
+		glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]);
+
+		attribs[1] = output->base.width;
+		attribs[3] = output->base.height;
+		output->image[i] =
+			eglCreateDRMImageMESA(ec->base.display, attribs);
+
+		printf("output->image[i]: %p\n", output->image[i]);
+		glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER,
+						       output->image[i]);
+		int handle;
+		eglExportDRMImageMESA(ec->base.display, output->image[i],
+				      NULL, &handle, NULL);
+		printf("handle: %d\n", handle);
+		output->source[i] =
+			wfdCreateSourceFromImage(ec->dev, output->pipeline,
+						 output->image[i], NULL);
+
+		if (output->source[i] == WFD_INVALID_HANDLE) {
+			fprintf(stderr, "failed to create source\n");
+			goto cleanup_pipeline;
+		}
+	}
+
+	output->current = 0;
+	glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+				  GL_COLOR_ATTACHMENT0,
+				  GL_RENDERBUFFER,
+				  output->rbo[output->current]);
+
+	rect[2] = width;
+	rect[3] = height;
+	wfdSetPipelineAttribiv(ec->dev, output->pipeline,
+			       WFD_PIPELINE_SOURCE_RECTANGLE, 4, rect);
+	wfdSetPipelineAttribiv(ec->dev, output->pipeline,
+			       WFD_PIPELINE_DESTINATION_RECTANGLE, 4, rect);
+
+	wfdBindSourceToPipeline(ec->dev, output->pipeline,
+				output->source[output->current ^ 1],
+				WFD_TRANSITION_AT_VSYNC, NULL);
+
+	wfdBindPipelineToPort(ec->dev, output->port, output->pipeline);
+
+	wfdDeviceCommit(ec->dev, WFD_COMMIT_ENTIRE_DEVICE, WFD_INVALID_HANDLE);
+
+	output->base.prepare_render = wfd_output_prepare_render;
+	output->base.present = wfd_output_present;
+
+	wl_list_insert(ec->base.output_list.prev, &output->base.link);
+
+	return 0;
+
+cleanup_pipeline:
+	wfdDestroyPipeline(ec->dev, output->pipeline);
+cleanup_wlsc_output:
+	wlsc_output_destroy(&output->base);
+cleanup_pipelines:
+	free(pipelines);
+cleanup_port:
+	wfdDestroyPort(ec->dev, output->port);
+	free(output);
+
+	return -1;
+}
+
+static int
+create_outputs(struct wfd_compositor *ec, int option_connector)
+{
+	int x = 0, y = 0;
+	WFDint i, num, *ports;
+	WFDPort port = WFD_INVALID_HANDLE;
+
+	num = wfdEnumeratePorts(ec->dev, NULL, 0, NULL);
+	ports = calloc(num, sizeof *ports);
+	if (ports == NULL)
+		return -1;
+
+	num = wfdEnumeratePorts(ec->dev, ports, num, NULL);
+	if (num < 1)
+		return -1;
+
+	for (i = 0; i < num; ++i) {
+		port = wfdCreatePort(ec->dev, ports[i], NULL);
+		if (port == WFD_INVALID_HANDLE)
+			continue;
+
+		if (wfdGetPortAttribi(ec->dev, port, WFD_PORT_ATTACHED) &&
+		    (option_connector == 0 || ports[i] == option_connector)) {
+			create_output_for_port(ec, port, x, y);
+
+			x += container_of(ec->base.output_list.prev,
+					  struct wlsc_output,
+					  link)->width;
+		} else {
+			wfdDestroyPort(ec->dev, port);
+		}
+	}
+
+	free(ports);
+
+	return 0;
+}
+
+static int
+destroy_output(struct wfd_output *output)
+{
+	struct wfd_compositor *ec =
+		(struct wfd_compositor *) output->base.compositor;
+	int i;
+
+	glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+				  GL_COLOR_ATTACHMENT0,
+				  GL_RENDERBUFFER,
+				  0);
+
+	glBindRenderbuffer(GL_RENDERBUFFER, 0);
+	glDeleteRenderbuffers(2, output->rbo);
+
+	for (i = 0; i < 2; i++) {
+		eglDestroyImageKHR(ec->base.display, output->image[i]);
+		wfdDestroySource(ec->dev, output->source[i]);
+	}
+	
+	ec->used_pipelines &= ~(1 << output->pipeline_id);
+	wfdDestroyPipeline(ec->dev, output->pipeline);
+	wfdDestroyPort(ec->dev, output->port);
+
+	wlsc_output_destroy(&output->base);
+	wl_list_remove(&output->base.link);
+
+	free(output);
+
+	return 0;
+}
+
+static int
+handle_port_state_change(struct wfd_compositor *ec)
+{
+	struct wfd_output *output, *next;
+	WFDint output_port_id;
+	int x = 0, y = 0;
+	int x_offset = 0, y_offset = 0;
+	WFDPort port;
+	WFDint port_id;
+	WFDboolean state;
+
+	port_id = wfdGetEventAttribi(ec->dev, ec->event,
+				     WFD_EVENT_PORT_ATTACH_PORT_ID);
+	state = wfdGetEventAttribi(ec->dev, ec->event,
+				   WFD_EVENT_PORT_ATTACH_STATE);
+
+	if (state) {
+		struct wlsc_output *last_output =
+			container_of(ec->base.output_list.prev,
+				     struct wlsc_output, link);
+
+		/* XXX: not yet needed, we die with 0 outputs */
+		if (!wl_list_empty(&ec->base.output_list))
+			x = last_output->x + last_output->width;
+		else
+			x = 0;
+		y = 0;
+
+		port = wfdCreatePort(ec->dev, port_id, NULL);
+		if (port == WFD_INVALID_HANDLE)
+			return -1;
+
+		create_output_for_port(ec, port, x, y);
+
+		return 0;
+	}
+
+	wl_list_for_each_safe(output, next, &ec->base.output_list, base.link) {
+		output_port_id =
+			wfdGetPortAttribi(ec->dev, output->port, WFD_PORT_ID);
+
+		if (!state && output_port_id == port_id) {
+			x_offset += output->base.width;
+			destroy_output(output);
+			continue;
+		}
+
+		if (x_offset != 0 || y_offset != 0) {
+			wlsc_output_move(&output->base,
+					 output->base.x - x_offset,
+					 output->base.y - y_offset);
+		}
+	}
+
+	if (ec->used_pipelines == 0)
+		wl_display_terminate(ec->base.wl_display);
+
+	return 0;
+}
+
+static void
+on_wfd_event(int fd, uint32_t mask, void *data)
+{
+	struct wfd_compositor *c = data;
+	struct wfd_output *output = NULL, *output_iter;
+	WFDEventType type;
+	const WFDtime timeout = 0;
+	WFDint pipeline_id;
+	WFDint bind_time;
+
+	type = wfdDeviceEventWait(c->dev, c->event, timeout);
+
+	switch (type) {
+	case WFD_EVENT_PIPELINE_BIND_SOURCE_COMPLETE:
+		pipeline_id =
+			wfdGetEventAttribi(c->dev, c->event,
+					   WFD_EVENT_PIPELINE_BIND_PIPELINE_ID);
+
+		bind_time =
+			wfdGetEventAttribi(c->dev, c->event,
+					   WFD_EVENT_PIPELINE_BIND_TIME_EXT);
+
+		wl_list_for_each(output_iter, &c->base.output_list, base.link) {
+			if (output_iter->pipeline_id == pipeline_id)
+				output = output_iter;
+		}
+
+		if (output == NULL)
+			return;
+
+		wlsc_output_finish_frame(&output->base,
+					 c->start_time + bind_time);
+		break;
+	case WFD_EVENT_PORT_ATTACH_DETACH:
+		handle_port_state_change(c);
+		break;
+	default:
+		return;
+	}
+}
+
+static void
+wfd_destroy(struct wlsc_compositor *ec)
+{
+	struct wfd_compositor *d = (struct wfd_compositor *) ec;
+
+	udev_unref(d->udev);
+
+	wfdDestroyDevice(d->dev);
+
+	tty_destroy(d->tty);
+
+	free(d);
+}
+
+struct wlsc_compositor *
+wfd_compositor_create(struct wl_display *display, int connector)
+{
+	struct wfd_compositor *ec;
+	struct wl_event_loop *loop;
+	struct timeval tv;
+
+	ec = malloc(sizeof *ec);
+	if (ec == NULL)
+		return NULL;
+
+	memset(ec, 0, sizeof *ec);
+
+	gettimeofday(&tv, NULL);
+	ec->start_time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+	ec->udev = udev_new();
+	if (ec->udev == NULL) {
+		fprintf(stderr, "failed to initialize udev context\n");
+		return NULL;
+	}
+
+	ec->dev = wfdCreateDevice(WFD_DEFAULT_DEVICE_ID, NULL);
+	if (ec->dev == WFD_INVALID_HANDLE) {
+		fprintf(stderr, "failed to create wfd device\n");
+		return NULL;
+	}
+
+	ec->event = wfdCreateEvent(ec->dev, NULL);
+	if (ec->event == WFD_INVALID_HANDLE) {
+		fprintf(stderr, "failed to create wfd event\n");
+		return NULL;
+	}
+
+	ec->base.wl_display = display;
+	if (init_egl(ec) < 0) {
+		fprintf(stderr, "failed to initialize egl\n");
+		return NULL;
+	}
+
+	ec->base.destroy = wfd_destroy;
+	ec->base.create_buffer = wlsc_shm_buffer_create;
+	ec->base.focus = 1;
+
+	glGenFramebuffers(1, &ec->base.fbo);
+	glBindFramebuffer(GL_FRAMEBUFFER, ec->base.fbo);
+
+	/* Can't init base class until we have a current egl context */
+	if (wlsc_compositor_init(&ec->base, display) < 0)
+		return NULL;
+
+	if (create_outputs(ec, connector) < 0) {
+		fprintf(stderr, "failed to create outputs\n");
+		return NULL;
+	}
+
+	evdev_input_add_devices(&ec->base, ec->udev);
+
+	loop = wl_display_get_event_loop(ec->base.wl_display);
+	ec->wfd_source =
+		wl_event_loop_add_fd(loop,
+				     wfdDeviceEventGetFD(ec->dev, ec->event),
+				     WL_EVENT_READABLE, on_wfd_event, ec);
+	ec->tty = tty_create(&ec->base);
+
+	return &ec->base;
+}
diff --git a/compositor/compositor.c b/compositor/compositor.c
index 333ab22..d4df045 100644
--- a/compositor/compositor.c
+++ b/compositor/compositor.c
@@ -1606,6 +1606,11 @@
 		ec = x11_compositor_create(display, width, height);
 #endif
 
+#if BUILD_OPENWFD_COMPOSITOR
+	if (ec == NULL && getenv("OPENWFD"))
+		ec = wfd_compositor_create(display, option_connector);
+#endif
+
 #if BUILD_DRM_COMPOSITOR
 	if (ec == NULL)
 		ec = drm_compositor_create(display, option_connector);
diff --git a/compositor/compositor.h b/compositor/compositor.h
index 6a7991c..832ad08 100644
--- a/compositor/compositor.h
+++ b/compositor/compositor.h
@@ -280,6 +280,9 @@
 drm_compositor_create(struct wl_display *display, int connector);
 
 struct wlsc_compositor *
+wfd_compositor_create(struct wl_display *display, int connector);
+
+struct wlsc_compositor *
 wayland_compositor_create(struct wl_display *display, int width, int height);
 
 void
diff --git a/configure.ac b/configure.ac
index 567d95b..960f134 100644
--- a/configure.ac
+++ b/configure.ac
@@ -44,6 +44,15 @@
 fi
 
 
+AC_ARG_ENABLE(openwfd-compositor, [  --enable-openwfd-compositor],,
+	      enable_openwfd_compositor=no)
+AM_CONDITIONAL(ENABLE_OPENWFD_COMPOSITOR, test x$enable_openwfd_compositor == xyes)
+if test x$enable_openwfd_compositor == xyes; then
+  AC_DEFINE([BUILD_OPENWFD_COMPOSITOR], [1], [Build the OpenWF compositor])
+  PKG_CHECK_MODULES(OPENWFD, [openwfd])
+fi
+
+
 AC_ARG_ENABLE(wayland-compositor, [  --enable-wayland-compositor],,
 	      enable_wayland_compositor=yes)
 AM_CONDITIONAL(ENABLE_WAYLAND_COMPOSITOR,