shell: handle surface type reassignment

So far nothing prevented a client for registering a surface as one type
and then as another type. With some special types, this would lead to
corrupted wl_lists.

Add a function, that either resets the surface type or posts an error to
the client. In case of an error, the set type operation must be aborted.

Change the type name SHELL_SURFACE_NORMAL to SHELL_SURFACE_NONE, as
there is nothing normal in the "none" type which just means uninitialised.

Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
diff --git a/compositor/shell.c b/compositor/shell.c
index 395d545..3572330 100644
--- a/compositor/shell.c
+++ b/compositor/shell.c
@@ -59,7 +59,7 @@
 };
 
 enum shell_surface_type {
-	SHELL_SURFACE_NORMAL,
+	SHELL_SURFACE_NONE,
 
 	SHELL_SURFACE_PANEL,
 	SHELL_SURFACE_BACKGROUND,
@@ -288,22 +288,47 @@
 		wl_resource_post_no_memory(resource);
 }
 
+static int
+reset_shell_surface_type(struct shell_surface *surface)
+{
+	switch (surface->type) {
+	case SHELL_SURFACE_FULLSCREEN:
+		surface->surface->x = surface->saved_x;
+		surface->surface->y = surface->saved_y;
+		surface->surface->fullscreen_output = NULL;
+		break;
+	case SHELL_SURFACE_PANEL:
+	case SHELL_SURFACE_BACKGROUND:
+		wl_list_remove(&surface->link);
+		wl_list_init(&surface->link);
+		break;
+	case SHELL_SURFACE_LOCK:
+		wl_resource_post_error(&surface->resource,
+				       WL_DISPLAY_ERROR_INVALID_METHOD,
+				       "cannot reassign lock surface type");
+		return -1;
+	case SHELL_SURFACE_NONE:
+	case SHELL_SURFACE_TOPLEVEL:
+	case SHELL_SURFACE_TRANSIENT:
+		break;
+	}
+
+	surface->type = SHELL_SURFACE_NONE;
+	return 0;
+}
+
 static void
 shell_surface_set_toplevel(struct wl_client *client,
 			   struct wl_resource *resource)
 
 {
-	struct shell_surface *shsurf = resource->data;
-	struct wlsc_surface *es = shsurf->surface;
+	struct shell_surface *surface = resource->data;
 
-	if (shsurf->type == SHELL_SURFACE_FULLSCREEN) {
-		es->x = shsurf->saved_x;
-		es->y = shsurf->saved_y;
-	}
+	if (reset_shell_surface_type(surface))
+		return;
 
-	wlsc_surface_damage(es);
-	shsurf->type = SHELL_SURFACE_TOPLEVEL;
-	es->fullscreen_output = NULL;
+	wlsc_surface_damage(surface->surface);
+	surface->type = SHELL_SURFACE_TOPLEVEL;
 }
 
 static void
@@ -317,6 +342,9 @@
 	struct shell_surface *pshsurf = parent_resource->data;
 	struct wlsc_surface *pes = pshsurf->surface;
 
+	if (reset_shell_surface_type(shsurf))
+		return;
+
 	/* assign to parents output  */
 	es->output = pes->output;
  
@@ -336,6 +364,9 @@
 	struct wlsc_surface *es = shsurf->surface;
 	struct wlsc_output *output;
 
+	if (reset_shell_surface_type(shsurf))
+		return;
+
 	/* FIXME: Fullscreen on first output */
 	/* FIXME: Handle output going away */
 	output = container_of(es->compositor->output_list.next,
@@ -438,7 +469,7 @@
 	/* init link so its safe to always remove it in destroy_shell_surface */
 	wl_list_init(&shsurf->link);
 
-	shsurf->type = SHELL_SURFACE_NORMAL;
+	shsurf->type = SHELL_SURFACE_NONE;
 
 	wl_client_add_resource(client, &shsurf->resource);
 }
@@ -458,6 +489,9 @@
 	struct wlsc_surface *surface = shsurf->surface;
 	struct shell_surface *priv;
 
+	if (reset_shell_surface_type(shsurf))
+		return;
+
 	wl_list_for_each(priv, &shell->backgrounds, link) {
 		if (priv->output == output_resource->data) {
 			priv->surface->output = NULL;
@@ -493,6 +527,9 @@
 	struct wlsc_surface *surface = shsurf->surface;
 	struct shell_surface *priv;
 
+	if (reset_shell_surface_type(shsurf))
+		return;
+
 	wl_list_for_each(priv, &shell->panels, link) {
 		if (priv->output == output_resource->data) {
 			priv->surface->output = NULL;
@@ -534,13 +571,17 @@
 			       struct wl_resource *surface_resource)
 {
 	struct wl_shell *shell = resource->data;
+	struct shell_surface *surface = surface_resource->data;
+
+	if (reset_shell_surface_type(surface))
+		return;
 
 	shell->prepare_event_sent = false;
 
 	if (!shell->locked)
 		return;
 
-	shell->lock_surface = surface_resource->data;
+	shell->lock_surface = surface;
 
 	shell->lock_surface_listener.func = handle_lock_surface_destroy;
 	wl_list_insert(&surface_resource->destroy_listener_list,
@@ -603,7 +644,7 @@
 
 	shsurf = get_shell_surface(surface);
 	if (!shsurf)
-		return SHELL_SURFACE_NORMAL;
+		return SHELL_SURFACE_NONE;
 	return shsurf->type;
 }
 
@@ -945,6 +986,9 @@
 	struct shell_surface *surface = shell_surface_resource->data;
 	struct wlsc_output *output = output_resource->data;
 
+	if (reset_shell_surface_type(surface))
+		return;
+
 	/* TODO */
 }