libweston: Implement alpha-compositing-v1 for gl renderer
diff --git a/Makefile.am b/Makefile.am
index e4da7b9..f298fd2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -169,7 +169,9 @@
 	protocol/input-timestamps-unstable-v1-protocol.c		\
 	protocol/input-timestamps-unstable-v1-server-protocol.h		\
 	protocol/weston-touch-calibration-protocol.c			\
-	protocol/weston-touch-calibration-server-protocol.h
+	protocol/weston-touch-calibration-server-protocol.h		\
+	protocol/alpha-compositing-unstable-v1-protocol.c		\
+	protocol/alpha-compositing-unstable-v1-server-protocol.h
 
 BUILT_SOURCES += $(nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES)
 
@@ -635,6 +637,8 @@
 nodist_weston_simple_egl_SOURCES =		\
 	protocol/xdg-shell-unstable-v6-protocol.c		\
 	protocol/xdg-shell-unstable-v6-client-protocol.h	\
+	protocol/alpha-compositing-unstable-v1-protocol.c	\
+	protocol/alpha-compositing-unstable-v1-client-protocol.h	\
 	protocol/ivi-application-protocol.c		\
 	protocol/ivi-application-client-protocol.h
 weston_simple_egl_CFLAGS = $(AM_CFLAGS) $(SIMPLE_EGL_CLIENT_CFLAGS)
@@ -934,7 +938,9 @@
 	protocol/tablet-unstable-v2-protocol.c		\
 	protocol/tablet-unstable-v2-client-protocol.h			\
 	protocol/input-timestamps-unstable-v1-protocol.c		\
-	protocol/input-timestamps-unstable-v1-client-protocol.h
+	protocol/input-timestamps-unstable-v1-client-protocol.h		\
+	protocol/alpha-compositing-unstable-v1-protocol.c	\
+	protocol/alpha-compositing-unstable-v1-client-protocol.h
 
 westondatadir = $(datadir)/weston
 dist_westondata_DATA =				\
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 8dc19f8..1c4c386 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -55,6 +55,7 @@
 #include "timeline.h"
 
 #include "compositor.h"
+#include "alpha-compositing-unstable-v1-server-protocol.h"
 #include "viewporter-server-protocol.h"
 #include "presentation-time-server-protocol.h"
 #include "shared/helpers.h"
@@ -305,6 +306,8 @@
 	pixman_region32_init(&view->clip);
 
 	view->alpha = 1.0;
+	view->blending_alpha = 1.0;
+	view->blending_equation = ZWP_BLENDING_V1_BLENDING_EQUATION_NONE;
 	pixman_region32_init(&view->transform.opaque);
 
 	wl_list_init(&view->geometry.transformation_list);
@@ -445,6 +448,9 @@
 	state->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
 	state->buffer_viewport.surface.width = -1;
 	state->buffer_viewport.changed = 0;
+
+	state->blending_equation = ZWP_BLENDING_V1_BLENDING_EQUATION_NONE;
+	state->blending_alpha = 1.0;
 }
 
 static void
@@ -1994,6 +2000,9 @@
 	if (surface->viewport_resource)
 		wl_resource_set_user_data(surface->viewport_resource, NULL);
 
+	if (surface->blending_resource)
+		wl_resource_set_user_data(surface->blending_resource, NULL);
+
 	weston_surface_destroy(surface);
 }
 
@@ -3229,6 +3238,12 @@
 	wl_list_init(&state->feedback_list);
 
 	wl_signal_emit(&surface->commit_signal, surface);
+
+	/* zwp_alpha_compositing.blending */
+	wl_list_for_each(view, &surface->views, surface_link) {
+		view->blending_alpha = state->blending_alpha;
+		view->blending_equation = state->blending_equation;
+	}
 }
 
 static void
@@ -6247,6 +6262,129 @@
 }
 
 static void
+destroy_blending(struct wl_resource *resource)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(resource);
+
+	if (!surface)
+		return;
+
+	pixman_region32_union_rect(&surface->pending.damage_surface,
+				   &surface->pending.damage_surface,
+				   0, 0, surface->width, surface->height);
+	surface->blending_resource = NULL;
+	surface->pending.blending_equation = ZWP_BLENDING_V1_BLENDING_EQUATION_NONE;
+	surface->pending.blending_alpha = 1.0;
+}
+
+static void
+blending_destroy(struct wl_client *client,
+		 struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static void
+blending_set_blending(struct wl_client *client,
+		      struct wl_resource *resource,
+		      uint32_t equation)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(resource);
+
+	if (!surface)
+		return;
+
+	pixman_region32_union_rect(&surface->pending.damage_surface,
+				   &surface->pending.damage_surface,
+				   0, 0, surface->width, surface->height);
+	surface->pending.blending_equation = equation;
+}
+
+static void
+blending_set_alpha(struct wl_client *client,
+		   struct wl_resource *resource,
+		   wl_fixed_t alpha)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(resource);
+
+	if (!surface)
+		return;
+
+	pixman_region32_union_rect(&surface->pending.damage_surface,
+				   &surface->pending.damage_surface,
+				   0, 0, surface->width, surface->height);
+	surface->pending.blending_alpha = wl_fixed_to_double(alpha);
+}
+
+static const struct zwp_blending_v1_interface blending_interface = {
+	blending_destroy,
+	blending_set_blending,
+	blending_set_alpha
+};
+
+static void
+alpha_compositing_destroy(struct wl_client *client,
+			  struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static void
+alpha_compositing_get_blending(struct wl_client *client,
+			       struct wl_resource *alpha_compositing,
+			       uint32_t id,
+			       struct wl_resource *surface_resource)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(surface_resource);
+	struct wl_resource *resource;
+
+	if (surface->blending_resource) {
+		wl_resource_post_error(alpha_compositing,
+			ZWP_ALPHA_COMPOSITING_V1_ERROR_BLENDING_EXISTS,
+			"a blending for that surface already exists");
+		return;
+	}
+
+	resource = wl_resource_create(client, &zwp_blending_v1_interface,
+				      1, id);
+	if (resource == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_resource_set_implementation(resource, &blending_interface,
+				       surface, destroy_blending);
+
+	surface->blending_resource = resource;
+}
+
+static const struct zwp_alpha_compositing_v1_interface alpha_compositing_interface = {
+	alpha_compositing_destroy,
+	alpha_compositing_get_blending,
+};
+
+static void
+bind_alpha_compositing(struct wl_client *client,
+		       void *data, uint32_t version, uint32_t id)
+{
+	struct wl_resource *resource;
+
+	resource = wl_resource_create(client, &zwp_alpha_compositing_v1_interface,
+				      version, id);
+	if (resource == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_resource_set_implementation(resource, &alpha_compositing_interface,
+				       NULL, NULL);
+}
+
+static void
 compositor_bind(struct wl_client *client,
 		void *data, uint32_t version, uint32_t id)
 {
@@ -6361,6 +6499,10 @@
 			      ec, bind_presentation))
 		goto fail;
 
+	if (!wl_global_create(ec->wl_display, &zwp_alpha_compositing_v1_interface, 1,
+			      ec, bind_alpha_compositing))
+		goto fail;
+
 	if (weston_input_init(ec) != 0)
 		goto fail;
 
diff --git a/libweston/compositor.h b/libweston/compositor.h
index abc9200..51c569f 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -1261,6 +1261,8 @@
 
 	pixman_region32_t clip;          /* See weston_view_damage_below() */
 	float alpha;                     /* part of geometry, see below */
+	float blending_alpha;
+	int blending_equation;
 
 	void *renderer_state;
 
@@ -1360,6 +1362,9 @@
 	/* wp_viewport.set_source */
 	/* wp_viewport.set_destination */
 	struct weston_buffer_viewport buffer_viewport;
+
+	float blending_alpha;
+	int blending_equation;
 };
 
 struct weston_surface_activation_data {
@@ -1442,6 +1447,8 @@
 	/* wp_viewport resource for this surface */
 	struct wl_resource *viewport_resource;
 
+	struct wl_resource *blending_resource;
+
 	/* All the pending state, that wl_surface.commit will apply. */
 	struct weston_surface_state pending;
 
diff --git a/libweston/gl-renderer.c b/libweston/gl-renderer.c
index 2c50d2d..9bcab7f 100644
--- a/libweston/gl-renderer.c
+++ b/libweston/gl-renderer.c
@@ -54,6 +54,7 @@
 #include "vertex-clipping.h"
 #include "linux-dmabuf.h"
 #include "linux-dmabuf-unstable-v1-server-protocol.h"
+#include "alpha-compositing-unstable-v1-server-protocol.h"
 
 #include "shared/helpers.h"
 #include "shared/platform.h"
@@ -879,11 +880,17 @@
 	int i;
 	struct gl_surface_state *gs = get_surface_state(view->surface);
 	struct gl_output_state *go = get_output_state(output);
+	float alpha = view->alpha;
+
+	if (view->blending_equation != ZWP_BLENDING_V1_BLENDING_EQUATION_NONE &&
+	    view->blending_equation != ZWP_BLENDING_V1_BLENDING_EQUATION_OPAQUE) {
+		alpha *= view->blending_alpha;
+	}
 
 	glUniformMatrix4fv(shader->proj_uniform,
 			   1, GL_FALSE, go->output_matrix.d);
 	glUniform4fv(shader->color_uniform, 1, gs->color);
-	glUniform1f(shader->alpha_uniform, view->alpha);
+	glUniform1f(shader->alpha_uniform, alpha);
 
 	for (i = 0; i < gs->num_textures; i++)
 		glUniform1i(shader->tex_uniforms[i], i);
@@ -900,6 +907,8 @@
 	pixman_region32_t repaint;
 	/* opaque region in surface coordinates: */
 	pixman_region32_t surface_opaque;
+	pixman_region32_t surface_opaque_full;
+	pixman_region32_t *surface_opaque_src_ptr;
 	/* non-opaque region in surface coordinates: */
 	pixman_region32_t surface_blend;
 	GLint filter;
@@ -919,7 +928,13 @@
 	if (!pixman_region32_not_empty(&repaint))
 		goto out;
 
-	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+	if (ev->blending_equation == ZWP_BLENDING_V1_BLENDING_EQUATION_PREMULTIPLIED) {
+		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+	} else if (ev->blending_equation == ZWP_BLENDING_V1_BLENDING_EQUATION_STRAIGHT) {
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	} else {
+		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+	}
 
 	if (gr->fan_debug) {
 		use_shader(gr, &gr->solid_shader);
@@ -951,14 +966,23 @@
 	pixman_region32_subtract(&surface_blend, &surface_blend,
 				 &ev->surface->opaque);
 
+	if (ev->blending_equation == ZWP_BLENDING_V1_BLENDING_EQUATION_OPAQUE) {
+		pixman_region32_clear(&surface_blend);
+		pixman_region32_init_rect(&surface_opaque_full, 0, 0,
+					  ev->surface->width, ev->surface->height);
+		surface_opaque_src_ptr = &surface_opaque_full;
+	} else {
+		surface_opaque_src_ptr = &ev->surface->opaque;
+	}
+
 	/* XXX: Should we be using ev->transform.opaque here? */
 	pixman_region32_init(&surface_opaque);
 	if (ev->geometry.scissor_enabled)
 		pixman_region32_intersect(&surface_opaque,
-					  &ev->surface->opaque,
+					  surface_opaque_src_ptr,
 					  &ev->geometry.scissor);
 	else
-		pixman_region32_copy(&surface_opaque, &ev->surface->opaque);
+		pixman_region32_copy(&surface_opaque, surface_opaque_src_ptr);
 
 	if (pixman_region32_not_empty(&surface_opaque)) {
 		if (gs->shader == &gr->texture_shader_rgba) {