From 216a204c7acc2c939a15a3753c453b5a95c5b9d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= <krh@bitplanet.net>
Date: Mon, 11 Apr 2011 14:15:50 -0400
Subject: [PATCH] meego compositor fun

---
 clients/Makefile.am             |   15 ++-
 clients/meego-app-switcher.c    |  187 +++++++++++++++++++++++++++++
 compositor/Makefile.am          |    8 +-
 compositor/compositor.c         |  224 +++++++++++++++++++++++++++++------
 compositor/compositor.h         |   50 +++++++-
 compositor/meego-tablet-shell.c |  250 +++++++++++++++++++++++++++++++++++++++
 compositor/meego-tablet-shell.h |   29 +++++
 protocol/meego-tablet.xml       |    9 ++
 8 files changed, 728 insertions(+), 44 deletions(-)
 create mode 100644 clients/meego-app-switcher.c
 create mode 100644 compositor/meego-tablet-shell.c
 create mode 100644 compositor/meego-tablet-shell.h
 create mode 100644 protocol/meego-tablet.xml

diff --git a/clients/Makefile.am b/clients/Makefile.am
index ee61efd..f5e26c5 100644
--- a/clients/Makefile.am
+++ b/clients/Makefile.am
@@ -9,7 +9,9 @@ noinst_PROGRAMS =				\
 	smoke					\
 	resizor					\
 	simple-client				\
-	eventdemo
+	eventdemo				\
+	meego-app-switcher
+
 
 noinst_LIBRARIES = libtoytoolkit.a
 
@@ -62,9 +64,18 @@ simple_client_LDADD = $(SIMPLE_CLIENT_LIBS) -lm
 eventdemo_SOURCES = eventdemo.c
 eventdemo_LDADD = $(toolkit_libs)
 
+meego_app_switcher_SOURCES =			\
+	meego-app-switcher.c			\
+	meego-tablet-protocol.c			\
+	meego-tablet-client-protocol.h
+
+meego_app_switcher_LDADD = $(toolkit_libs)
+
 BUILT_SOURCES =					\
 	screenshooter-client-protocol.h		\
-	screenshooter-protocol.c
+	screenshooter-protocol.c		\
+	meego-tablet-protocol.c			\
+	meego-tablet-client-protocol.h
 
 CLEANFILES = $(BUILT_SOURCES)
 
diff --git a/clients/meego-app-switcher.c b/clients/meego-app-switcher.c
new file mode 100644
index 0000000..d5fcc14
--- /dev/null
+++ b/clients/meego-app-switcher.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright © 2010 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 "wayland-util.h"
+#include "wayland-client.h"
+#include "wayland-glib.h"
+
+#include "window.h"
+#include "meego-tablet-client-protocol.h"
+
+#include <X11/keysym.h>
+
+static struct wl_app_switcher *app_switcher;
+
+struct switcher {
+	struct display *display;
+	struct window *window;
+	struct window *menu;
+	int32_t width, height;
+};
+
+static void
+rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius)
+{
+	cairo_move_to(cr, x0, y0 + radius);
+	cairo_arc(cr, x0 + radius, y0 + radius, radius, M_PI, 3 * M_PI / 2);
+	cairo_line_to(cr, x1 - radius, y0);
+	cairo_arc(cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2, 2 * M_PI);
+	cairo_line_to(cr, x1, y1 - radius);
+	cairo_arc(cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2);
+	cairo_line_to(cr, x0 + radius, y1);
+	cairo_arc(cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI);
+	cairo_close_path(cr);
+}
+
+static void
+switcher_draw(struct switcher *switcher)
+{
+	cairo_surface_t *surface;
+	cairo_t *cr;
+	struct rectangle allocation;
+	struct wl_surface *ws;
+	int r = 5;
+
+	window_draw(switcher->window);
+
+	window_get_child_allocation(switcher->window, &allocation);
+
+	surface = window_get_surface(switcher->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.0);
+	cairo_fill(cr);
+
+	rounded_rect(cr, r, r, allocation.width - r, allocation.height - r, r);
+	cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+	cairo_set_source_rgba(cr, 1.0, 1.0, 0.0, 1.0);
+	cairo_fill(cr);
+	cairo_destroy(cr);
+
+	cairo_surface_destroy(surface);
+
+	window_flush(switcher->window);
+
+	/* FIXME: Should we do app_swithcer.create_surface instead and
+	 * then just surface.map_toplevel()? */
+	ws = window_get_wl_surface(switcher->window);
+	wl_app_switcher_map_switcher(app_switcher, ws);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+	    uint32_t key, uint32_t sym, uint32_t state, void *data)
+{
+	if (state == 0)
+		return;
+
+	switch (sym) {
+	case XK_Escape:
+		exit(0);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+redraw_handler(struct window *window, void *data)
+{
+	struct switcher *switcher = data;
+
+	switcher_draw(switcher);
+}
+
+static struct switcher *
+switcher_create(struct display *display)
+{
+	struct switcher *switcher;
+
+	switcher = malloc(sizeof *switcher);
+	if (switcher == NULL)
+		return switcher;
+	memset(switcher, 0, sizeof *switcher);
+
+	switcher->window = window_create(display, 200, 200);
+	window_set_title(switcher->window, "Switcher");
+	switcher->display = display;
+
+	window_set_key_handler(switcher->window, key_handler);
+	window_set_user_data(switcher->window, switcher);
+	window_set_redraw_handler(switcher->window, redraw_handler);
+	window_set_decoration(switcher->window, 0);
+
+	switcher->width = 200;
+	switcher->height = 200;
+
+	window_set_child_size(switcher->window,
+			      switcher->width, switcher->height);
+
+	switcher_draw(switcher);
+
+	return switcher;
+}
+
+static void
+global_handler(struct display *display,
+	       const char *interface, uint32_t id, uint32_t version)
+{
+	if (strcmp(interface, "app_switcher") != 0)
+		return;
+
+	app_switcher =
+		wl_app_switcher_create(display_get_display(display), id);
+
+	fprintf(stderr, "got app switcher, id %d, proxy %p\n",
+		id, app_switcher);
+}
+
+int
+main(int argc, char *argv[])
+{
+	struct display *d;
+
+	d = display_create(&argc, &argv, NULL, global_handler);
+	if (d == NULL) {
+		fprintf(stderr, "failed to create display: %m\n");
+		return -1;
+	}
+
+	switcher_create (d);
+
+	display_run(d);
+
+	return 0;
+}
diff --git a/compositor/Makefile.am b/compositor/Makefile.am
index e90830a..4ff4952 100644
--- a/compositor/Makefile.am
+++ b/compositor/Makefile.am
@@ -31,6 +31,10 @@ compositor_SOURCES =				\
 	screenshooter-protocol.c		\
 	screenshooter-server-protocol.h		\
 	shm.c					\
+	meego-tablet-shell.c			\
+	meego-tablet-shell.h			\
+	meego-tablet-protocol.c			\
+	meego-tablet-server-protocol.h		\
 	$(drm_compositor_sources)		\
 	$(x11_compositor_sources)		\
 	$(wayland_compositor_sources)
@@ -42,7 +46,9 @@ dist_udevrulesd_DATA =				\
 
 BUILT_SOURCES =					\
 	screenshooter-server-protocol.h		\
-	screenshooter-protocol.c
+	screenshooter-protocol.c		\
+	meego-tablet-protocol.c			\
+	meego-tablet-server-protocol.h
 
 CLEANFILES = $(BUILT_SOURCES)
 
diff --git a/compositor/compositor.c b/compositor/compositor.c
index f2c0a05..6d103dd 100644
--- a/compositor/compositor.c
+++ b/compositor/compositor.c
@@ -35,6 +35,7 @@
 
 #include "wayland-server.h"
 #include "compositor.h"
+#include "meego-tablet-shell.h"
 
 struct wlsc_switcher {
 	struct wlsc_compositor *compositor;
@@ -126,6 +127,46 @@ wlsc_matrix_transform(struct wlsc_matrix *matrix, struct wlsc_vector *v)
 	*v = t;
 }
 
+void
+wlsc_tweener_init(struct wlsc_tweener *tweener, double value)
+{
+	tweener->k = 3.0;
+	tweener->current = value;
+	tweener->target = value;
+	tweener->previous = value;
+}
+
+void
+wlsc_tweener_update(struct wlsc_tweener *tweener, uint32_t msec)
+{
+	double force, current;
+
+	current = tweener->current;
+	force = tweener->k * (tweener->target - current) / 10.0 +
+		(tweener->previous - current);
+
+	tweener->current =
+		current + (current - tweener->previous) + force;
+	tweener->previous = current;
+
+	if (tweener->current >= 1.0) {
+		tweener->current = 1.0;
+		tweener->previous = 1.0;
+	}
+
+	if (tweener->current <= 0.0) {
+		tweener->current = 0.0;
+		tweener->previous = 0.0;
+	}
+}
+
+int
+wlsc_tweener_done(struct wlsc_tweener *tweener)
+{
+	return fabs(tweener->previous - tweener->target) < 0.001 &&
+		fabs(tweener->current - tweener->target) < 0.001;
+}
+
 static struct wlsc_surface *
 wlsc_surface_create(struct wlsc_compositor *compositor,
 		    int32_t x, int32_t y, int32_t width, int32_t height)
@@ -479,11 +520,52 @@ wlsc_surface_update_matrix(struct wlsc_surface *es)
 }
 
 void
-wlsc_compositor_finish_frame(struct wlsc_compositor *compositor, int msecs)
+wlsc_compositor_damage_all(struct wlsc_compositor *compositor)
+{
+	struct wlsc_output *output;
+
+	wl_list_for_each(output, &compositor->output_list, link)
+		pixman_region32_union_rect(&compositor->damage_region,
+					   &compositor->damage_region,
+					   output->x, output->y,
+					   output->width, output->height);
+	wlsc_compositor_schedule_repaint(compositor);
+}
+
+void
+wlsc_compositor_finish_frame(struct wlsc_compositor *compositor,
+			     uint32_t msecs)
 {
 	wl_display_post_frame(compositor->wl_display, msecs);
 	wl_event_source_timer_update(compositor->timer_source, 5);
 	compositor->repaint_on_timeout = 1;
+
+	app_switcher_update(compositor->meego_tablet_shell, msecs);
+
+	wlsc_tweener_update(&compositor->tint, msecs);
+	if (!wlsc_tweener_done(&compositor->tint))
+		wlsc_compositor_damage_all(compositor);
+}
+
+static void
+tint_output(struct wlsc_output *output,
+	    GLfloat tint, pixman_region32_t *region)
+{
+	struct wlsc_compositor *compositor = output->compositor;
+	struct wlsc_surface surface;
+	GLfloat color[4] = { 0.0, 0.0, 0.0, tint };
+
+	surface.compositor = compositor;
+	surface.x = output->x;
+	surface.y = output->y;
+	surface.width = output->width;
+	surface.height = output->height;
+	surface.visual = &compositor->compositor.premultiplied_argb_visual;
+	glUseProgram(compositor->solid_shader.program);
+	glUniformMatrix4fv(compositor->solid_shader.proj_uniform,
+			   1, GL_FALSE, output->matrix.d);
+	glUniform4fv(compositor->solid_shader.color_uniform, 1, color);
+	wlsc_surface_draw(&surface, output, region);
 }
 
 static void
@@ -498,8 +580,10 @@ wlsc_output_repaint(struct wlsc_output *output)
 
 	glViewport(0, 0, output->width, output->height);
 
-	glUniformMatrix4fv(ec->proj_uniform, 1, GL_FALSE, output->matrix.d);
-	glUniform1i(ec->tex_uniform, 0);
+	glUseProgram(ec->texture_shader.program);
+	glUniformMatrix4fv(ec->texture_shader.proj_uniform,
+			   1, GL_FALSE, output->matrix.d);
+	glUniform1i(ec->texture_shader.tex_uniform, 0);
 
 	pixman_region32_init(&new_damage);
 	pixman_region32_init(&total_damage);
@@ -555,8 +639,12 @@ wlsc_output_repaint(struct wlsc_output *output)
 		}
 	}
 
-	if (ec->switcher)
-		wlsc_surface_draw(ec->switcher->current,
+	if (ec->tint.current > 0.001)
+		tint_output(output, ec->tint.current, &total_damage);
+
+	glUseProgram(ec->texture_shader.program);
+	if (ec->overlay_surface)
+		wlsc_surface_draw(ec->overlay_surface,
 				  output, &total_damage);
 
 	if (ec->focus)
@@ -593,6 +681,13 @@ wlsc_compositor_schedule_repaint(struct wlsc_compositor *compositor)
 	compositor->repaint_on_timeout = 1;
 }
 
+void
+wlsc_compositor_set_tint(struct wlsc_compositor *compositor, float tint)
+{
+	compositor->tint.target = tint;
+	wlsc_compositor_damage_all(compositor);
+}
+
 static void
 surface_destroy(struct wl_client *client,
 		struct wl_surface *surface)
@@ -933,7 +1028,7 @@ notify_motion(struct wl_input_device *device, uint32_t time, int x, int y)
 	wlsc_surface_damage(wd->sprite);
 }
 
-static void
+void
 wlsc_surface_activate(struct wlsc_surface *surface,
 		      struct wlsc_input_device *device, uint32_t time)
 {
@@ -1015,6 +1110,7 @@ wlsc_switcher_next(struct wlsc_switcher *switcher)
 	if (l == &switcher->compositor->surface_list)
 		l = switcher->compositor->surface_list.next;
 	switcher->current = container_of(l, struct wlsc_surface, link);
+	switcher->compositor->overlay_surface = switcher->current;
 	wl_list_remove(&switcher->listener.link);
 	wl_list_insert(switcher->current->surface.destroy_listener_list.prev,
 		       &switcher->listener.link);
@@ -1040,6 +1136,7 @@ wlsc_switcher_create(struct wlsc_compositor *compositor)
 	switcher->compositor = compositor;
 	switcher->current = container_of(compositor->surface_list.next,
 					 struct wlsc_surface, link);
+	switcher->compositor->overlay_surface = switcher->current;
 	switcher->listener.func = switcher_handle_surface_destroy;
 	wl_list_init(&switcher->listener.link);
 
@@ -1329,7 +1426,7 @@ static const char vertex_shader[] =
 	"   v_texcoord = texcoord;\n"
 	"}\n";
 
-static const char fragment_shader[] =
+static const char texture_fragment_shader[] =
 	"precision mediump float;\n"
 	"varying vec2 v_texcoord;\n"
 	"uniform sampler2D tex;\n"
@@ -1338,53 +1435,103 @@ static const char fragment_shader[] =
 	"   gl_FragColor = texture2D(tex, v_texcoord)\n;"
 	"}\n";
 
+static const char solid_fragment_shader[] =
+	"precision mediump float;\n"
+	"varying vec2 v_texcoord;\n"
+	"uniform vec4 color;\n"
+	"void main()\n"
+	"{\n"
+	"   gl_FragColor = color\n;"
+	"}\n";
+
 static int
-init_shaders(struct wlsc_compositor *ec)
+compile_shader(GLenum type, const char *source)
 {
-	GLuint v, f, program;
-	const char *p;
+	GLuint s;
 	char msg[512];
 	GLint status;
 
-	p = vertex_shader;
-	v = glCreateShader(GL_VERTEX_SHADER);
-	glShaderSource(v, 1, &p, NULL);
-	glCompileShader(v);
-	glGetShaderiv(v, GL_COMPILE_STATUS, &status);
+	s = glCreateShader(type);
+	glShaderSource(s, 1, &source, NULL);
+	glCompileShader(s);
+	glGetShaderiv(s, GL_COMPILE_STATUS, &status);
 	if (!status) {
-		glGetShaderInfoLog(v, sizeof msg, NULL, msg);
-		fprintf(stderr, "vertex shader info: %s\n", msg);
-		return -1;
+		glGetShaderInfoLog(s, sizeof msg, NULL, msg);
+		fprintf(stderr, "shader info: %s\n", msg);
+		return GL_NONE;
 	}
 
-	p = fragment_shader;
-	f = glCreateShader(GL_FRAGMENT_SHADER);
-	glShaderSource(f, 1, &p, NULL);
-	glCompileShader(f);
-	glGetShaderiv(f, GL_COMPILE_STATUS, &status);
+	return s;
+}
+
+static int
+wlsc_shader_init(struct wlsc_shader *shader,
+		 const char *vertex_source, const char *fragment_source)
+{
+	char msg[512];
+	GLint status;
+
+	shader->vertex_shader =
+		compile_shader(GL_VERTEX_SHADER, vertex_source);
+	shader->fragment_shader =
+		compile_shader(GL_FRAGMENT_SHADER, fragment_source);
+
+	shader->program = glCreateProgram();
+	glAttachShader(shader->program, shader->vertex_shader);
+	glAttachShader(shader->program, shader->fragment_shader);
+	glBindAttribLocation(shader->program, 0, "position");
+	glBindAttribLocation(shader->program, 1, "texcoord");
+
+	glLinkProgram(shader->program);
+	glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
 	if (!status) {
-		glGetShaderInfoLog(f, sizeof msg, NULL, msg);
-		fprintf(stderr, "fragment shader info: %s\n", msg);
+		glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg);
+		fprintf(stderr, "link info: %s\n", msg);
 		return -1;
 	}
 
-	program = glCreateProgram();
-	glAttachShader(program, v);
-	glAttachShader(program, f);
-	glBindAttribLocation(program, 0, "position");
-	glBindAttribLocation(program, 1, "texcoord");
+	shader->proj_uniform = glGetUniformLocation(shader->program, "proj");
+	shader->tex_uniform = glGetUniformLocation(shader->program, "tex");
+
+	return 0;
+}
+
+static void
+init_solid_shader(struct wlsc_shader *shader,
+		  GLuint vertex_shader, const char *fragment_source)
+{
+	GLint status;
+	char msg[512];
+
+	shader->vertex_shader = vertex_shader;
+	shader->fragment_shader =
+		compile_shader(GL_FRAGMENT_SHADER, fragment_source);
+
+	shader->program = glCreateProgram();
+	glAttachShader(shader->program, shader->vertex_shader);
+	glAttachShader(shader->program, shader->fragment_shader);
+	glBindAttribLocation(shader->program, 0, "position");
+	glBindAttribLocation(shader->program, 1, "texcoord");
 
-	glLinkProgram(program);
-	glGetProgramiv(program, GL_LINK_STATUS, &status);
+	glLinkProgram(shader->program);
+	glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
 	if (!status) {
-		glGetProgramInfoLog(program, sizeof msg, NULL, msg);
+		glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg);
 		fprintf(stderr, "link info: %s\n", msg);
-		return -1;
 	}
 
-	glUseProgram(program);
-	ec->proj_uniform = glGetUniformLocation(program, "proj");
-	ec->tex_uniform = glGetUniformLocation(program, "tex");
+	shader->proj_uniform = glGetUniformLocation(shader->program, "proj");
+	shader->color_uniform = glGetUniformLocation(shader->program, "color");
+}
+
+static int
+init_shaders(struct wlsc_compositor *ec)
+{
+	wlsc_shader_init(&ec->texture_shader,
+			 vertex_shader, texture_fragment_shader);
+	init_solid_shader(&ec->solid_shader,
+			  ec->texture_shader.vertex_shader,
+			  solid_fragment_shader);
 
 	return 0;
 }
@@ -1468,6 +1615,7 @@ wlsc_compositor_init(struct wlsc_compositor *ec, struct wl_display *display)
 	wl_list_init(&ec->input_device_list);
 	wl_list_init(&ec->output_list);
 	wl_list_init(&ec->binding_list);
+	ec->tint.k = 0.2;
 
 	wlsc_compositor_add_binding(ec, KEY_BACKSPACE,
 				    MODIFIER_CTRL | MODIFIER_ALT,
@@ -1565,6 +1713,8 @@ int main(int argc, char *argv[])
 	wl_event_loop_add_signal(loop, SIGTERM, on_term_signal, ec);
 	wl_event_loop_add_signal(loop, SIGINT, on_term_signal, ec);
 
+	ec->meego_tablet_shell = meego_tablet_shell_create(ec);
+
 	wl_display_run(display);
 
 	eglUnbindWaylandDisplayWL(ec->display, display);
diff --git a/compositor/compositor.h b/compositor/compositor.h
index 865e527..4fac6d6 100644
--- a/compositor/compositor.h
+++ b/compositor/compositor.h
@@ -79,6 +79,23 @@ struct wlsc_shm {
 	struct wl_object object;
 };
 
+struct meego_tablet_shell;
+
+struct wlsc_tweener {
+	double k;
+	double current;
+	double target;
+	double previous;
+};
+
+struct wlsc_shader {
+	GLuint program;
+	GLuint vertex_shader, fragment_shader;
+	GLuint proj_uniform;
+	GLuint tex_uniform;
+	GLuint color_uniform;
+};
+
 struct wlsc_compositor {
 	struct wl_compositor compositor;
 
@@ -87,10 +104,11 @@ struct wlsc_compositor {
 	EGLContext context;
 	EGLConfig config;
 	GLuint fbo;
-	GLuint proj_uniform, tex_uniform;
+	struct wlsc_shader texture_shader;
+	struct wlsc_shader solid_shader;
+
 	struct wl_buffer **pointer_buffers;
 	struct wl_display *wl_display;
-
 	/* We implement the shell interface. */
 	struct wl_shell shell;
 
@@ -101,6 +119,8 @@ struct wlsc_compositor {
 	struct wl_list input_device_list;
 	struct wl_list surface_list;
 	struct wl_list binding_list;
+	struct wlsc_surface *overlay_surface;
+	struct wlsc_tweener tint;
 
 	/* Repaint state. */
 	struct wl_event_source *timer_source;
@@ -113,6 +133,8 @@ struct wlsc_compositor {
 	struct wlsc_switcher *switcher;
 	uint32_t focus;
 
+	struct meego_tablet_shell *meego_tablet_shell;
+
 	void (*destroy)(struct wlsc_compositor *ec);
 	int (*authenticate)(struct wlsc_compositor *c, uint32_t id);
 	void (*present)(struct wlsc_compositor *c);
@@ -138,7 +160,9 @@ enum wlsc_surface_map_type {
 	WLSC_SURFACE_MAP_UNMAPPED,
 	WLSC_SURFACE_MAP_TOPLEVEL,
 	WLSC_SURFACE_MAP_TRANSIENT,
-	WLSC_SURFACE_MAP_FULLSCREEN
+	WLSC_SURFACE_MAP_FULLSCREEN,
+
+	WLSC_SURFACE_MAP_SWITCHER
 };
 
 struct wlsc_surface {
@@ -157,6 +181,15 @@ struct wlsc_surface {
 };
 
 void
+app_switcher_update(struct meego_tablet_shell *shell, uint32_t msecs);
+void
+wlsc_tweener_init(struct wlsc_tweener *tweener, double value);
+void
+wlsc_tweener_update(struct wlsc_tweener *tweener, uint32_t msec);
+int
+wlsc_tweener_done(struct wlsc_tweener *tweener);
+
+void
 wlsc_surface_update_matrix(struct wlsc_surface *es);
 
 void
@@ -181,9 +214,18 @@ notify_keyboard_focus(struct wl_input_device *device,
 		      struct wl_array *keys);
 
 void
-wlsc_compositor_finish_frame(struct wlsc_compositor *compositor, int msecs);
+wlsc_compositor_finish_frame(struct wlsc_compositor *compositor,
+			     uint32_t msecs);
 void
 wlsc_compositor_schedule_repaint(struct wlsc_compositor *compositor);
+void
+wlsc_compositor_set_tint(struct wlsc_compositor *compositor, float tint);
+void
+wlsc_compositor_damage_all(struct wlsc_compositor *compositor);
+
+void
+wlsc_surface_activate(struct wlsc_surface *surface,
+		      struct wlsc_input_device *device, uint32_t time);
 
 struct wlsc_binding;
 typedef void (*wlsc_binding_handler_t)(struct wlsc_input_device *device,
diff --git a/compositor/meego-tablet-shell.c b/compositor/meego-tablet-shell.c
new file mode 100644
index 0000000..1daeaf8
--- /dev/null
+++ b/compositor/meego-tablet-shell.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright © 2011 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 <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <linux/input.h>
+
+#include "compositor.h"
+#include "meego-tablet-server-protocol.h"
+#include "meego-tablet-shell.h"
+
+/* FIXME: We need a way to generate a different prefix. */
+struct wl_app_switcher {
+	struct wl_object object;
+	struct wlsc_surface *surface;
+	pid_t pid;
+	struct wl_listener listener;
+	struct wlsc_input_device *device;
+	struct wlsc_tweener scale;
+	int width, height;
+};
+
+struct meego_tablet_shell {
+	struct wl_app_switcher app_switcher;
+	struct wl_event_source *sigchld_source;
+};
+
+static void
+sigchld_handler(int signal_number, void *data)
+{
+	struct meego_tablet_shell *shell = data;
+	int status;
+
+	wait(&status);
+	shell->app_switcher.pid = -1;
+}
+
+void
+app_switcher_update(struct meego_tablet_shell *shell, uint32_t msecs)
+{
+	struct wl_app_switcher *app_switcher = &shell->app_switcher;
+	struct wlsc_surface *es = app_switcher->surface;
+	struct wlsc_output *output;
+	float scale;
+
+	if (!app_switcher->surface)
+		return;
+
+	output = container_of(es->compositor->output_list.next,
+			      struct wlsc_output, link);
+
+	scale = app_switcher->scale.current + 1.0;
+	wlsc_tweener_update(&app_switcher->scale, msecs);
+	if (!wlsc_tweener_done(&app_switcher->scale))
+		wlsc_surface_damage(app_switcher->surface);
+
+	es->width = scale * app_switcher->width;
+	es->height = scale * app_switcher->height;
+	es->x = (output->width - es->width) / 2;
+	es->y = (output->height - es->height) / 2;
+	wlsc_surface_update_matrix(es);
+	wlsc_surface_damage(es);
+	wlsc_compositor_damage_all(es->compositor);
+}
+
+static void
+switcher_map_switcher(struct wl_client *client,
+		      struct wl_app_switcher *app_switcher,
+		      struct wl_surface *surface)
+{
+	struct wlsc_surface *es = (struct wlsc_surface *) surface;
+	struct meego_tablet_shell *shell =
+		container_of(app_switcher,
+			     struct meego_tablet_shell, app_switcher);
+
+	switch (es->map_type) {
+	default:
+		/* FIXME: The window.c clients always map as toplevel
+		 * first... so just override that. */
+		wlsc_tweener_init(&app_switcher->scale, 1.0);
+		app_switcher->scale.k = 0.8;
+		app_switcher->scale.target = 0.0;
+		app_switcher->surface = es;
+		app_switcher->width = es->width;
+		app_switcher->height = es->height;
+		app_switcher_update(shell, 0);
+		es->map_type = WLSC_SURFACE_MAP_SWITCHER;
+
+		wl_list_insert(surface->destroy_listener_list.prev,
+			       &app_switcher->listener.link);
+
+		wlsc_surface_activate(es, app_switcher->device, get_time());
+		wlsc_compositor_set_tint(es->compositor, 0.5);
+		es->compositor->overlay_surface = es;
+		break;
+
+	case WLSC_SURFACE_MAP_SWITCHER:
+		break;
+	}
+
+}
+
+static const struct wl_app_switcher_interface app_switcher_interface = {
+	switcher_map_switcher
+};
+
+static void
+launch_switcher(struct wlsc_compositor *compositor)
+{
+	struct meego_tablet_shell *shell = compositor->meego_tablet_shell;
+	char s[32];
+	int sv[2], flags;
+	struct wl_client *client;
+
+	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+		fprintf(stderr, "socketpair failed\n");
+		return;
+	}
+
+	/* SOCK_CLOEXEC closes both ends, so we need to unset the flag
+	 * on the client fd. */
+	flags = fcntl(sv[1], F_GETFD);
+	if (flags != -1)
+		fcntl(sv[1], F_SETFD, flags & ~FD_CLOEXEC);
+
+	shell->app_switcher.pid = fork();
+	switch (shell->app_switcher.pid) {
+	case 0:
+		snprintf(s, sizeof s, "%d", sv[1]);
+		setenv("WAYLAND_SOCKET", s, 1);
+		setenv("EGL_PLATFORM", "wayland", 1);
+#if 1
+		if (execl("./clients/meego-app-switcher",
+			  "./clients/meego-app-switcher", NULL) < 0)
+			fprintf(stderr, "exec failed: %m\n");
+#else
+		if (execl("/usr/bin/meego-qml-launcher",
+			  "/usr/bin/meego-qml-launcher",
+			  "-platform", "wayland",
+			  "--app", "meego-app-calculator",
+			  "--width", "600", "--height", "480"
+			  "--opengl", NULL) < 0)
+			fprintf(stderr, "exec failed: %m\n");
+#endif
+		exit(0);
+
+	default:
+		close(sv[1]);
+		client = wl_client_create(compositor->wl_display, sv[0]);
+
+		/* FIXME: If this is just a global, anybody who knows
+		 * the global name can access it.  We need the
+		 * subscribtion mechanism where we send back an ack
+		 * that an object of a certain type and version has
+		 * been bound to a client id.  Can't do that because
+		 * the client needs to allocate the id... */
+		wl_client_post_global(client, &shell->app_switcher.object);
+		break;
+
+	case -1:
+		fprintf(stderr, "failed to fork\n");
+		break;
+	}
+}
+
+static void
+app_switcher_binding(struct wlsc_input_device *device,
+		     uint32_t time, uint32_t key, uint32_t state, void *data)
+{
+	struct wlsc_compositor *compositor = data;
+	struct meego_tablet_shell *shell = compositor->meego_tablet_shell;
+
+	if (shell->app_switcher.surface || !state)
+		return;
+	if (shell->app_switcher.pid != -1)
+		fprintf(stderr, "lingering switcher child...\n");
+
+	shell->app_switcher.device = device;
+	launch_switcher(compositor);
+}
+
+static void
+handle_surface_destroy(struct wl_listener *listener,
+		       struct wl_surface *surface, uint32_t time)
+{
+	struct wl_app_switcher *switcher =
+		container_of(listener, struct wl_app_switcher, listener);
+	struct wlsc_surface *es = (struct wlsc_surface *) surface;
+
+	switcher->surface = NULL;
+	es->compositor->overlay_surface = NULL;
+	wlsc_compositor_set_tint(es->compositor, 0.0);
+}
+
+struct meego_tablet_shell *
+meego_tablet_shell_create(struct wlsc_compositor *compositor)
+{
+	struct meego_tablet_shell *shell;
+	struct wl_event_loop *loop;
+
+	shell = malloc(sizeof *shell);
+	if (shell == NULL)
+		return NULL;
+
+	shell->app_switcher.surface = NULL;
+	shell->app_switcher.pid = -1;
+
+	shell->app_switcher.object.interface = &wl_app_switcher_interface;
+	shell->app_switcher.object.implementation =
+		(void (**)(void)) &app_switcher_interface;
+	wl_display_add_object(compositor->wl_display,
+			      &shell->app_switcher.object);
+
+	loop = wl_display_get_event_loop(compositor->wl_display);
+	shell->sigchld_source =
+		wl_event_loop_add_signal(loop, SIGCHLD,
+					 sigchld_handler, shell);
+
+	shell->app_switcher.listener.func = handle_surface_destroy;
+	wl_list_init(&shell->app_switcher.listener.link);
+
+	wlsc_compositor_add_binding(compositor, KEY_GRAVE, MODIFIER_ALT,
+				    app_switcher_binding, compositor);
+
+	return shell;
+}
diff --git a/compositor/meego-tablet-shell.h b/compositor/meego-tablet-shell.h
new file mode 100644
index 0000000..96be25b
--- /dev/null
+++ b/compositor/meego-tablet-shell.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2011 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 MEEGO_TABLET_SHELL_H
+#define MEEGO_TABLET_SHELL_H
+
+struct meego_tablet_shell *
+meego_tablet_shell_create(struct wlsc_compositor *compositor);
+
+#endif /* MEEGO_TABLET_SHELL_H */
diff --git a/protocol/meego-tablet.xml b/protocol/meego-tablet.xml
new file mode 100644
index 0000000..25540d8
--- /dev/null
+++ b/protocol/meego-tablet.xml
@@ -0,0 +1,9 @@
+<protocol name="meego_tablet">
+
+  <interface name="app_switcher" version="1">
+    <request name="map_switcher">
+      <arg name="surface" type="object" interface="surface"/>
+    </request>
+  </interface>
+
+</protocol>
-- 
1.7.4.1

