compositor: Support loadable shells

The shell module is responsible for implementing the higher level
compositor behavior.  We default to the desktop-lite shell built in to
the compositor.
diff --git a/compositor/Makefile.am b/compositor/Makefile.am
index 47f8091..7855feb 100644
--- a/compositor/Makefile.am
+++ b/compositor/Makefile.am
@@ -8,8 +8,10 @@
 
 AM_CFLAGS = $(GCC_CFLAGS)
 
+compositor_LDFLAGS = -export-dynamic
 compositor_LDADD =				\
-	$(COMPOSITOR_LIBS)
+	$(COMPOSITOR_LIBS)			\
+	$(DLOPEN_LIBS)
 
 if ENABLE_DRM_COMPOSITOR
 drm_compositor_sources = compositor-drm.c tty.c evdev.c
diff --git a/compositor/compositor-x11.c b/compositor/compositor-x11.c
index fe8038b..2f02ec3 100644
--- a/compositor/compositor-x11.c
+++ b/compositor/compositor-x11.c
@@ -448,7 +448,7 @@
 
 			output = x11_compositor_find_output(c, focus_in->event);
 			notify_keyboard_focus(c->base.input_device,
-					      get_time(),
+					      wlsc_compositor_get_time(),
 					      &output->base, &c->keys);
 
 			free(prev);
@@ -540,7 +540,8 @@
 			if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED)
 				break;
 			notify_keyboard_focus(c->base.input_device,
-					      get_time(), NULL, NULL);
+					      wlsc_compositor_get_time(),
+					      NULL, NULL);
 			break;
 
 		default:
diff --git a/compositor/compositor.c b/compositor/compositor.c
index 9be57df..b76f301 100644
--- a/compositor/compositor.c
+++ b/compositor/compositor.c
@@ -32,6 +32,7 @@
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <math.h>
 #include <linux/input.h>
+#include <dlfcn.h>
 
 #include "wayland-server.h"
 #include "compositor.h"
@@ -42,6 +43,7 @@
 static const char *option_socket_name = NULL;
 static const char *option_background = "background.jpg";
 static const char *option_geometry = "1024x640";
+static const char *option_shell = NULL;
 static int option_idle_time = 5;
 static int option_connector = 0;
 
@@ -56,6 +58,8 @@
 	  &option_socket_name, "Socket Name" },
 	{ "idle-time", 'i', 0, G_OPTION_ARG_INT,
 	  &option_idle_time, "Screensaver idle time" },
+	{ "shell", 'i', 0, G_OPTION_ARG_STRING,
+	  &option_shell, "Shell module" },
 	{ NULL }
 };
 
@@ -123,7 +127,7 @@
 	*v = t;
 }
 
-void
+WL_EXPORT void
 wlsc_tweener_init(struct wlsc_tweener *tweener,
 		  double k, double current, double target)
 {
@@ -133,7 +137,7 @@
 	tweener->target = target;
 }
 
-void
+WL_EXPORT void
 wlsc_tweener_update(struct wlsc_tweener *tweener, uint32_t msec)
 {
 	double force, current, step;
@@ -160,14 +164,14 @@
 	}
 }
 
-int
+WL_EXPORT int
 wlsc_tweener_done(struct wlsc_tweener *tweener)
 {
 	return fabs(tweener->previous - tweener->target) < 0.0002 &&
 		fabs(tweener->current - tweener->target) < 0.0002;
 }
 
-struct wlsc_surface *
+WL_EXPORT struct wlsc_surface *
 wlsc_surface_create(struct wlsc_compositor *compositor,
 		    int32_t x, int32_t y, int32_t width, int32_t height)
 {
@@ -209,7 +213,7 @@
 	return surface;
 }
 
-void
+WL_EXPORT void
 wlsc_surface_damage_rectangle(struct wlsc_surface *surface,
 			      int32_t x, int32_t y,
 			      int32_t width, int32_t height)
@@ -223,15 +227,15 @@
 	wlsc_compositor_schedule_repaint(compositor);
 }
 
-void
+WL_EXPORT void
 wlsc_surface_damage(struct wlsc_surface *surface)
 {
 	wlsc_surface_damage_rectangle(surface, 0, 0,
 				      surface->width, surface->height);
 }
 
-uint32_t
-get_time(void)
+WL_EXPORT uint32_t
+wlsc_compositor_get_time(void)
 {
 	struct timeval tv;
 
@@ -263,7 +267,7 @@
 
 	wl_list_remove(&surface->buffer_link);
 
-	time = get_time();
+	time = wlsc_compositor_get_time();
 	wl_list_for_each_safe(l, next,
 			      &surface->surface.destroy_listener_list, link)
 		l->func(l, &surface->surface, time);
@@ -586,7 +590,7 @@
 	wl_list_insert(&compositor->surface_list, &surface->link);
 }
 
-void
+WL_EXPORT void
 wlsc_surface_update_matrix(struct wlsc_surface *es)
 {
 	wlsc_matrix_init(&es->matrix);
@@ -599,7 +603,7 @@
 			  1.0 / es->width, 1.0 / es->height, 1);
 }
 
-void
+WL_EXPORT void
 wlsc_compositor_damage_all(struct wlsc_compositor *compositor)
 {
 	struct wlsc_output *output;
@@ -608,7 +612,7 @@
 		wlsc_output_damage(output);
 }
 
-void
+WL_EXPORT void
 wlsc_output_finish_frame(struct wlsc_output *output, int msecs)
 {
 	struct wlsc_compositor *compositor = output->compositor;
@@ -632,7 +636,7 @@
 		animation->frame(animation, output, msecs);
 }
 
-void
+WL_EXPORT void
 wlsc_output_damage(struct wlsc_output *output)
 {
 	struct wlsc_compositor *compositor = output->compositor;
@@ -654,8 +658,10 @@
 
 	wlsc_tweener_update(&compositor->fade.tweener, msecs);
 	if (wlsc_tweener_done(&compositor->fade.tweener)) {
-		if (compositor->fade.tweener.current > 0.999)
+		if (compositor->fade.tweener.current > 0.999) {
 			compositor->state = WLSC_COMPOSITOR_SLEEPING;
+			compositor->shell->lock(compositor->shell);
+		}
 		compositor->fade.tweener.current =
 			compositor->fade.tweener.target;
 		wl_list_remove(&animation->link);
@@ -717,11 +723,6 @@
 			      &output->previous_damage_region);
 	pixman_region32_copy(&output->previous_damage_region, &new_damage);
 
-	if (ec->state == WLSC_COMPOSITOR_SLEEPING) {
-		glClear(GL_COLOR_BUFFER_BIT);
-		return;
-	}
-
 	if (ec->focus)
 		if (output->set_hardware_cursor(output, ec->input_device) < 0)
 			using_hardware_cursor = 0;
@@ -729,8 +730,7 @@
 		using_hardware_cursor = 0;
 
 	es = container_of(ec->surface_list.next, struct wlsc_surface, link);
-	if (es->map_type == WLSC_SURFACE_MAP_FULLSCREEN &&
-	    es->fullscreen_output == output) {
+	if (es->fullscreen_output == output) {
 		if (es->visual == &ec->compositor.rgb_visual &&
 		    using_hardware_cursor) {
 			if (output->prepare_scanout_surface(output, es) == 0) {
@@ -827,7 +827,7 @@
 	return 1;
 }
 
-void
+WL_EXPORT void
 wlsc_compositor_schedule_repaint(struct wlsc_compositor *compositor)
 {
 	struct wlsc_output *output;
@@ -845,7 +845,7 @@
 	compositor->repaint_on_timeout = 1;
 }
 
-void
+WL_EXPORT void
 wlsc_compositor_fade(struct wlsc_compositor *compositor, float tint)
 {
 	int done;
@@ -856,7 +856,8 @@
 		return;
 
 	if (done)
-		compositor->fade.tweener.timestamp = get_time();
+		compositor->fade.tweener.timestamp =
+			wlsc_compositor_get_time();
 
 	wlsc_compositor_damage_all(compositor);
 	if (wl_list_empty(&compositor->fade.animation.link))
@@ -871,7 +872,7 @@
 	wl_resource_destroy(&surface->resource, client);
 }
 
-void
+WL_EXPORT void
 wlsc_surface_assign_output(struct wlsc_surface *es)
 {
 	struct wlsc_compositor *ec = es->compositor;
@@ -920,6 +921,8 @@
 	if (x != 0 || y != 0)
 		wlsc_surface_assign_output(es);
 	wlsc_surface_update_matrix(es);
+
+	es->compositor->shell->attach(es->compositor->shell, es);
 }
 
 static void
@@ -1180,7 +1183,7 @@
 	motion_grab_end
 };
 
-void
+WL_EXPORT void
 wlsc_compositor_wake(struct wlsc_compositor *compositor)
 {
 	if (compositor->idle_inhibit)
@@ -1294,7 +1297,7 @@
 	wlsc_surface_damage(wd->sprite);
 }
 
-void
+WL_EXPORT void
 wlsc_surface_activate(struct wlsc_surface *surface,
 		      struct wlsc_input_device *device, uint32_t time)
 {
@@ -1367,7 +1370,7 @@
 		wl_display_terminate(compositor->wl_display);
 }
 
-struct wlsc_binding *
+WL_EXPORT struct wlsc_binding *
 wlsc_compositor_add_binding(struct wlsc_compositor *compositor,
 			    uint32_t key, uint32_t button, uint32_t modifier,
 			    wlsc_binding_handler_t handler, void *data)
@@ -1388,7 +1391,7 @@
 	return binding;
 }
 
-void
+WL_EXPORT void
 wlsc_binding_destroy(struct wlsc_binding *binding)
 {
 	wl_list_remove(&binding->link);
@@ -1861,9 +1864,6 @@
 	ec->fade.animation.frame = fade_frame;
 	wl_list_init(&ec->fade.animation.link);
 
-	wlsc_shell_init(ec);
-	wlsc_switcher_init(ec);
-
 	wlsc_compositor_add_binding(ec, KEY_BACKSPACE, 0,
 				    MODIFIER_CTRL | MODIFIER_ALT,
 				    terminate_binding, ec);
@@ -1917,6 +1917,8 @@
 	GError *error = NULL;
 	GOptionContext *context;
 	int width, height;
+	void *shell_module;
+	int (*shell_init)(struct wlsc_compositor *ec);
 
 	g_type_init(); /* GdkPixbuf needs this, it seems. */
 
@@ -1936,6 +1938,21 @@
 
 	ec = NULL;
 
+	shell_init = desktop_shell_init;
+	if (option_shell) {
+		shell_module = dlopen(option_shell, RTLD_LAZY);
+		if (!shell_module) {
+			fprintf(stderr, "failed to load shell module: %m\n");
+			exit(EXIT_FAILURE);
+		}
+		shell_init = dlsym(shell_module, "shell_init");
+		if (!shell_init) {
+			fprintf(stderr,
+				"failed to lookup shell init function: %m\n");
+			exit(EXIT_FAILURE);
+		}
+	}
+
 #if BUILD_WAYLAND_COMPOSITOR
 	if (getenv("WAYLAND_DISPLAY"))
 		ec = wayland_compositor_create(display, width, height);
@@ -1961,6 +1978,9 @@
 		exit(EXIT_FAILURE);
 	}
 
+	if (shell_init(ec) < 0)
+		exit(EXIT_FAILURE);
+
 	if (wl_display_add_socket(display, option_socket_name)) {
 		fprintf(stderr, "failed to add socket: %m\n");
 		exit(EXIT_FAILURE);
diff --git a/compositor/compositor.h b/compositor/compositor.h
index 3c0d073..22095ff 100644
--- a/compositor/compositor.h
+++ b/compositor/compositor.h
@@ -112,6 +112,11 @@
 	uint32_t timestamp;
 };
 
+struct wlsc_shell {
+	void (*lock)(struct wlsc_shell *shell);
+	void (*attach)(struct wlsc_shell *shell, struct wlsc_surface *surface);
+};
+
 enum {
 	WLSC_COMPOSITOR_ACTIVE,
 	WLSC_COMPOSITOR_SLEEPING
@@ -132,8 +137,7 @@
 	struct wlsc_shader solid_shader;
 	struct wl_display *wl_display;
 
-	/* We implement the shell interface. */
-	struct wl_shell shell;
+	struct wlsc_shell *shell;
 
 	/* There can be more than one, but not right now... */
 	struct wl_input_device *input_device;
@@ -296,7 +300,7 @@
 			 struct wl_surface *surface, uint32_t time);
 
 uint32_t
-get_time(void);
+wlsc_compositor_get_time(void);
 
 int
 wlsc_compositor_init(struct wlsc_compositor *ec, struct wl_display *display);
@@ -313,7 +317,7 @@
 		       struct wlsc_compositor *ec);
 
 int
-wlsc_shell_init(struct wlsc_compositor *ec);
+desktop_shell_init(struct wlsc_compositor *ec);
 
 void
 wlsc_switcher_init(struct wlsc_compositor *compositor);
diff --git a/compositor/shell.c b/compositor/shell.c
index c842b97..d3859ae 100644
--- a/compositor/shell.c
+++ b/compositor/shell.c
@@ -28,6 +28,11 @@
 #include "wayland-server.h"
 #include "compositor.h"
 
+struct wl_shell {
+	struct wl_object object;
+	struct wlsc_shell shell;
+};
+
 struct wlsc_move_grab {
 	struct wl_grab grab;
 	struct wlsc_surface *surface;
@@ -110,6 +115,7 @@
 	uint32_t edges;
 	int32_t dx, dy, width, height;
 	struct wlsc_surface *surface;
+	struct wl_shell *shell;
 };
 
 static void
@@ -118,8 +124,6 @@
 {
 	struct wlsc_resize_grab *resize = (struct wlsc_resize_grab *) grab;
 	struct wl_input_device *device = grab->input_device;
-	struct wlsc_compositor *ec =
-		(struct wlsc_compositor *) device->compositor;
 	struct wl_surface *surface = &resize->surface->surface;
 	int32_t width, height;
 
@@ -139,7 +143,7 @@
 		height = resize->height;
 	}
 
-	wl_client_post_event(surface->client, &ec->shell.object,
+	wl_client_post_event(surface->client, &resize->shell->object,
 			     WL_SHELL_CONFIGURE, time, resize->edges,
 			     surface, width, height);
 }
@@ -195,6 +199,7 @@
 	resize->width = es->width;
 	resize->height = es->height;
 	resize->surface = es;
+	resize->shell = shell;
 
 	if (edges == 0 || edges > 15 ||
 	    (edges & 3) == 3 || (edges & 12) == 12)
@@ -243,7 +248,8 @@
 
 	wl_list_remove(&drag->drag_focus_listener.link);
 	if (drag->grab.input_device)
-		wl_input_device_end_grab(drag->grab.input_device, get_time());
+		wl_input_device_end_grab(drag->grab.input_device,
+					 wlsc_compositor_get_time());
 
 	free(drag);
 }
@@ -614,7 +620,8 @@
 
 	if (wd && wd->selection == selection) {
 		wd->selection = NULL;
-		wlsc_selection_set_focus(selection, NULL, get_time());
+		wlsc_selection_set_focus(selection, NULL,
+					 wlsc_compositor_get_time());
 	}
 
 	wl_list_remove(&selection->selection_focus_listener.link);
@@ -667,23 +674,21 @@
 move_binding(struct wl_input_device *device, uint32_t time,
 	     uint32_t key, uint32_t button, uint32_t state, void *data)
 {
-	struct wlsc_compositor *compositor = data;
+	struct wl_shell *shell = data;
 	struct wlsc_surface *surface =
 		(struct wlsc_surface *) device->pointer_focus;
 
 	if (surface == NULL)
 		return;
 
-	shell_move(NULL,
-		   (struct wl_shell *) &compositor->shell,
-		   &surface->surface, device, time);
+	shell_move(NULL, shell, &surface->surface, device, time);
 }
 
 static void
 resize_binding(struct wl_input_device *device, uint32_t time,
 	       uint32_t key, uint32_t button, uint32_t state, void *data)
 {
-	struct wlsc_compositor *compositor = data;
+	struct wl_shell *shell = data;
 	struct wlsc_surface *surface =
 		(struct wlsc_surface *) device->pointer_focus;
 	uint32_t edges = 0;
@@ -709,15 +714,30 @@
 	else
 		edges |= WL_SHELL_RESIZE_BOTTOM;
 
-	shell_resize(NULL,
-		     (struct wl_shell *) &compositor->shell,
-		     &surface->surface, device, time, edges);
+	shell_resize(NULL, shell, &surface->surface, device, time, edges);
+}
+
+static void
+lock(struct wlsc_shell *shell)
+{
+}
+
+static void
+attach(struct wlsc_shell *shell, struct wlsc_surface *surface)
+{
 }
 
 int
-wlsc_shell_init(struct wlsc_compositor *ec)
+desktop_shell_init(struct wlsc_compositor *ec)
 {
-	struct wl_shell *shell = &ec->shell;
+	struct wl_shell *shell;
+
+	shell = malloc(sizeof *shell);
+	if (shell == NULL)
+		return -1;
+
+	shell->shell.lock = lock;
+	shell->shell.attach = attach;
 
 	shell->object.interface = &wl_shell_interface;
 	shell->object.implementation = (void (**)(void)) &shell_interface;
@@ -726,9 +746,11 @@
 		return -1;
 
 	wlsc_compositor_add_binding(ec, 0, BTN_LEFT, MODIFIER_SUPER,
-				    move_binding, ec);
+				    move_binding, shell);
 	wlsc_compositor_add_binding(ec, 0, BTN_MIDDLE, MODIFIER_SUPER,
-				    resize_binding, ec);
+				    resize_binding, shell);
+
+	ec->shell = &shell->shell;
 
 	return 0;
 }
diff --git a/configure.ac b/configure.ac
index 960f134..8873bd0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,8 +15,16 @@
 AC_PROG_CC
 AC_PROG_RANLIB
 
+# Initialize libtool
+LT_PREREQ([2.2])
+LT_INIT
+
 PKG_PROG_PKG_CONFIG()
 
+AC_CHECK_FUNC([dlopen], [],
+              AC_CHECK_LIB([dl], [dlopen], DLOPEN_LIBS="-ldl"))
+AC_SUBST(DLOPEN_LIBS)
+
 PKG_CHECK_MODULES(COMPOSITOR,
 		  [wayland-server wayland-client wayland-egl egl >= 7.10 glesv2 gdk-pixbuf-2.0 libudev >= 136 libdrm >= 2.4.23] pixman-1 x11 x11-xcb)
 PKG_CHECK_MODULES(SIMPLE_CLIENT, [egl >= 7.10 glesv2 wayland-client wayland-egl])